
Un scanner de CVE open-source et à script unique pour les flottes gérées par RMM. PowerShell 7 pur — croise votre inventaire logiciel RMM avec NVD, CISA KEV, EPSS et SSVC pour répondre à la question : cette version est-elle vulnérable, et quelle est son urgence ?
Un scanner CVE open-source, mono-script pour les parcs gérés par RMM. PowerShell 7 pur, sans agents, sans appliances, sans frais de licence.
Il répond à une question pour chaque logiciel sur chaque poste que vous gérez : la version installée est-elle connue comme vulnérable, et quelle est son urgence ? — en croisant l'inventaire logiciel de votre RMM avec des flux de sécurité gratuits et reconnus :
Sorties : un rapport CSV par appareil, un historique SQLite avec des horloges SLA et des événements de changement de semaine en semaine, un tableau de bord HTML autonome, et un rapport d'exposition optionnel au format markdown.
v1.0.0. Le moteur a été porté en chambre blanche à partir d'un scanner qui tournait chaque semaine en production sur un parc Windows (voir docs/HISTORY.md). Avant cette version, les deux ont été exécutés sur le même parc en direct et comparés ligne par ligne : sur des dizaines de milliers de constatations où les deux voyaient un (appareil, logiciel, version) identique, chaque champ calculé — statut, CVSS, sévérité, indicateur KEV, décision SSVC, version de correctif — correspondait exactement.
Cette comparaison couvre la logique de verdict. Elle n'a pas mis en œuvre la couche historique/SLA, et ne remplace pas une validation de l'outil dans votre propre environnement. Lisez docs/known-limitations.md avant de lui faire confiance — il est écrit pour être honnête plutôt que flatteur.
Vous avez besoin de PowerShell 7 (pwsh). Deux façons de l'exécuter :
cp config.example.json config.json
# éditez config.json : remplissez ninjaone.client_id / client_secret / base_url,
# définissez output.report_dir, et (recommandé) nvd.api_key
pwsh -File fleet-cve-scan.ps1
Exportez votre inventaire vers un CSV avec les colonnes hostname, software, version (optionnel device_id, os), puis :
cp config.example.json config.json # seul output.report_dir est nécessaire ici
pwsh -File fleet-cve-scan.ps1 -InputCsv inventory.csv
-InputCsv fait zéro appel NinjaOne — pas d'OAuth, pas d'API. Il interroge toujours NVD et les autres flux publics, donc vous voulez toujours un nvd.api_key dans config.json et un output.report_dir valide. Voir docs/rmm-adapters.md pour le contrat d'entrée et les recettes d'export par RMM.
brew install powershell ; Windows : winget install Microsoft.PowerShell ; Debian/Ubuntu : installer depuis le dépôt de paquets Microsoft (packages.microsoft.com).brew install sqlite ; Debian/Ubuntu : apt-get install sqlite3 ; Windows : télécharger le bundle « tools » SQLite depuis sqlite.org et placer sqlite3.exe sur le PATH ou à côté du script. Sans lui, le scan s'exécute toujours ; first_seen tombe à la date d'exécution.first_seen. Absent = ces fonctionnalités sont ignorées, le scan se termine quand même.epss.empiricalsecurity.com (l'hôte de notation en masse EPSS — FIRST a déplacé l'hébergement des données EPSS là-bas), MSRC, endoflife.date, et GitHub raw (CVE Program cvelistV5) — plus l'API NinjaOne en mode direct.La configuration est un fichier JSON (par défaut config.json à côté du script ; remplacez par -ConfigPath). Commencez par config.example.json. Chaque clé lue par le scanner, sa valeur par défaut lorsqu'omise, et ce qu'elle fait :
ninjaone (mode direct)nvd| Clé | Défaut | Objectif |
|---|---|---|
nvd.api_key | "" | Clé API NVD, envoyée comme en-tête apiKey. Vide exécute une session non authentifiée, ce qui abaisse les valeurs par défaut de nvd_rate_limit et nvd_min_spacing_ms au plafond anonyme de NVD (4/30s, 6500ms) au lieu du plafond authentifié (48/30s, 700ms). Gratuit et ~10 fois plus rapide. |
nvd_min_spacing_ms, nvd_rate_limit, et parallel_throttle sont lus par le scanner mais ne sont pas présents dans config.example.json — ajoutez-les uniquement si vous devez les ajuster. Voir docs/rate-limiting.md.
outputTout ce qui suit atterrit dans output.report_dir.
vuln-report-YYYY-MM-DD.csv — le rapport par constatation. 28 colonnes (schéma ci-dessous).vuln-history.db — historique SQLite (nécessite sqlite3) : runs (agrégats par exécution), findings (historique des constatations ouvertes par (device, software, cve) avec first_seen / last_seen / resolved_date / epss_score), et changes (événements typés de semaine en semaine).cve-dashboard.html — tableau de bord HTML autonome (bibliothèque de graphiques intégrée, aucune requête externe). Optionnellement copié vers output.dashboard_share.L'ensemble des colonnes est fixe et validé par une passerelle. Dans l'ordre :
Run-WeeklyScan.ps1 est un wrapper pour les exécutions planifiées : il exécute le scanner depuis son propre répertoire, capture tous les flux de sortie vers un journal daté sous <répertoire script>/logs/ (remplacez par -LogDir), et propage le code de sortie du scanner afin que le planificateur voie les échecs.
Planificateur de tâches Windows :
schtasks /Create /TN "CVE-Scan-Weekly" ^
/TR "pwsh -NoProfile -File C:\chemin\vers\Run-WeeklyScan.ps1" ^
/SC WEEKLY /D SUN /ST 22:00 /RU SYSTEM
cron (Linux/macOS) :
0 22 * * 0 pwsh -File /chemin/vers/Run-WeeklyScan.ps1
cve-dashboard.html est entièrement autonome — ouvrez-le directement, déposez-le sur n'importe quel hôte statique ou partage de fichiers, ou copiez-le automatiquement à chaque exécution via output.dashboard_share. Pour un hébergement à accès contrôlé, le répertoire dashboard-auth/ fournit un hôte ASP.NET minimal avec authentification Microsoft Entra (Azure AD) ; consultez son README et son RUNBOOK.
-InputCsv permet à n'importe quel export RMM/SCCM/Intune de l'alimenter, mais seul l'adaptateur NinjaOne a été exécuté en production.La liste complète et sans fard — le taux de saut CPE, le masquage des clés de déduplication, totalResults page 1, les lacunes de remplacement MSRC, et plus encore — se trouve dans docs/known-limitations.md.
Quelques choix structurels qui semblent non conventionnels sont délibérés :
ForEach-Object -Parallel, et ces runspaces ne peuvent pas voir les fonctions de la portée externe — c'est pourquoi quelques aides sont intentionnellement dupliquées à l'intérieur du bloc parallèle. Une disposition de module avec une fonction par fichier ne supprimerait pas cette duplication (le corps parallèle aurait toujours besoin de la source injectée par runspace) ; elle ajouterait simplement une étape de construction et casserait le modèle de déploiement à un seul fichier. Si le projet dépasse un jour le fichier unique, l'empaquetage est un changement de forme v2, pas un correctif.[PSCustomObject], pas de classes. Les enregistrements de constatations sont produits à l'intérieur des runspaces parallèles et renvoyés au runspace principal. Les instances de classes PowerShell ne survivent pas de manière fiable à cette frontière ; [PSCustomObject] le fait, et le contrat de colonnes CSV est appliqué par la passerelle de version plutôt que par un système de types.tests/ sont des scripts sans dépendances avec des assertions de code de sortie, exécutables partout où pwsh existe — y compris le serveur de scan — sans rien à installer. CI les exécute tous sur Windows et Ubuntu.Les PRs sont les bienvenues — les adaptateurs RMM avant tout. Voir CONTRIBUTING.md pour les règles de base et l'exigence de direction sûre (la logique de statut ne peut errer que vers plus de visibilité, jamais silencieusement vers PATCHED).
Cet outil n'est aussi utile que les flux gratuits et reconnus qu'il croise. Chacun des suivants est interrogé au moment du scan ; veuillez respecter leurs conditions si vous construisez dessus.
cvelistV5 — enregistrements CVE plus les données SSVC / vulnrichment fournies par l'ADP de CISA. CVE® est une marque déposée de The MITRE Corporation. https://github.com/CVEProject/cvelistV5Apache-2.0 — voir LICENSE.
| Clé | Défaut | Objectif |
|---|
ninjaone.client_id | "" | Identifiant client OAuth2 de l'API NinjaOne. Requis pour les scans en direct ; laissez vide pour -InputCsv. |
ninjaone.client_secret | "" | Secret client OAuth2 de l'API NinjaOne. |
ninjaone.base_url | — (l'exemple fournit https://app.ninjarmm.com) | URL de base de l'API NinjaOne ; requise pour les scans en direct, aucun repli intégré. Utilisez l'hôte de votre région (par ex. eu.ninjarmm.com, oc.ninjarmm.com). |
| Clé | Défaut | Objectif |
|---|
cvss_threshold | 7.0 | Score de base CVSS minimal pour qu'une CVE notée soit signalée comme VULNÉRABLE. Les CVE listées dans KEV contournent ce seuil. |
ssvc_mission_prevalence | high | Entrée partie prenante de la prévalence de mission SSVC (low / medium / high). |
ssvc_public_wellbeing | high | Entrée partie prenante du bien-être public SSVC (low / medium / high). |
| Clé | Défaut | Objectif |
|---|
nvd_cache_ttl_days | 7 | Jours pendant lesquels un résultat NVD (par nom de logiciel) reste en cache avant une nouvelle requête. |
nvd_cache_flush_every | 500 | Point de sauvegarde du cache NVD sur disque tous les N éléments terminés, afin qu'un scan interrompu conserve ses récupérations. 0 désactive (la sauvegarde de fin de scan s'exécute toujours). |
nvd_min_spacing_ms | 700 avec une clé API, 6500 sans | Millisecondes minimales entre les appels NVD. La contrainte de débit contraignante — ajustez cela en premier. |
nvd_rate_limit | 48 avec une clé API, 4 sans | Limite glissante sur les appels NVD par 30s (une protection). Minimum de 1. |
parallel_throttle | 20 | Nombre de runspaces ForEach-Object -Parallel. Les appels NVD sont de toute façon sérialisés ; cela ne limite que la concurrence des accès au cache. Minimum de 1. |
kev_cache_ttl_hours | 24 | TTL pour le catalogue KEV CISA mis en cache. |
epss_cache_ttl_hours | 24 | TTL pour le fichier de scores EPSS de FIRST.org mis en cache. |
eol_cache_ttl_days | 7 | TTL pour les consultations endoflife.date mises en cache. |
msrc_cache_ttl_days | 30 | TTL pour les données CVRF MSRC mises en cache. |
cvelist_cache_ttl_days | 7 | TTL pour les données SSVC CVE Program (cvelistV5) / CISA-ADP mises en cache. |
cvelist_fetch_budget | 300 | Nombre maximal de récupérations d'enregistrements CVE par exécution pour l'enrichissement SSVC/vulnrichment ; le reste est reporté à l'exécution suivante. |
| Clé | Défaut | Objectif |
|---|
epss_spike_threshold | 0.10 | Augmentation minimale du score EPSS d'une constatation entre les scans pour émettre un événement de changement EPSS_SPIKE. |
sla_days_critical | 14 | Fenêtre SLA (jours depuis first_seen) pour les constatations CRITIQUES. |
sla_days_high | 30 | Fenêtre SLA pour les autres sévérités notées. |
denied_software_csv | "" | Chemin vers un CSV optionnel de vérification croisée des logiciels interdits. Vide désactive la vérification. |
| Clé | Défaut | Objectif |
|---|
output.report_dir | (obligatoire) | Répertoire pour toutes les sorties — CSV, base de données d'historique, tableau de bord et caches. |
output.wiki_security_page | "" | Chemin optionnel vers une page d'exposition au format markdown. Écrite uniquement lorsqu'elle est définie. |
output.dashboard_share | "" | Chemin/partage optionnel qui reçoit une copie du tableau de bord à chaque exécution. Vide = local uniquement. |
eol-report-YYYY-MM-DD.csv — écrit lorsque des familles en fin de vie sont détectées.denied-report-YYYY-MM-DD.csv — écrit lorsque denied_software_csv est configuré et correspond.nvd-cache.json, kev-cache.json, epss-cache.json, eol-cache.json, msrc-cache.json, cvelist-cache.json (tous ignorés par git).output.wiki_security_page, uniquement lorsque défini.| # | Colonne | Signification |
|---|
| 1 | device_id | Identifiant d'appareil RMM (ou un identifiant synthétique déterministe en mode -InputCsv). |
| 2 | hostname | Nom d'hôte de l'appareil. |
| 3 | os | Chaîne du système d'exploitation (peut être vide). |
| 4 | software | Nom du produit tel qu'inventorié. |
| 5 | installed_version | Chaîne de version telle qu'inventoriée. |
| 6 | cve_id | Identifiant CVE. |
| 7 | cvss_score | Score de base CVSS (0 si aucun). |
| 8 | severity | Étiquette de sévérité CVSS. |
| 9 | cvss_version | Quelle norme CVSS a produit le score (4.0/3.1/3.0/2.0/vide). |
| 10 | epss_score | Probabilité FIRST.org EPSS (vide si la CVE est absente du catalogue). |
| 11 | epss_percentile | Centile EPSS (vide si absent). |
| 12 | ssvc_exploitation | Option d'exploitation SSVC brute de CISA (none/poc/active ; vide sans données ADP). |
| 13 | ssvc_decision | Décision SSVC Act/Attend/Track (vide sans données ADP). |
| 14 | status | VULNERABLE / PATCHED / UNKNOWN. |
| 15 | published_date | Date de publication de la CVE. |
| 16 | unknown_reason | Pourquoi une ligne est UNKNOWN (SUSPECT_VERSION / NO_VERSION_BOUND / VERSION_PARSE_ERROR ; vide sinon). |
| 17 | is_kev | True lorsque la CVE figure dans le catalogue KEV de CISA. |
| 18 | kev_due_date | Date limite de correction KEV CISA. |
| 19 | kev_ransomware_use | Indicateur d'utilisation de campagne de ransomware KEV. |
| 20 | fix_version | Version de la limite de correctif calculée. |
| 21 | fix_boundary_inclusive | Si la limite de correctif est inclusive. |
| 22 | advisory_url | URL de l'avis du fournisseur ou des détails NVD. |
| 23 | patch_evidence | Preuve lorsqu'un UNKNOWN Microsoft a été basculé en PATCHED. |
| 24 | triage_status | Disposition de triage depuis triage.json (vide si aucun). |
| 25 | triage_note | Note de triage. |
| 26 | first_seen | Date à laquelle la constatation a été observée pour la première fois (depuis la base de données d'historique). |
| 27 | sla_due | Date d'échéance SLA. |
| 28 | sla_breach | Si le SLA est enfreint. |