
fleet-cve-scanner v1.1.0
Uno scanner CVE open-source e a script singolo per flotte gestite con RMM. Puro PowerShell 7 — unisce l'inventario software del tuo RMM con NVD, CISA KEV, EPSS e SSVC per rispondere: questa versione è vulnerabile e quanto è urgente?
fleet-cve-scanner
Uno scanner CVE open-source e a script singolo per flotte gestite tramite RMM. Puro PowerShell 7, senza agenti, senza appliance, senza costi di licenza.
Risponde a una domanda per ogni software su ogni endpoint che gestisci: la versione installata è nota come vulnerabile e quanto è urgente? — unendo l'inventario software del tuo RMM con feed di sicurezza gratuiti e autorevoli:
- NIST NVD — CVE note + intervalli di versione interessati
- CISA KEV — sfruttate attivamente in libertà (supera la soglia del punteggio)
- EPSS — probabilità di sfruttamento nei prossimi 30 giorni
- CISA SSVC — decisioni di priorità agisci / presidia / monitora
- MSRC — prova che una patch Microsoft copre già la scoperta
- endoflife.date — software che non riceverà più patch
Output: un report CSV per dispositivo, uno storico SQLite con orologi SLA ed eventi di modifica settimana su settimana, un dashboard HTML autonomo e un report opzionale in markdown sulle esposizioni.
Stato
v1.0.0. Il motore è stato portato da zero da uno scanner eseguito settimanalmente in produzione su una flotta Windows (vedi docs/HISTORY.md). Prima di questo rilascio, i due sono stati eseguiti sulla stessa flotta live e confrontati riga per riga: su decine di migliaia di rilevamenti in cui entrambi vedevano lo stesso (dispositivo, software, versione), ogni campo calcolato — stato, CVSS, gravità, flag KEV, decisione SSVC, versione di fix — corrispondeva esattamente.
Tale confronto copre la logica di verdetto. Non ha esercitato il livello storico/SLA e non sostituisce la validazione dello strumento nel tuo ambiente. Leggi docs/known-limitations.md prima di fidarti — è scritto per essere onesto, non lusinghiero.
Avvio rapido
Ti serve PowerShell 7 (pwsh). Due modi per eseguirlo:
A. Scansione NinjaOne live
cp config.example.json config.json
# modifica config.json: inserisci ninjaone.client_id / client_secret / base_url,
# imposta output.report_dir e (consigliato) nvd.api_key
pwsh -File fleet-cve-scan.ps1
B. Offline — qualsiasi RMM, SCCM o Intune (nessun accesso API)
Esporta il tuo inventario in un CSV con colonne hostname, software, version (opzionale device_id, os), quindi:
cp config.example.json config.json # qui serve solo output.report_dir
pwsh -File fleet-cve-scan.ps1 -InputCsv inventory.csv
-InputCsv non effettua nessuna chiamata NinjaOne — niente OAuth, niente API. Interroga comunque NVD e gli altri feed pubblici, quindi ti serve comunque un nvd.api_key in config.json e un output.report_dir valido. Vedi docs/rmm-adapters.md per il contratto di input e le ricette di esportazione per RMM specifici.
Installazione dei prerequisiti
- PowerShell 7 — macOS:
brew install powershell; Windows:winget install Microsoft.PowerShell; Debian/Ubuntu: installa dal repository dei pacchetti Microsoft (packages.microsoft.com). - sqlite3 (opzionale, abilita storico/trend) — macOS: preinstallato o
brew install sqlite; Debian/Ubuntu:apt-get install sqlite3; Windows: scarica il bundle "tools" di SQLite da sqlite.org e mettisqlite3.exenelPATHo accanto allo script. Senza di esso la scansione funziona comunque;first_seenviene impostato alla data di esecuzione.
Requisiti
- PowerShell 7 (7.4+ consigliato). La scansione parallela richiede 7.0+.
- sqlite3 — opzionale; abilita il DB storico, i trend e il tracciamento SLA
first_seen. Se assente = quelle funzionalità vengono saltate, la scansione completa comunque. - Traffico di rete in uscita verso NVD, CISA (KEV),
epss.empiricalsecurity.com(l'host del punteggio bulk EPSS — FIRST ha spostato lì l'hosting dei dati EPSS), MSRC, endoflife.date e raw.github.com (CVE ProgramcvelistV5) — oltre all'API NinjaOne in modalità live. - Credenziali — modalità live necessita di client id/secret OAuth2 di NinjaOne; modalità offline necessita solo di un CSV di inventario. Una chiave API NVD è tecnicamente opzionale ma di fatto richiesta a scala di flotta: NVD consente 50 richieste/30s con chiave e solo 5/30s senza. Lo scanner rileva quale hai e si adatta di conseguenza, quindi un'esecuzione senza chiave è corretta ma circa 10x più lenta — va bene per una manciata di prodotti, impraticabile per una flotta reale. Le chiavi sono gratuite.
Riferimento configurazione
La configurazione è un file JSON (predefinito config.json accanto allo script; sovrascrivere con -ConfigPath). Inizia da config.example.json. Ogni chiave che lo scanner legge, il suo valore predefinito quando omessa e cosa fa:
ninjaone (modalità live)
| Chiave | Predefinito | Scopo |
|---|---|---|
ninjaone.client_id | "" | ID client OAuth2 API NinjaOne. Richiesto per scansioni live; lascia vuoto per -InputCsv. |
ninjaone.client_secret | "" | Segreto client OAuth2 API NinjaOne. |
ninjaone.base_url | — (l'esempio fornisce https://app.ninjarmm.com) | URL base API NinjaOne; richiesto per scansioni live, nessun fallback predefinito. Usa l'host della tua regione (es. eu.ninjarmm.com, oc.ninjarmm.com). |
nvd
| Chiave | Predefinito | Scopo |
|---|---|---|
nvd.api_key | "" | Chiave API NVD, inviata come header apiKey. Vuota esegue autenticazione non autenticata, che abbassa i valori predefiniti per nvd_rate_limit e nvd_min_spacing_ms al limite anonimo di NVD (4/30s, 6500ms) invece di quello autenticato (48/30s, 700ms). Gratuito e ~10x più veloce. |
Scansione e punteggio
| Chiave | Predefinito | Scopo |
|---|---|---|
cvss_threshold | 7.0 | Punteggio base CVSS minimo affinché un CVE con punteggio venga segnalato come VULNERABILE. I CVE presenti nell'elenco KEV bypassano questa soglia. |
ssvc_mission_prevalence | high | Input stakeholder SSVC Mission Prevalence (low / medium / high). |
ssvc_public_wellbeing | high | Input stakeholder SSVC Public Well-being (low / medium / high). |
Caching e limitazione della velocità
| Chiave | Predefinito | Scopo |
|---|---|---|
nvd_cache_ttl_days | 7 | Giorni in cui un risultato NVD (per nome software) rimane nella cache prima di essere richiesto nuovamente. |
nvd_cache_flush_every | 500 | Punto di controllo della cache NVD su disco ogni N elementi completati, in modo che una scansione interrotta mantenga i dati recuperati. 0 disabilita (il salvataggio a fine scansione viene comunque eseguito). |
nvd_min_spacing_ms | 700 con chiave API, 6500 senza | Millisecondi minimi tra chiamate NVD. Il vincolo di velocità fondamentale — regola prima questo. |
nvd_rate_limit | 48 con chiave API, 4 senza | Limite massimo di chiamate NVD per 30s (una protezione). Minimo 1. |
parallel_throttle | 20 | Conteggio runspace ForEach-Object -Parallel. Le chiamate NVD sono comunque serializzate; questo limita solo la concorrenza dei cache-hit. Minimo 1. |
kev_cache_ttl_hours | 24 | TTL per il catalogo CISA KEV memorizzato nella cache. |
epss_cache_ttl_hours | 24 | TTL per il file dei punteggi EPSS di FIRST.org memorizzato nella cache. |
eol_cache_ttl_days | 7 | TTL per le ricerche endoflife.date memorizzate nella cache. |
msrc_cache_ttl_days | 30 | TTL per i dati CVRF di MSRC memorizzati nella cache. |
cvelist_cache_ttl_days | 7 | TTL per i dati SSVC di CVE Program (cvelistV5) / CISA-ADP memorizzati nella cache. |
cvelist_fetch_budget | 300 | Numero massimo di recuperi di record CVE per esecuzione per l'arricchimento SSVC/vulnrichment; il resto viene rimandato all'esecuzione successiva. |
nvd_min_spacing_ms, nvd_rate_limit e parallel_throttle vengono letti dallo scanner ma non sono presenti in config.example.json — aggiungili solo se devi regolarli. Vedi docs/rate-limiting.md.
Arricchimento e SLA
| Chiave | Predefinito | Scopo |
|---|---|---|
epss_spike_threshold | 0.10 | Aumento minimo del punteggio EPSS di un rilevamento tra scansioni per generare un evento di modifica EPSS_SPIKE. |
sla_days_critical | 14 | Finestra SLA (giorni da first_seen) per rilevamenti CRITICI. |
sla_days_high | 30 | Finestra SLA per altre gravità con punteggio. |
denied_software_csv | "" | Percorso per un controllo incrociato opzionale su un CSV di software negato. Vuoto disabilita il controllo. |
output
| Chiave | Predefinito | Scopo |
|---|---|---|
output.report_dir | (obbligatorio) | Directory per tutti gli output: CSV, DB storico, dashboard e cache. |
output.wiki_security_page | "" | Percorso opzionale per una pagina di esposizione in markdown. Viene scritta solo quando impostato. |
output.dashboard_share | "" | Percorso/condivisione opzionale che riceve una copia del dashboard a ogni esecuzione. Vuoto = solo locale. |
Output
Tutto quanto segue viene inserito in output.report_dir.
vuln-report-YYYY-MM-DD.csv— il report per rilevamento. 28 colonne (schema sotto).vuln-history.db— storico SQLite (richiedesqlite3):runs(aggregati per esecuzione),findings(storico dei rilevamenti aperti per(device, software, cve)confirst_seen/last_seen/resolved_date/epss_score) echanges(eventi settimana su settimana tipizzati).cve-dashboard.html— dashboard HTML autonomo (libreria grafica incorporata, nessuna richiesta esterna). Opzionalmente copiato inoutput.dashboard_share.eol-report-YYYY-MM-DD.csv— scritto quando vengono rilevate famiglie a fine vita.denied-report-YYYY-MM-DD.csv— scritto quandodenied_software_csvè configurato e corrisponde.- File di cache —
nvd-cache.json,kev-cache.json,epss-cache.json,eol-cache.json,msrc-cache.json,cvelist-cache.json(tutti in gitignore). - Pagina di esposizione in markdown — in
output.wiki_security_page, solo quando impostato.
Schema CSV (28 colonne)
Il set di colonne è fisso e controllato al gate. In ordine:
| # | Colonna | Significato |
|---|---|---|
| 1 | device_id | ID dispositivo RMM (o un ID sintetico deterministico in modalità -InputCsv). |
| 2 | hostname | Nome host del dispositivo. |
| 3 | os | Stringa del sistema operativo (può essere vuota). |
| 4 | software | Nome del prodotto come inventariato. |
| 5 | installed_version | Stringa della versione come inventariata. |
| 6 | cve_id | Identificatore CVE. |
| 7 | cvss_score | Punteggio base CVSS (0 se assente). |
| 8 | severity | Etichetta di gravità CVSS. |
| 9 | cvss_version | Quale standard CVSS ha prodotto il punteggio (4.0/3.1/3.0/2.0/vuoto). |
| 10 | epss_score | Probabilità FIRST.org EPSS (vuoto se il CVE è assente dal catalogo). |
| 11 | epss_percentile | Percentile EPSS (vuoto se assente). |
| 12 | ssvc_exploitation | Opzione di sfruttamento SSVC CISA grezza (none/poc/active; vuoto senza dati ADP). |
| 13 | ssvc_decision | Decisione SSVC Act/Attend/Track (vuoto senza dati ADP). |
| 14 | status | VULNERABLE / PATCHED / UNKNOWN. |
| 15 | published_date | Data di pubblicazione CVE. |
| 16 | unknown_reason | Perché una riga è UNKNOWN (SUSPECT_VERSION / NO_VERSION_BOUND / VERSION_PARSE_ERROR; vuoto altrimenti). |
| 17 | is_kev | True quando il CVE è nel catalogo KEV di CISA. |
| 18 | kev_due_date | Data di scadenza della bonifica KEV di CISA. |
| 19 | kev_ransomware_use | Flag di utilizzo in campagne ransomware KEV. |
| 20 | fix_version | Versione calcolata del confine di fix. |
| 21 | fix_boundary_inclusive | Se il confine di fix è inclusivo. |
| 22 | advisory_url | URL del bollettino del fornitore o dettagli NVD. |
| 23 | patch_evidence | Evidenza quando un UNKNOWN Microsoft è stato trasformato in PATCHED. |
| 24 | triage_status | Disposizione di triage da triage.json (vuoto se nessuno). |
| 25 | triage_note | Nota di triage. |
| 26 | first_seen | Data in cui il rilevamento è stato osservato per la prima volta (dal DB storico). |
| 27 | sla_due | Data di scadenza SLA. |
| 28 | sla_breach | Se lo SLA è stato violato. |
Programmazione
Run-WeeklyScan.ps1 è un wrapper per esecuzioni programmate: esegue lo scanner dalla propria directory, cattura tutti i flussi di output in un log datato sotto <script dir>/logs/ (sovrascrivibile con -LogDir) e propaga il codice di uscita dello scanner in modo che il programmatore veda gli errori.
Utilità di pianificazione di Windows:
schtasks /Create /TN "CVE-Scan-Weekly" ^
/TR "pwsh -NoProfile -File C:\path\to\Run-WeeklyScan.ps1" ^
/SC WEEKLY /D SUN /ST 22:00 /RU SYSTEM
cron (Linux/macOS):
0 22 * * 0 pwsh -File /path/to/Run-WeeklyScan.ps1
Hosting del dashboard
cve-dashboard.html è completamente autonomo — aprilo direttamente, posizionalo su qualsiasi host statico o condivisione file, oppure copialo automaticamente a ogni esecuzione tramite output.dashboard_share. Per hosting con controllo degli accessi, la directory dashboard-auth/ include un host ASP.NET minimale con autenticazione Microsoft Entra (Azure AD); consulta il suo README e RUNBOOK.
Limitazioni oneste (leggere prima di fidarsi)
- Il matching CPE è per natura fuzzy. Il 40–60% di un inventario tipico non ha alcuna voce NVD e viene saltato (registrato, mai silenziosamente). La selezione CPE basata sul nome può scegliere il prodotto sbagliato; esiste un flusso di lavoro di soppressione perché è necessario. Questo è il limite dell'approccio NVD gratuito — uno scanner a pagamento con un catalogo di rilevamento curato sarà più accurato. Il valore di questo strumento è essere gratuito, trasparente e verificabile.
- UNKNOWN è un verdetto di prima classe. Quando lo scanner non può provare vulnerabile-o-patched, lo dice invece di indovinare.
- Verificato solo contro NinjaOne. Il motore di scansione è indipendente dall'RMM (l'input è solo dispositivo + software + versione); la modalità
-InputCsvconsente a qualsiasi esportazione RMM/SCCM/Intune di alimentarlo, ma solo l'adattatore NinjaOne è stato eseguito in produzione.
L'elenco completo e senza sconti — il tasso di skip CPE, il mascheramento delle chiavi di deduplicazione, totalResults di pagina 1, le lacune di supersedenza MSRC e altro — si trova in docs/known-limitations.md.
Note di progettazione
Alcune scelte strutturali che sembrano non convenzionali sono intenzionali:
- File singolo, non un modulo. Il percorso critico della scansione viene eseguito all'interno di
ForEach-Object -Parallele questi runspace non possono vedere funzioni dello scope esterno — motivo per cui una manciata di helper viene intenzionalmente duplicata all'interno del blocco parallelo. Un layout modulare con una funzione per file non rimuoverebbe quella duplicazione (il corpo parallelo avrebbe comunque bisogno del sorgente iniettato per ogni runspace); aggiungerebbe solo un passaggio di build e romperebbe il modello di distribuzione a file singolo. Se il progetto supererà mai il file singolo, il packaging è la modifica a forma di v2, non una patch. - Record
[PSCustomObject], non classi. I record di rilevamento vengono prodotti all'interno di runspace paralleli e restituiti al runspace principale. Le istanze di classe PowerShell non sopravvivono in modo affidabile a quel confine;[PSCustomObject]sì, e il contratto delle colonne CSV viene imposto dal gate di rilascio invece che da un sistema di tipi. - Script di test PS7 puri, non Pester. Le suite in
tests/sono script senza dipendenze con asserzioni basate sul codice di uscita, eseguibili ovunque esistapwsh— incluso il server di scansione — senza installare nulla. La CI li esegue tutti su Windows e Ubuntu.
Documentazione
- docs/how-it-works.md — la pipeline di scansione fase per fase, logica di verdetto, disciplina della cache e contratto di fallimento graduale.
- docs/known-limitations.md — ogni modo noto in cui lo scanner può sbagliare e perché.
- docs/rate-limiting.md — le lezioni sui limiti di velocità NVD come guida per l'operatore e tabella di regolazione.
- docs/rmm-adapters.md — il contratto di input, i due percorsi di inventario predefiniti e come aggiungere un adattatore nativo.
- docs/HISTORY.md — cronologia delle versioni condensata e ripulita e le lezioni ingegneristiche dietro ogni rilascio.
Contribuire
PR benvenuti — gli adattatori RMM più di tutti. Vedi CONTRIBUTING.md per le regole di base e il requisito di direzione sicura (la logica di stato può solo tendere a una maggiore visibilità, mai silenziosamente verso PATCHED).
Fonti dati e attribuzione
Questo strumento è utile solo quanto i feed gratuiti e autorevoli a cui si collega. Ciascuno dei seguenti viene interrogato al momento della scansione; per favore rispetta i loro termini se costruisci su questo.
- NIST National Vulnerability Database (NVD) — record CVE e intervalli di versione interessati, tramite API NVD. Questo prodotto utilizza l'API NVD ma non è approvato o certificato dalla NVD. https://nvd.nist.gov/
- Catalogo delle vulnerabilità note sfruttate (KEV) di CISA — l'elenco autorevole dei CVE noti per essere sfruttati in libertà. https://www.cisa.gov/known-exploited-vulnerabilities-catalog
- CVE Program —
cvelistV5— record CVE più i dati SSVC / vulnrichment contribuiti da CISA ADP. CVE® è un marchio registrato di The MITRE Corporation. https://github.com/CVEProject/cvelistV5 - SSVC (Stakeholder-Specific Vulnerability Categorization) — la logica delle decisioni agisci / presidia / monitora in questo strumento deriva da CERT/CC SSVC (Carnegie Mellon University Software Engineering Institute) e dall'albero decisionale SSVC Coordinator di CISA, versione 2.0.3. È richiesto il riconoscimento a CERT/CC e CISA. https://www.cisa.gov/ssvc · https://github.com/CERTCC/SSVC
- FIRST.org EPSS (Exploit Prediction Scoring System) — probabilità di sfruttamento nei prossimi 30 giorni. I dati EPSS sono forniti da FIRST. Vedi Jay Jacobs, Sasha Romanosky, Benjamin Edwards, Michael Roytman, Idris Adjerid (2021), Exploit Prediction Scoring System, Digital Threats: Research and Practice, 2(3). https://www.first.org/epss/
- Microsoft Security Response Center (MSRC) — l'API CVRF, utilizzata per confermare quando un aggiornamento di sicurezza Microsoft copre già un rilevamento. © Microsoft Corporation. https://api.msrc.microsoft.com/ · https://msrc.microsoft.com/
- endoflife.date — date di fine vita e fine del supporto per software che non riceveranno più patch. Dati per gentile concessione del progetto endoflife.date. https://endoflife.date/
Licenza
Apache-2.0 — vedi LICENSE.