
Scanner de vulnérabilités runtime : détecte les CVE dans les services réellement exécutés sur un hôte et les classe selon leur exposition réseau.
# OffSeq Threat Finder
[](https://github.com/offseq/threat-finder/actions/workflows/ci.yml)
[](https://crates.io/crates/threat-finder)
[](https://docs.rs/threat-finder)
[](#license)
**threat-finder** identifie les logiciels vulnérables _réellement en cours d'exécution_ sur un hôte — et non ce qu'un manifeste prétend — et vous indique quels résultats sont **accessibles depuis le réseau**.
Il convertit chaque service en cours d'exécution (et, avec `--scope all`, chaque paquet OS installé) en un [Package-URL](https://github.com/package-url/purl-spec) exact, puis le compare au catalogue [OffSeq Radar](https://radar.offseq.com) en utilisant les règles de version natives de l'écosystème, afin que les versions backportées/corrigées ne soient pas signalées à tort.

```text
Vulnerability summary (highest risk first):
openssh-server@1:8.9p1-3ubuntu0.6 — 1 finding(s) [PUBLIC tcp 0.0.0.0:22]
[ACT-NOW 92] HIGH CVE-2024-6387 [KEV] regreSSHion: remote code execution in OpenSSH
→ fix: 1:8.9p1-3ubuntu0.10 https://radar.offseq.com/threat/…
[email protected] — 1 finding(s)
[SCHEDULE 41] MED CVE-2023-44487 HTTP/2 Rapid Reset
2 confirmed finding(s) across 2 asset(s); 1 exposed, 1 known-exploited.
```
## Installation
```sh
brew install offseq/tap/threat-finder # Homebrew (macOS/Linux), prebuilt
cargo binstall threat-finder # prebuilt binary, no toolchain
cargo install threat-finder # from source
```
Les archives précompilées pour Linux/macOS (x86_64 + arm64) et Windows (x86_64) sont disponibles sur la [page des versions](https://github.com/offseq/threat-finder/releases/latest). La compilation à partir des sources nécessite Rust ≥ 1.87. Linux, macOS, les BSD et Windows sont tous pris en charge.
## Démarrage rapide
```sh
export OFFSEQ_API_KEY=... # from https://radar.offseq.com/console
threat-finder
```
Analyse les services en cours d'exécution, affiche un résumé classé par risque et écrit le rapport JSON complet dans `/tmp/threats.json`. Ajoutez `--scope all` pour également analyser chaque paquet OS installé.
## Utilisation
```sh
threat-finder [OPTIONS]
```
| Option | Description |
|------|-------------|
| `-o, --output <PATH>` | Écrire le rapport JSON dans `PATH` (défaut : invite, ou `/tmp/threats.json`) |
| `--json` | Afficher le rapport JSON sur stdout au lieu d'un fichier |
| `--scope <SCOPE>` | `running` (défaut) ou `all` (+ chaque paquet OS installé) |
| `--severity <LEVEL>` | Ne rapporter que les résultats au niveau ou au-dessus de `critical\|high\|medium\|low` |
| `--strict` | Ignorer les résultats non confirmés par coordonnées (ne rapporter que les confirmés) |
| `--fail-on <WHAT>` | Sort avec le code `5` si des résultats correspondants existent : `any\|critical\|high\|medium\|low\|kev\|exposed` |
| `--sarif <PATH>` | Écrire également un rapport SARIF 2.1.0 (pour les interfaces d'analyse de code) |
| `--include <GLOB>` / `--exclude <GLOB>` | Filtrer les actifs par glob sur le nom (répétable) |
| `-q, --quiet` | Supprimer la bannière, la progression et le résumé |
| `--no-color` | Désactiver les couleurs ANSI |
| `-y, --yes` | Accepter les valeurs par défaut, sans invite (CI/cron) |
| `--reset` | Re-saisir la clé API, en ignorant celle enregistrée |
| `--register` | Enregistrer l'hôte analysé auprès de Radar pour une surveillance continue (sans invite) |
| `--no-register` | Ne pas enregistrer ni demander de surveillance pour cette exécution |
| `--host-name <NAME>` | Nom d'hôte convivial à envoyer lors d'un enregistrement |
| `--unregister` | Retirer l'inventaire de cet hôte de Radar puis quitter |
| `--windows-missing-updates` | (Windows) Lister également les mises à jour de sécurité en attente de Windows Update Agent (analyse en ligne ; à exécuter en tant qu'administrateur) |
| `-h, --help` / `-V, --version` | Aide / version |
```sh
# CI: only high+ findings, JSON to stdout, no prompts
OFFSEQ_API_KEY=… threat-finder --yes --json --severity high > report.json
# Fail the build only when a network-exposed service has a known-exploited CVE
OFFSEQ_API_KEY=… threat-finder --yes --quiet --fail-on exposed
```
**Codes de sortie :** `0` ok · `1` erreur de recherche/E-S · `2` pas de clé API · `3` OS non pris en charge · `4` limite de débit/quota, ou accès API requis (mise à niveau nécessaire) · `5` seuil `--fail-on` atteint.
**Clé API** (obtenez-en une depuis la [Radar Console](https://radar.offseq.com/console)), résolue dans l'ordre :
1. La variable d'environnement `OFFSEQ_API_KEY` (idéale pour CI/cron).
2. La clé enregistrée dans `$XDG_CONFIG_HOME/offseq-rust/config.toml` (`0600`), sauf si `--reset` est donné.
3. Une invite interactive masquée sur un TTY — puis enregistrée pour la prochaine fois.
En mode non interactif (`--yes` / pas de TTY) sans clé disponible, le code de sortie est `2`.
Définissez `OFFSEQ_CONFIG_DIR` pour remplacer l'emplacement de la configuration (défaut : le répertoire de configuration utilisateur de l'OS) — pratique pour les conteneurs/CI qui ont besoin d'un chemin déterministe.
## Fonctionnement
**Correspondance par coordonnées exactes.** Chaque actif devient un purl portant sa version _complète_ (epoch + révision de la distribution) et un qualificateur `?distro=`, par ex. `pkg:deb/ubuntu/openssh-server@1:8.9p1-3ubuntu0.6?distro=jammy`. L'inventaire est mis en correspondance par lots via des appels `POST /match/batch` (une requête par bloc adapté à la taille du palier) **côté serveur, avec les règles de version natives de l'écosystème** (dpkg/rpm/apk/semver) — de sorte qu'une version backportée et corrigée comme `1.18.0-6+deb11u3` n'est pas signalée à tort, et qu'il n'y a aucune estimation de version côté client. Les résultats sont répartis selon le drapeau `confirmed` de l'API : les correspondances confirmées sont rapportées ; les correspondances de coordonnées dont la version ne peut pas être confirmée sont présentées séparément comme **non confirmées / à trier** (exclues du décompte, de `byCve` et de `--fail-on` ; éliminez-les avec `--strict`).
**Corrélation d'exposition réseau.** Les scanners de manifestes (Trivy, Grype, osv-scanner) lisent les listes de paquets ; les scanners externes (Nessus, OpenVAS) nécessitent un second hôte. Cet outil fait correspondre le processus de chaque service en cours d'exécution aux sockets sur lesquels il est **à l'écoute** (`/proc/net` sous Linux, `lsof` sur les autres Unix, `Get-NetTCPConnection` / `netstat` sous Windows) et classe l'accessibilité — `loopback` / `private` / `public`. Un service vulnérable sur `0.0.0.0` est un risque très différent de celui sur `127.0.0.1` : les résultats sont classés exposés en premier et `--fail-on exposed` conditionne la CI exactement sur cela. Aucun paquet n'est envoyé. Les résultats incluent également le **KEV** de la CISA et l'**EPSS**.
**Priorisation tenant compte de l'exposition.** Chaque résultat reçoit un `riskScore` (0–100) et une catégorie de `decision` de type SSVC — **`act-now` · `soon` · `schedule` · `track`** — fusionnant sévérité, EPSS, KEV et l'**exposition réseau** de l'actif concerné. Le résumé commence chaque ligne par un badge de style `[ACT-NOW 92]` et trie par celui-ci, afin que la poignée de problèmes publics, connus et exploités remonte en haut d'un hôte bruyant. Le score utilise la même formule localement et côté serveur (la vue de surveillance Radar ajoute un petit bonus lorsqu'un résultat KEV a dépassé sa date limite — une date dont la CLI ne dispose pas), et apparaît dans `--json` et SARIF (`properties`).
## Surveillance continue
Une analyse ponctuelle est un instantané. Enregistrez un hôte une fois et Radar continue de surveiller : lorsqu'un **CVE nouvellement publié** affecte l'une de ses coordonnées, vous recevez une alerte (e-mail + Console), priorisée selon l'exposition — sans nouvelle analyse nécessaire.
Après une analyse interactive, l'outil demande :
```text
Add these 42 services to Radar for continuous monitoring & alerts? [Y/n/never]
```
`Y` enregistre cet hôte ; `n` ignore cette exécution ; `never` mémorise votre choix (il est enregistré dans la configuration et la question ne vous sera plus posée). Lors d'une nouvelle analyse, il rapporte également les **écarts** (`+added / -removed / ~changed`) et tout résultat **nouveau depuis votre dernière analyse**. Gérez vos hôtes — activez/désactivez la surveillance, consultez les résultats, désenregistrez — sous la rubrique **Inventory** dans la [Radar Console](https://radar.offseq.com/console).
Pour l'automatisation, ignorez l'invite : `--register` enregistre de manière non interactive (désactivé par défaut en CI), `--no-register` refuse l'enregistrement, `--host-name <NAME>` étiquette l'hôte et `--unregister` le retire. Un identifiant d'hôte stable et votre préférence d'invite sont stockés dans `$XDG_CONFIG_HOME/offseq-rust/config.toml`. L'enregistrement ne modifie jamais le code de sortie — un incident de surveillance ne fera pas échouer votre analyse. Nécessite un plan Basic/Pro/Enterprise (ou la Pro Console).
## Périmètre et couverture
`--scope running` (défaut) analyse les services actifs — le petit ensemble à fort signal dont l'exposition peut être corrélée. `--scope all` énumère en plus **chaque paquet OS installé** (`dpkg`/`rpm`/`pacman`/`apk`/`brew`/`pkg`/`pkg_info`), élargissant la surface de correspondance de 10 à 50×. Un paquet qui sert également de base à un processus actif et exposé conserve cette exposition (les actifs sont dédupliqués et fusionnés par coordonnée). Le noyau est couvert en tant que paquet (`linux-image…`) avec `--scope all`.
> `--scope all` peut produire des centaines à des milliers de paquets. Sur le niveau gratuit (15 recherches/heure), cela déclenchera une limitation de débit ; l'outil avertit lorsque l'inventaire dépasse le budget. (Les recherches sont déjà regroupées par lots ; un cache de résultats local est prévu.)
## Prise en charge par OS
| OS | Découverte | Source des coordonnées |
|----|-----------|-------------------|
| Linux (systemd) | `ListUnits` → `/proc/<pid>/exe` | dpkg / rpm / pacman / apk |
| Linux (SysV/OpenRC) | `service --status-all` / `rc-status` | base de données de paquets |
| macOS | `launchctl list` → `ps` (tiers uniquement) | Homebrew |
| FreeBSD / DragonFly | `service -e` | `pkg` |
| OpenBSD | `rcctl ls started` | `pkg_info` |
| NetBSD | statut `/etc/rc.d` | `pkg_info` |
| Solaris / illumos | `svcs` → `svcprop` | sonde (`--version`) |
| Windows | `Win32_Service` → `Get-NetTCPConnection` / `netstat` | registre / winget / Appx / Chocolatey / Scoop → CPE; npm / pip / dotnet → purl; la version de l'OS → CPE |
Lorsqu'un binaire n'appartient à aucun paquet, la version retombe sur une sonde `--version` renforcée (chemin absolu uniquement, environnement assaini). Sous macOS, les services système Apple (`com.apple.*`, chemins protégés par SIP) sont ignorés — ils sont couverts par la version de l'OS, et sonder des centaines d'entre eux est inutile.
Sous **Windows**, tout est collecté via `powershell.exe` intégré (aucun runtime supplémentaire, aucun administrateur requis pour l'inventaire de base). Les applications installées sont mappées aux CPE NVD via une table de noms soigneusement sélectionnée (les applications non mappées retombent sur une recherche par nom plutôt qu'une estimation erronée) ; l'édition/build de l'OS devient un CPE OS qualifié par fonctionnalité (par ex. `windows_11_23h2`) ; et `--windows-missing-updates` ajoute un avis facultatif de Windows Update Agent concernant les correctifs de sécurité en attente.
## Sortie
JSON, avec des clés déterministes (triées) et sans horodatage, afin que les rapports se comparent proprement :
- **`services`** — `pkg@version` → résultats confirmés (`cveId`, `severity`, `cvssScore`, `epss`, `kev`, `riskScore`, `decision`, `confirmed`, `matchedRange`, `matchBasis`, `fixedVersions`, `remediation`, `cwes`, `references`, `radarUrl`), risque le plus élevé en premier.
- **`unconfirmed`** — correspondances de coordonnées dont la version n'a pas pu être confirmée (à trier).
- **`assets`** — `pkg@version` → `{ exe, versionSource, exposed, reachability, listeners }` (`versionSource` = `package-db` | `probe` ; `reachability` couvre TCP **et** UDP).
- **`byCve`** — chaque CVE regroupé sur tous les actifs concernés ("corriger une fois, réparer beaucoup").
- **`errors`** — échecs de recherche par actif, afin qu'un échec ne soit jamais lu comme "propre".
- **`registration`** — présent lorsque l'exécution a enregistré l'hôte : `host_id`, `monitoring`, `drift`, `summary`, `newSinceLastCount`.
- **`meta`** — `{ tool, version, schemaVersion }` (`schemaVersion` 2).
Un rapport SARIF 2.1.0 (`--sarif`) est également disponible pour les interfaces d'analyse de code.
## L'écosystème OffSeq
| | |
|---|---|
| [**OffSeq**](https://offseq.com) | Audits de sécurité UE, surveillance des menaces, CISO-as-a-Service, conformité NIS2 |
| [**Radar**](https://radar.offseq.com) | Renseignement sur les menaces en temps réel — le catalogue auquel threat-finder se compare |
| [**Radar Console**](https://radar.offseq.com/console) | Abonnements, flux personnalisés et votre `OFFSEQ_API_KEY` |
| [**Radar API**](https://radar.offseq.com/api-docs) | Documentation REST pour le point de terminaison `/match` utilisé ici |
| [**Radar Threats**](https://radar.offseq.com/threats) | Base de données consultable des CVE / malwares / acteurs de menace |
| [**Radar Feeds**](https://radar.offseq.com/feeds) | Flux personnalisés agrégeant CISA, CIRCL, ThreatFox, … |
| [**Radar Pricing**](https://radar.offseq.com/pricing) | Du niveau gratuit à Enterprise |
| [**Breach**](https://breach.offseq.com) | Surveillance des fuites de données et des identifiants exposés sur le dark web |
| [**Veil**](https://veil.offseq.com) | Stéganographie PNG côté client (AES-256-GCM) |
| [**Guard**](https://offseq.com/guard/) | Analyste IA de sécurité et conformité des sites web |
| [**Training**](https://training.offseq.com) | Cours de sécurité et de confidentialité accrédités PECB |
## Développement
```sh
cargo build --release
cargo test # unit tests
cargo test -- --ignored # + macOS live-discovery smoke test
cargo clippy --all-targets
```
Le moteur est une crate bibliothèque (`find_threats`) avec une abstraction `Collector` (running-services et os-packages aujourd'hui ; lockfiles / conteneurs / SBOM ensuite), donc le binaire est une fine couche CLI par-dessus.
## Licence
Sous double licence [MIT](https://github.com/offseq/threat-finder/blob/main/LICENSE) ou [Apache-2.0](https://github.com/offseq/threat-finder/blob/main/LICENSE), à votre choix.