
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?
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:
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.
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.
Ti serve PowerShell 7 (pwsh). Due modi per eseguirlo:
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
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.
brew install powershell; Windows: winget install Microsoft.PowerShell; Debian/Ubuntu: installa dal repository dei pacchetti Microsoft (packages.microsoft.com).brew install sqlite; Debian/Ubuntu: apt-get install sqlite3; Windows: scarica il bundle "tools" di SQLite da sqlite.org e metti sqlite3.exe nel PATH o accanto allo script. Senza di esso la scansione funziona comunque; first_seen viene impostato alla data di esecuzione.first_seen. Se assente = quelle funzionalità vengono saltate, la scansione completa comunque.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 Program cvelistV5) — oltre all'API NinjaOne in modalità live.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)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. |
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.
outputTutto 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 (richiede sqlite3): runs (aggregati per esecuzione), findings (storico dei rilevamenti aperti per (device, software, cve) con first_seen / last_seen / resolved_date / epss_score) e changes (eventi settimana su settimana tipizzati).cve-dashboard.html — dashboard HTML autonomo (libreria grafica incorporata, nessuna richiesta esterna). Opzionalmente copiato in output.dashboard_share.Il set di colonne è fisso e controllato al gate. In ordine:
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
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.
-InputCsv consente 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.
Alcune scelte strutturali che sembrano non convenzionali sono intenzionali:
ForEach-Object -Parallel e 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.[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.tests/ sono script senza dipendenze con asserzioni basate sul codice di uscita, eseguibili ovunque esista pwsh — incluso il server di scansione — senza installare nulla. La CI li esegue tutti su Windows e Ubuntu.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).
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.
cvelistV5 — record CVE più i dati SSVC / vulnrichment contribuiti da CISA ADP. CVE® è un marchio registrato di The MITRE Corporation. https://github.com/CVEProject/cvelistV5Apache-2.0 — vedi LICENSE.
| 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). |
| 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). |
| 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. |
| 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. |
| 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. |
eol-report-YYYY-MM-DD.csv — scritto quando vengono rilevate famiglie a fine vita.denied-report-YYYY-MM-DD.csv — scritto quando denied_software_csv è configurato e corrisponde.nvd-cache.json, kev-cache.json, epss-cache.json, eol-cache.json, msrc-cache.json, cvelist-cache.json (tutti in gitignore).output.wiki_security_page, solo quando impostato.| # | 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. |