
Um scanner de CVE de código aberto e script único para frotas gerenciadas por RMM. Puro PowerShell 7 — combina seu inventário de software RMM com NVD, CISA KEV, EPSS e SSVC para responder: esta versão é vulnerável e qual é a urgência?
# fleet-cve-scanner Um scanner de CVE de código aberto, script único para frotas gerenciadas por RMM. PowerShell 7 puro, sem agentes, sem appliances, sem taxas de licença. Ele responde a uma pergunta para cada software em cada endpoint que você gerencia: **a versão instalada é conhecida por ser vulnerável e quão urgente é?** — combinando o inventário de software do seu RMM com feeds de segurança gratuitos e autoritativos: - **NIST NVD** — CVEs conhecidos + faixas de versões afetadas - **CISA KEV** — explorados ativamente na natureza (ignora o limiar de pontuação) - **EPSS** — probabilidade de exploração nos próximos 30 dias - **CISA SSVC** — decisões de priorização act / attend / track - **MSRC** — prova de que um patch da Microsoft já cobre a descoberta - **endoflife.date** — software que nunca mais será corrigido Saídas: um relatório CSV por dispositivo, um histórico SQLite com relógios SLA e eventos de mudança semana a semana, um painel HTML autônomo e um relatório opcional de exposição em markdown. ## Status **v1.0.0.** O mecanismo foi portado em sala limpa de um scanner que rodava semanalmente em produção contra uma frota Windows (veja [docs/HISTORY.md](https://github.com/boostedchaos/fleet-cve-scanner/blob/main/docs/HISTORY.md)). Antes deste lançamento, os dois foram executados contra a mesma frota ao vivo e comparados linha por linha: em dezenas de milhares de descobertas onde ambos viram um *(dispositivo, software, versão)* idêntico, todos os campos calculados — status, CVSS, gravidade, flag KEV, decisão SSVC, versão de correção — coincidiram exatamente. Essa comparação cobre a lógica de **veredito**. Ela não exerceu a camada de histórico/SLA e não substitui a validação da ferramenta no seu próprio ambiente. Leia [docs/known-limitations.md](https://github.com/boostedchaos/fleet-cve-scanner/blob/main/docs/known-limitations.md) antes de confiar nela — está escrito para ser honesto, não lisonjeiro. ## Quick start Você precisa do [PowerShell 7](#requirements) (`pwsh`). Duas maneiras de executar: ### A. Varredura NinjaOne ao vivo ```bash cp config.example.json config.json # edit config.json: fill ninjaone.client_id / client_secret / base_url, # set output.report_dir, and (recommended) nvd.api_key pwsh -File fleet-cve-scan.ps1 ``` ### B. Offline — qualquer RMM, SCCM ou Intune (sem acesso à API) Exporte seu inventário para um CSV com colunas `hostname, software, version` (opcional `device_id, os`), depois: ```bash cp config.example.json config.json # only output.report_dir is needed here pwsh -File fleet-cve-scan.ps1 -InputCsv inventory.csv ``` `-InputCsv` faz **zero** chamadas NinjaOne — sem OAuth, sem API. Ainda consulta NVD e outros feeds públicos, então você ainda precisa de um `nvd.api_key` no `config.json` e um `output.report_dir` válido. Veja [docs/rmm-adapters.md](https://github.com/boostedchaos/fleet-cve-scanner/blob/main/docs/rmm-adapters.md) para o contrato de entrada e receitas de exportação por RMM. ### Instalando pré-requisitos - **PowerShell 7** — macOS: `brew install powershell`; Windows: `winget install Microsoft.PowerShell`; Debian/Ubuntu: instale a partir do repositório de pacotes da Microsoft (`packages.microsoft.com`). - **sqlite3** (opcional, alimenta histórico/trending) — macOS: pré-instalado ou `brew install sqlite`; Debian/Ubuntu: `apt-get install sqlite3`; Windows: baixe o pacote "tools" do SQLite de sqlite.org e coloque `sqlite3.exe` no `PATH` ou ao lado do script. Sem ele, a varredura ainda é executada; `first_seen` recai para a data da execução. ## Requisitos - **PowerShell 7** (7.4+ recomendado). A varredura paralela requer 7.0+. - **sqlite3** — opcional; permite o banco de dados de histórico, trending e rastreamento de `first_seen` do SLA. Ausente = esses recursos são ignorados, a varredura ainda é concluída. - **Egresso de rede** para NVD, CISA (KEV), `epss.empiricalsecurity.com` (o host de pontuação em massa EPSS — a FIRST moveu a hospedagem de dados EPSS para lá), MSRC, endoflife.date e GitHub raw (CVE Program `cvelistV5`) — mais a API NinjaOne no modo ao vivo. - **Credenciais** — modo ao vivo precisa de client id/secret OAuth2 do NinjaOne; modo offline precisa apenas de um CSV de inventário. Uma chave de API NVD é tecnicamente opcional, mas **efetivamente necessária em escala de frota**: NVD permite 50 requisições/30s com chave e apenas 5/30s sem. O scanner detecta qual você tem e limita para corresponder, então uma execução sem chave está correta, mas cerca de 10x mais lenta — bom para um punhado de produtos, impraticável para uma frota real. [Chaves são gratuitas.](https://nvd.nist.gov/developers/request-an-api-key) ## Referência de configuração A configuração é um arquivo JSON (padrão `config.json` ao lado do script; substitua com `-ConfigPath`). Comece com [`config.example.json`](https://github.com/boostedchaos/fleet-cve-scanner/blob/main/config.example.json). Toda chave que o scanner lê, seu padrão quando omitida e o que faz: ### `ninjaone` (modo ao vivo) | Chave | Padrão | Propósito | | --- | --- | --- | | `ninjaone.client_id` | `""` | ID do cliente OAuth2 da API NinjaOne. Necessário para varreduras ao vivo; deixe em branco para `-InputCsv`. | | `ninjaone.client_secret` | `""` | Segredo do cliente OAuth2 da API NinjaOne. | | `ninjaone.base_url` | — (exemplo fornece `https://app.ninjarmm.com`) | URL base da API NinjaOne; necessária para varreduras ao vivo, sem fallback embutido. Use o host da sua região (ex.: `eu.ninjarmm.com`, `oc.ninjarmm.com`). | ### `nvd` | Chave | Padrão | Propósito | | --- | --- | --- | | `nvd.api_key` | `""` | Chave da API NVD, enviada como cabeçalho `apiKey`. Em branco executa não autenticado, o que reduz os padrões de `nvd_rate_limit` e `nvd_min_spacing_ms` para o teto anônimo da NVD (4/30s, 6500ms) em vez do autenticado (48/30s, 700ms). Gratuito e ~10x mais rápido. | ### Varredura & pontuação | Chave | Padrão | Propósito | | --- | --- | --- | | `cvss_threshold` | `7.0` | Pontuação base mínima de CVSS para que um CVE pontuado seja reportado como VULNERABLE. CVEs listados no KEV ignoram esse piso. | | `ssvc_mission_prevalence` | `high` | Entrada de stakeholder Prevalência de Missão do SSVC (`low` / `medium` / `high`). | | `ssvc_public_wellbeing` | `high` | Entrada de stakeholder Bem-Estar Público do SSVC (`low` / `medium` / `high`). | ### Cache & limite de taxa | Chave | Padrão | Propósito | | --- | --- | --- | | `nvd_cache_ttl_days` | `7` | Dias que um resultado NVD (por nome de software) permanece em cache antes de ser consultado novamente. | | `nvd_cache_flush_every` | `500` | Checkpoint do cache NVD em disco a cada N itens concluídos, para que uma varredura interrompida mantenha suas buscas. `0` desabilita (salvamento ao final da varredura ainda é executado). | | `nvd_min_spacing_ms` | `700` com chave API, `6500` sem | Milissegundos mínimos entre chamadas NVD. **A restrição limitante de taxa** — ajuste esta primeiro. | | `nvd_rate_limit` | `48` com chave API, `4` sem | Limite máximo de chamadas NVD por janela de 30s (um backstop). Mínimo de 1. | | `parallel_throttle` | `20` | Contagem de runspaces do `ForEach-Object -Parallel`. Chamadas NVD são serializadas independentemente; isso apenas limita a concorrência de cache-hit. Mínimo de 1. | | `kev_cache_ttl_hours` | `24` | TTL para o catálogo CISA KEV em cache. | | `epss_cache_ttl_hours` | `24` | TTL para o arquivo de pontuação EPSS da FIRST.org em cache. | | `eol_cache_ttl_days` | `7` | TTL para consultas endoflife.date em cache. | | `msrc_cache_ttl_days` | `30` | TTL para dados MSRC CVRF em cache. | | `cvelist_cache_ttl_days` | `7` | TTL para dados SSVC do CVE Program (`cvelistV5`) / CISA-ADP em cache. | | `cvelist_fetch_budget` | `300` | Máximo de buscas de registros CVE por execução para enriquecimento SSVC/vulnrichment; o restante adia para a próxima execução. | `nvd_min_spacing_ms`, `nvd_rate_limit` e `parallel_throttle` são lidos pelo scanner mas não estão presentes em `config.example.json` — adicione-os apenas se precisar ajustar. Veja [docs/rate-limiting.md](https://github.com/boostedchaos/fleet-cve-scanner/blob/main/docs/rate-limiting.md). ### Enriquecimento & SLA | Chave | Padrão | Propósito | | --- | --- | --- | | `epss_spike_threshold` | `0.10` | Aumento mínimo na pontuação EPSS de uma descoberta entre varreduras para emitir um evento de mudança `EPSS_SPIKE`. | | `sla_days_critical` | `14` | Janela SLA (dias a partir de `first_seen`) para descobertas CRITICAL. | | `sla_days_high` | `30` | Janela SLA para outras gravidades pontuadas. | | `denied_software_csv` | `""` | Caminho para uma verificação opcional de software negado em CSV. Vazio desabilita a verificação. | ### `output` | Chave | Padrão | Propósito | | --- | --- | --- | | `output.report_dir` | *(obrigatório)* | Diretório para todas as saídas — CSV, banco de dados de histórico, painel e caches. | | `output.wiki_security_page` | `""` | Caminho opcional para uma página de exposição em markdown. Escrito apenas quando definido. | | `output.dashboard_share` | `""` | Caminho/compartilhamento opcional que recebe uma cópia do painel a cada execução. Vazio = apenas local. | ## Saídas Tudo abaixo termina em `output.report_dir`. - **`vuln-report-YYYY-MM-DD.csv`** — o relatório por descoberta. 28 colunas (esquema abaixo). - **`vuln-history.db`** — histórico SQLite (requer `sqlite3`): `runs` (agregados por execução), `findings` (histórico de descobertas abertas por `(dispositivo, software, cve)` com `first_seen` / `last_seen` / `resolved_date` / `epss_score`) e `changes` (eventos de mudança semana a semana tipificados). - **`cve-dashboard.html`** — painel HTML autônomo (biblioteca de gráficos incorporada, sem requisições externas). Opcionalmente copiado para `output.dashboard_share`. - **`eol-report-YYYY-MM-DD.csv`** — escrito quando famílias de fim de vida são detectadas. - **`denied-report-YYYY-MM-DD.csv`** — escrito quando `denied_software_csv` está configurado e encontra correspondências. - **Arquivos de cache** — `nvd-cache.json`, `kev-cache.json`, `epss-cache.json`, `eol-cache.json`, `msrc-cache.json`, `cvelist-cache.json` (todos ignorados pelo git). - **Página de exposição em markdown** — em `output.wiki_security_page`, apenas quando definido. ### Esquema do CSV (28 colunas) O conjunto de colunas é fixo e validado na liberação. Em ordem: | # | Coluna | Significado | | --- | --- | --- | | 1 | `device_id` | ID do dispositivo RMM (ou um ID sintético determinístico no modo `-InputCsv`). | | 2 | `hostname` | Nome do host do dispositivo. | | 3 | `os` | String do sistema operacional (pode estar em branco). | | 4 | `software` | Nome do produto conforme inventariado. | | 5 | `installed_version` | String da versão conforme inventariada. | | 6 | `cve_id` | Identificador do CVE. | | 7 | `cvss_score` | Pontuação base CVSS (`0` se nenhuma). | | 8 | `severity` | Rótulo de gravidade CVSS. | | 9 | `cvss_version` | Qual padrão CVSS produziu a pontuação (`4.0`/`3.1`/`3.0`/`2.0`/em branco). | | 10 | `epss_score` | Probabilidade EPSS da FIRST.org (em branco se o CVE estiver ausente do catálogo). | | 11 | `epss_percentile` | Percentil EPSS (em branco se ausente). | | 12 | `ssvc_exploitation` | Opção de exploração SSVC raw da CISA (`none`/`poc`/`active`; em branco sem dados ADP). | | 13 | `ssvc_decision` | Decisão SSVC `Act`/`Attend`/`Track` (em branco sem dados ADP). | | 14 | `status` | `VULNERABLE` / `PATCHED` / `UNKNOWN`. | | 15 | `published_date` | Data de publicação do CVE. | | 16 | `unknown_reason` | Por que uma linha é UNKNOWN (`SUSPECT_VERSION` / `NO_VERSION_BOUND` / `VERSION_PARSE_ERROR`; em branco caso contrário). | | 17 | `is_kev` | `True` quando o CVE está no catálogo CISA KEV. | | 18 | `kev_due_date` | Data de vencimento de remediação CISA KEV. | | 19 | `kev_ransomware_use` | Flag de uso em campanha de ransomware do KEV. | | 20 | `fix_version` | Versão computada do limite de correção. | | 21 | `fix_boundary_inclusive` | Se o limite de correção é inclusivo. | | 22 | `advisory_url` | URL do aviso do fornecedor ou detalhe do NVD. | | 23 | `patch_evidence` | Evidência quando um UNKNOWN da Microsoft foi alterado para PATCHED. | | 24 | `triage_status` | Disposição de triagem de `triage.json` (em branco se nenhum). | | 25 | `triage_note` | Nota de triagem. | | 26 | `first_seen` | Data em que a descoberta foi observada pela primeira vez (do banco de dados de histórico). | | 27 | `sla_due` | Data de vencimento do SLA. | | 28 | `sla_breach` | Se o SLA foi violado. | ## Agendamento `Run-WeeklyScan.ps1` é um wrapper para execuções agendadas: executa o scanner a partir de seu próprio diretório, captura **todos** os fluxos de saída para um log datado em `<script dir>/logs/` (substitua com `-LogDir`) e propaga o código de saída do scanner para que o agendador veja falhas. **Agendador de Tarefas do Windows:** ```bat 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):** ```cron 0 22 * * 0 pwsh -File /path/to/Run-WeeklyScan.ps1 ``` ## Hospedagem do painel `cve-dashboard.html` é totalmente autônomo — abra diretamente, coloque em qualquer host estático ou compartilhamento de arquivos, ou copie automaticamente a cada execução via `output.dashboard_share`. Para hospedagem com controle de acesso, o diretório [`dashboard-auth/`](https://github.com/boostedchaos/fleet-cve-scanner/blob/main/dashboard-auth) fornece um host ASP.NET mínimo com autenticação Microsoft Entra (Azure AD); veja seu [README](https://github.com/boostedchaos/fleet-cve-scanner/blob/main/dashboard-auth/README.md) e [RUNBOOK](https://github.com/boostedchaos/fleet-cve-scanner/blob/main/dashboard-auth/RUNBOOK.md). ## Limitações honestas (leia antes de confiar) - **A correspondência CPE é inerentemente difusa.** 40–60% do inventário típico não tem entrada NVD e é ignorado (registrado, nunca silenciosamente). A seleção de CPE baseada em nome pode escolher o produto errado; um fluxo de supressão existe porque precisa. Este é o teto da abordagem NVD gratuita — um scanner pago com um catálogo de detecção curado será mais preciso. O valor desta ferramenta é ser gratuita, transparente e auditável. - **UNKNOWN é um veredito de primeira classe.** Quando o scanner não consegue provar vulnerável ou corrigido, ele diz isso em vez de adivinhar. - **Verificado apenas contra NinjaOne.** O mecanismo de varredura é independente de RMM (a entrada é apenas dispositivo + software + versão); o modo `-InputCsv` permite que qualquer exportação de RMM/SCCM/Intune o alimente, mas apenas o adaptador NinjaOne foi executado em produção. A lista completa e sem rodeios — taxa de skip de CPE, mascaramento de chave de dedup, `totalResults` da página 1, lacunas de substituição do MSRC e mais — está em [docs/known-limitations.md](https://github.com/boostedchaos/fleet-cve-scanner/blob/main/docs/known-limitations.md). ## Notas de design Algumas escolhas estruturais que parecem não convencionais são deliberadas: - **Arquivo único, não um módulo.** O caminho quente da varredura é executado dentro de `ForEach-Object -Parallel`, e esses runspaces não conseguem ver funções do escopo externo — por isso um punhado de ajudantes é intencionalmente duplicado dentro do bloco paralelo. Um layout de módulo com uma função por arquivo não removeria essa duplicação (o corpo paralelo ainda precisaria da fonte injetada por runspace); apenas adicionaria uma etapa de compilação e quebraria o modelo de implantação de copiar um arquivo. Se o projeto um dia ultrapassar o arquivo único, o empacotamento é a mudança em forma de v2, não um patch. - **Registros `[PSCustomObject]`, não classes.** Os registros de descoberta são produzidos dentro de runspaces paralelos e retornados ao runspace principal. Instâncias de classe do PowerShell não sobrevivem confiavelmente a esse limite; `[PSCustomObject]` sobrevive, e o contrato de colunas CSV é aplicado pela validação de liberação em vez de um sistema de tipos. - **Scripts de teste PS7 simples, não Pester.** As suítes em `tests/` são scripts sem dependências com asserções de código de saída, executáveis em qualquer lugar que `pwsh` exista — incluindo o servidor de varredura — sem nada para instalar. A CI executa todos eles no Windows e Ubuntu. ## Documentação - [docs/how-it-works.md](https://github.com/boostedchaos/fleet-cve-scanner/blob/main/docs/how-it-works.md) — o pipeline de varredura etapa por etapa, lógica de veredito, disciplina de cache e o contrato de falha suave. - [docs/known-limitations.md](https://github.com/boostedchaos/fleet-cve-scanner/blob/main/docs/known-limitations.md) — todas as formas conhecidas de o scanner estar errado e por quê. - [docs/rate-limiting.md](https://github.com/boostedchaos/fleet-cve-scanner/blob/main/docs/rate-limiting.md) — as lições de limite de taxa NVD como guia do operador e tabela de ajuste. - [docs/rmm-adapters.md](https://github.com/boostedchaos/fleet-cve-scanner/blob/main/docs/rmm-adapters.md) — o contrato de entrada, os dois caminhos de inventário embutidos e como adicionar um adaptador nativo. - [docs/HISTORY.md](https://github.com/boostedchaos/fleet-cve-scanner/blob/main/docs/HISTORY.md) — histórico de versões condensado e anonimizado e as lições de engenharia por trás de cada lançamento. ## Contribuindo PRs são bem-vindos — adaptadores RMM acima de tudo. Veja [CONTRIBUTING.md](https://github.com/boostedchaos/fleet-cve-scanner/blob/main/CONTRIBUTING.md) para as regras básicas e o requisito de direção segura (a lógica de status só pode errar em direção a mais visibilidade, nunca silenciosamente em direção a PATCHED). ## Fontes de dados & atribuição Esta ferramenta só é tão útil quanto os feeds gratuitos e autoritativos contra os quais ela se une. Cada um dos seguintes é consultado no momento da varredura; por favor, honre seus termos se você construir sobre isso. - **NIST National Vulnerability Database (NVD)** — registros CVE e faixas de versões afetadas, via API NVD. Este produto usa a API NVD, mas não é endossado ou certificado pelo NVD. <https://nvd.nist.gov/> - **Catálogo de Vulnerabilidades Exploradas Conhecidas (KEV) da CISA** — a lista autoritativa de CVEs conhecidos por serem explorados na natureza. <https://www.cisa.gov/known-exploited-vulnerabilities-catalog> - **CVE Program — `cvelistV5`** — registros CVE mais os dados SSVC/vulnrichment contribuídos pelo ADP da CISA. CVE® é uma marca registrada da MITRE Corporation. <https://github.com/CVEProject/cvelistV5> - **SSVC (Stakeholder-Specific Vulnerability Categorization)** — a lógica de decisão act/attend/track nesta ferramenta é derivada do CERT/CC SSVC (Carnegie Mellon University Software Engineering Institute) e da árvore de decisão do Coordenador SSVC da CISA, versão 2.0.3. Crédito ao CERT/CC e à CISA é necessário. <https://www.cisa.gov/ssvc> · <https://github.com/CERTCC/SSVC> - **FIRST.org EPSS (Exploit Prediction Scoring System)** — probabilidade de exploração nos próximos 30 dias. Dados EPSS são fornecidos pela FIRST. Veja 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)** — a API CVRF, usada para confirmar quando uma atualização de segurança da Microsoft já cobre uma descoberta. © Microsoft Corporation. <https://api.msrc.microsoft.com/> · <https://msrc.microsoft.com/> - **endoflife.date** — datas de fim de vida e fim de suporte para software que nunca mais será corrigido. Dados cortesia do projeto endoflife.date. <https://endoflife.date/> ## Licença Apache-2.0 — veja [LICENSE](https://github.com/boostedchaos/fleet-cve-scanner/blob/main/LICENSE).