Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Submit
ToolsExploitsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

FeedsContactPrivacy© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
fleet-cve-scanner — An open-source, single-script CVE scanner for RMM-managed fleets. Pure PowerShell 7 — joins your RMM software inventory against NVD, CISA KEV, EPSS and SSVC to answer: is this version vulnerable, and how urgent is it? | Kitploit
Tools/GitHubGitHub/boostedchaos/fleet-cve-scanner
Vulnerability ScannersVulnerability AnalysisConfiguration AuditingThreat IntelligenceIncident ResponseLog Analysis
GitHubboostedchaos/fleet-cve-scanner

fleet-cve-scanner

An open-source, single-script CVE scanner for RMM-managed fleets. Pure PowerShell 7 — joins your RMM software inventory against NVD, CISA KEV, EPSS and SSVC to answer: is this version vulnerable, and how urgent is it?

View Repository
121233 months agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share
# fleet-cve-scanner

An open-source, single-script CVE scanner for RMM-managed fleets. Pure
PowerShell 7, no agents, no appliances, no license fees.

It answers one question for every piece of software on every endpoint you
manage: **is the installed version known to be vulnerable, and how urgent is
it?** — by joining your RMM's software inventory against free, authoritative
security feeds:

- **NIST NVD** — known CVEs + affected version ranges
- **CISA KEV** — actively exploited in the wild (bypasses the score threshold)
- **EPSS** — probability of exploitation in the next 30 days
- **CISA SSVC** — act / attend / track prioritization decisions
- **MSRC** — proof a Microsoft patch already covers the finding
- **endoflife.date** — software that will never be patched again

Outputs: a per-device CSV report, a SQLite history with SLA clocks and
week-over-week change events, a self-contained HTML dashboard, and an optional
markdown exposure report.

## Status

**v1.0.0.** The engine was ported clean-room from a scanner that ran weekly in
production against a Windows fleet (see
[docs/HISTORY.md](https://github.com/boostedchaos/fleet-cve-scanner/blob/main/docs/HISTORY.md)). Before this release the two were run
against the same live fleet and compared row-for-row: across tens of thousands
of findings where both saw an identical *(device, software, version)*, every
computed field — status, CVSS, severity, KEV flag, SSVC decision, fix version —
matched exactly.

That comparison covers the **verdict** logic. It did not exercise the
history/SLA layer, and it is not a substitute for validating the tool in your
own environment. Read [docs/known-limitations.md](https://github.com/boostedchaos/fleet-cve-scanner/blob/main/docs/known-limitations.md)
before trusting it — it is written to be honest rather than flattering.

## Quick start

You need [PowerShell 7](#requirements) (`pwsh`). Two ways to run:

### A. Live NinjaOne scan

```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 — any RMM, SCCM, or Intune (no API access)

Export your inventory to a CSV with columns `hostname, software, version`
(optional `device_id, os`), then:

```bash
cp config.example.json config.json   # only output.report_dir is needed here
pwsh -File fleet-cve-scan.ps1 -InputCsv inventory.csv
```

`-InputCsv` makes **zero** NinjaOne calls — no OAuth, no API. It still queries
NVD and the other public feeds, so you still want an `nvd.api_key` in
`config.json` and a valid `output.report_dir`. See
[docs/rmm-adapters.md](https://github.com/boostedchaos/fleet-cve-scanner/blob/main/docs/rmm-adapters.md) for the input contract and
per-RMM export recipes.

### Installing prerequisites

- **PowerShell 7** — macOS: `brew install powershell`; Windows:
  `winget install Microsoft.PowerShell`; Debian/Ubuntu: install from the
  Microsoft package repo (`packages.microsoft.com`).
- **sqlite3** (optional, powers history/trending) — macOS: preinstalled or
  `brew install sqlite`; Debian/Ubuntu: `apt-get install sqlite3`; Windows:
  download the SQLite "tools" bundle from sqlite.org and put `sqlite3.exe` on
  `PATH` or beside the script. Without it the scan still runs; `first_seen`
  falls back to the run date.

## Requirements

- **PowerShell 7** (7.4+ recommended). The parallel scan requires 7.0+.
- **sqlite3** — optional; enables the history DB, trending, and SLA
  `first_seen` tracking. Absent = those features skip, scan still completes.
- **Network egress** to NVD, CISA (KEV), `epss.empiricalsecurity.com`
  (the EPSS bulk-score host — FIRST moved EPSS data hosting there), MSRC,
  endoflife.date, and GitHub raw (CVE Program `cvelistV5`) — plus the NinjaOne
  API in live mode.
- **Credentials** — live mode needs NinjaOne OAuth2 client id/secret; offline
  mode needs only an inventory CSV. An NVD API key is technically optional but
  **effectively required at fleet scale**: NVD allows 50 requests/30s with a
  key and only 5/30s without one. The scanner detects which you have and
  throttles to match, so a keyless run is correct but roughly 10x slower — fine
  for a handful of products, impractical for a real fleet.
  [Keys are free.](https://nvd.nist.gov/developers/request-an-api-key)

## Configuration reference

Config is a JSON file (default `config.json` beside the script; override with
`-ConfigPath`). Start from [`config.example.json`](https://github.com/boostedchaos/fleet-cve-scanner/blob/main/config.example.json). Every
key the scanner reads, its default when omitted, and what it does:

### `ninjaone` (live mode)

| Key | Default | Purpose |
| --- | --- | --- |
| `ninjaone.client_id` | `""` | NinjaOne API OAuth2 client id. Required for live scans; leave blank for `-InputCsv`. |
| `ninjaone.client_secret` | `""` | NinjaOne API OAuth2 client secret. |
| `ninjaone.base_url` | — (example ships `https://app.ninjarmm.com`) | NinjaOne API base URL; required for live scans, no built-in fallback. Use your region's host (e.g. `eu.ninjarmm.com`, `oc.ninjarmm.com`). |

### `nvd`

| Key | Default | Purpose |
| --- | --- | --- |
| `nvd.api_key` | `""` | NVD API key, sent as the `apiKey` header. Blank runs unauthenticated, which drops the defaults for `nvd_rate_limit` and `nvd_min_spacing_ms` to NVD's anonymous ceiling (4/30s, 6500ms) instead of the authenticated one (48/30s, 700ms). Free, and ~10x faster. |

### Scan & scoring

| Key | Default | Purpose |
| --- | --- | --- |
| `cvss_threshold` | `7.0` | Minimum CVSS base score for a scored CVE to be reported VULNERABLE. KEV-listed CVEs bypass this floor. |
| `ssvc_mission_prevalence` | `high` | SSVC Mission Prevalence stakeholder input (`low` / `medium` / `high`). |
| `ssvc_public_wellbeing` | `high` | SSVC Public Well-being stakeholder input (`low` / `medium` / `high`). |

### Caching & rate limiting

| Key | Default | Purpose |
| --- | --- | --- |
| `nvd_cache_ttl_days` | `7` | Days an NVD result (per software name) stays cached before re-query. |
| `nvd_cache_flush_every` | `500` | Checkpoint the NVD cache to disk every N completed items, so a killed scan keeps its fetches. `0` disables (end-of-scan save still runs). |
| `nvd_min_spacing_ms` | `700` with an API key, `6500` without | Minimum milliseconds between NVD calls. **The binding rate constraint** — tune this first. |
| `nvd_rate_limit` | `48` with an API key, `4` without | Sliding-window cap on NVD calls per 30s (a backstop). Floor of 1. |
| `parallel_throttle` | `20` | `ForEach-Object -Parallel` runspace count. NVD calls are serialized regardless; this only bounds cache-hit concurrency. Floor of 1. |
| `kev_cache_ttl_hours` | `24` | TTL for the cached CISA KEV catalog. |
| `epss_cache_ttl_hours` | `24` | TTL for the cached FIRST.org EPSS score file. |
| `eol_cache_ttl_days` | `7` | TTL for cached endoflife.date lookups. |
| `msrc_cache_ttl_days` | `30` | TTL for cached MSRC CVRF data. |
| `cvelist_cache_ttl_days` | `7` | TTL for cached CVE Program (`cvelistV5`) / CISA-ADP SSVC data. |
| `cvelist_fetch_budget` | `300` | Max CVE-record fetches per run for SSVC/vulnrichment enrichment; the rest defer to the next run. |

`nvd_min_spacing_ms`, `nvd_rate_limit`, and `parallel_throttle` are read by the
scanner but not present in `config.example.json` — add them only if you need to
tune. See [docs/rate-limiting.md](https://github.com/boostedchaos/fleet-cve-scanner/blob/main/docs/rate-limiting.md).

### Enrichment & SLA

| Key | Default | Purpose |
| --- | --- | --- |
| `epss_spike_threshold` | `0.10` | Minimum rise in a finding's EPSS score between scans to emit an `EPSS_SPIKE` change event. |
| `sla_days_critical` | `14` | SLA window (days from `first_seen`) for CRITICAL findings. |
| `sla_days_high` | `30` | SLA window for other scored severities. |
| `denied_software_csv` | `""` | Path to an optional denied-software CSV cross-check. Empty disables the check. |

### `output`

| Key | Default | Purpose |
| --- | --- | --- |
| `output.report_dir` | *(required)* | Directory for all outputs — CSV, history DB, dashboard, and caches. |
| `output.wiki_security_page` | `""` | Optional path to a markdown exposure page. Written only when set. |
| `output.dashboard_share` | `""` | Optional path/share that receives a copy of the dashboard each run. Empty = local-only. |

## Outputs

Everything below lands in `output.report_dir`.

- **`vuln-report-YYYY-MM-DD.csv`** — the per-finding report. 28 columns
  (schema below).
- **`vuln-history.db`** — SQLite history (requires `sqlite3`): `runs` (per-run
  aggregates), `findings` (per `(device, software, cve)` open-finding history
  with `first_seen` / `last_seen` / `resolved_date` / `epss_score`), and
  `changes` (typed week-over-week events).
- **`cve-dashboard.html`** — self-contained HTML dashboard (charting library
  inlined, no external requests). Optionally copied to `output.dashboard_share`.
- **`eol-report-YYYY-MM-DD.csv`** — written when end-of-life families are
  detected.
- **`denied-report-YYYY-MM-DD.csv`** — written when `denied_software_csv` is
  configured and matches.
- **Cache files** — `nvd-cache.json`, `kev-cache.json`, `epss-cache.json`,
  `eol-cache.json`, `msrc-cache.json`, `cvelist-cache.json` (all gitignored).
- **Markdown exposure page** — at `output.wiki_security_page`, only when set.

### CSV schema (28 columns)

The column set is fixed and gate-enforced. In order:

| # | Column | Meaning |
| --- | --- | --- |
| 1 | `device_id` | RMM device id (or a deterministic synthetic id in `-InputCsv` mode). |
| 2 | `hostname` | Device hostname. |
| 3 | `os` | Operating system string (may be blank). |
| 4 | `software` | Product name as inventoried. |
| 5 | `installed_version` | Version string as inventoried. |
| 6 | `cve_id` | CVE identifier. |
| 7 | `cvss_score` | CVSS base score (`0` if none). |
| 8 | `severity` | CVSS severity label. |
| 9 | `cvss_version` | Which CVSS standard produced the score (`4.0`/`3.1`/`3.0`/`2.0`/blank). |
| 10 | `epss_score` | FIRST.org EPSS probability (blank if the CVE is absent from the catalog). |
| 11 | `epss_percentile` | EPSS percentile (blank if absent). |
| 12 | `ssvc_exploitation` | Raw CISA SSVC exploitation option (`none`/`poc`/`active`; blank without ADP data). |
| 13 | `ssvc_decision` | SSVC decision `Act`/`Attend`/`Track` (blank without ADP data). |
| 14 | `status` | `VULNERABLE` / `PATCHED` / `UNKNOWN`. |
| 15 | `published_date` | CVE published date. |
| 16 | `unknown_reason` | Why a row is UNKNOWN (`SUSPECT_VERSION` / `NO_VERSION_BOUND` / `VERSION_PARSE_ERROR`; blank otherwise). |
| 17 | `is_kev` | `True` when the CVE is on the CISA KEV catalog. |
| 18 | `kev_due_date` | CISA KEV remediation due date. |
| 19 | `kev_ransomware_use` | KEV ransomware-campaign use flag. |
| 20 | `fix_version` | Computed fix-boundary version. |
| 21 | `fix_boundary_inclusive` | Whether the fix boundary is inclusive. |
| 22 | `advisory_url` | Vendor advisory or NVD detail URL. |
| 23 | `patch_evidence` | Evidence when a Microsoft UNKNOWN was flipped to PATCHED. |
| 24 | `triage_status` | Triage disposition from `triage.json` (blank if none). |
| 25 | `triage_note` | Triage note. |
| 26 | `first_seen` | Date the finding was first observed (from the history DB). |
| 27 | `sla_due` | SLA due date. |
| 28 | `sla_breach` | Whether the SLA is breached. |

## Scheduling

`Run-WeeklyScan.ps1` is a wrapper for scheduled runs: it runs the scanner from
its own directory, captures **all** output streams to a dated log under
`<script dir>/logs/` (override with `-LogDir`), and propagates the scanner's
exit code so the scheduler sees failures.

**Windows Task Scheduler:**

```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
```

## Dashboard hosting

`cve-dashboard.html` is fully self-contained — open it directly, drop it on any
static host or file share, or copy it automatically each run via
`output.dashboard_share`. For access-controlled hosting, the
[`dashboard-auth/`](https://github.com/boostedchaos/fleet-cve-scanner/blob/main/dashboard-auth) directory ships a minimal ASP.NET host
with Microsoft Entra (Azure AD) authentication; see its
[README](https://github.com/boostedchaos/fleet-cve-scanner/blob/main/dashboard-auth/README.md) and [RUNBOOK](https://github.com/boostedchaos/fleet-cve-scanner/blob/main/dashboard-auth/RUNBOOK.md).

## Honest limitations (read before trusting it)

- **CPE matching is fuzzy by nature.** 40–60% of typical inventory has no NVD
  entry at all and is skipped (logged, never silently). Name-based CPE
  selection can pick the wrong product; a suppression workflow exists because
  it has to. This is the ceiling of the free-NVD approach — a paid scanner
  with a curated detection catalog will be more accurate. This tool's value is
  being free, transparent, and auditable.
- **UNKNOWN is a first-class verdict.** When the scanner can't prove
  vulnerable-or-patched, it says so instead of guessing.
- **Verified against NinjaOne only.** The scan engine is RMM-agnostic (input
  is just device + software + version); the `-InputCsv` mode lets any
  RMM/SCCM/Intune export feed it, but only the NinjaOne adapter has been run
  in production.

The full, unflinching list — the CPE skip rate, dedup-key masking, page-1
`totalResults`, MSRC supersedence gaps, and more — is in
[docs/known-limitations.md](https://github.com/boostedchaos/fleet-cve-scanner/blob/main/docs/known-limitations.md).

## Design notes

A few structural choices that look unconventional are deliberate:

- **Single file, not a module.** The scan hot path runs inside
  `ForEach-Object -Parallel`, and those runspaces cannot see outer-scope
  functions — which is why a handful of helpers are intentionally duplicated
  inside the parallel block. A one-function-per-file module layout would not
  remove that duplication (the parallel body would still need the source
  injected per runspace); it would just add a build step and break the
  copy-one-file deployment model. If the project ever outgrows single-file,
  packaging is the v2-shaped change, not a patch.
- **`[PSCustomObject]` records, not classes.** Finding records are produced
  inside parallel runspaces and marshaled back to the main runspace.
  PowerShell class instances do not reliably survive that boundary;
  `[PSCustomObject]` does, and the CSV column contract is enforced by the
  release gate instead of a type system.
- **Plain PS7 test scripts, not Pester.** The suites in `tests/` are
  dependency-free scripts with exit-code assertions, runnable anywhere
  `pwsh` exists — including the scan server — with nothing to install. CI
  runs them all on Windows and Ubuntu.

## Documentation

- [docs/how-it-works.md](https://github.com/boostedchaos/fleet-cve-scanner/blob/main/docs/how-it-works.md) — the scan pipeline stage by
  stage, verdict logic, cache discipline, and the fail-soft contract.
- [docs/known-limitations.md](https://github.com/boostedchaos/fleet-cve-scanner/blob/main/docs/known-limitations.md) — every known way the
  scanner can be wrong, and why.
- [docs/rate-limiting.md](https://github.com/boostedchaos/fleet-cve-scanner/blob/main/docs/rate-limiting.md) — the NVD rate-limit lessons
  as an operator guide and tuning table.
- [docs/rmm-adapters.md](https://github.com/boostedchaos/fleet-cve-scanner/blob/main/docs/rmm-adapters.md) — the input contract, the two
  built-in inventory paths, and how to add a native adapter.
- [docs/HISTORY.md](https://github.com/boostedchaos/fleet-cve-scanner/blob/main/docs/HISTORY.md) — condensed, sanitized version history and
  the engineering lessons behind each release.

## Contributing

PRs welcome — RMM adapters most of all. See
[CONTRIBUTING.md](https://github.com/boostedchaos/fleet-cve-scanner/blob/main/CONTRIBUTING.md) for the ground rules and the
safe-direction requirement (status logic may only err toward *more*
visibility, never silently toward PATCHED).

## Data sources & attribution

This tool is only as useful as the free, authoritative feeds it joins against.
Each of the following is queried at scan time; please honor their terms if you
build on this.

- **NIST National Vulnerability Database (NVD)** — CVE records and affected
  version ranges, via the NVD API. This product uses the NVD API but is not
  endorsed or certified by the NVD. <https://nvd.nist.gov/>
- **CISA Known Exploited Vulnerabilities (KEV) Catalog** — the authoritative
  list of CVEs known to be exploited in the wild.
  <https://www.cisa.gov/known-exploited-vulnerabilities-catalog>
- **CVE Program — `cvelistV5`** — CVE records plus the SSVC / vulnrichment data
  contributed by the CISA ADP. CVE® is a registered trademark of The MITRE
  Corporation. <https://github.com/CVEProject/cvelistV5>
- **SSVC (Stakeholder-Specific Vulnerability Categorization)** — the act /
  attend / track decision logic in this tool is derived from CERT/CC SSVC
  (Carnegie Mellon University Software Engineering Institute) and the CISA SSVC
  Coordinator decision tree, version 2.0.3. Credit to CERT/CC and CISA is
  required. <https://www.cisa.gov/ssvc> · <https://github.com/CERTCC/SSVC>
- **FIRST.org EPSS (Exploit Prediction Scoring System)** — probability of
  exploitation in the next 30 days. EPSS data is provided by FIRST. See 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)** — the CVRF API, used to confirm
  when a Microsoft security update already covers a finding. © Microsoft
  Corporation. <https://api.msrc.microsoft.com/> · <https://msrc.microsoft.com/>
- **endoflife.date** — end-of-life and end-of-support dates for software that
  will never be patched again. Data courtesy of the endoflife.date project.
  <https://endoflife.date/>

## License

Apache-2.0 — see [LICENSE](https://github.com/boostedchaos/fleet-cve-scanner/blob/main/LICENSE).
Download Tool