Skip to content

feat: match the package inventory against public CVE feeds - #29

Merged
vsilent merged 1 commit into
mainfrom
feat/cve-watch
Nov 13, 2026
Merged

vsilent merged 1 commit into
mainfrom
feat/cve-watch

Conversation

@vsilent

@vsilent vsilent commented Nov 13, 2026

Copy link
Copy Markdown
  • vuln.cve-watch answers "what does this exact package version have known CVEs" for every package on the host and in running containers — so a CVE published tonight is checked on the next refresh without a Stackdog release.
  • Sources, verified against the live APIs while building: OSV matches Debian/Ubuntu versions server-side (full deb version, epoch included); for Alpine the -rN package revision must be stripped (9.7_p1-r6 -> 9.7_p1) — that is what their data carries. querybatch does one request per refresh, not one per package. CISA's canonical KEV URL rejects datacenter clients (403), so the cisagov/kev-data mirror is tried first; KEV matches are Critical, everything else carries the advisory's severity label.
  • Cadence and state: every package re-queried at least daily (that is how new CVEs surface), answers cached in SQLite for 24h so restarts don't re-query everything, each package+CVE alerted exactly once (owner-scoped). 50 findings per pass, KEV first, the rest arrive later passes.
  • Honest limits baked into the alerts: a match means a fix is missing, not that a box is exploitable — distros backport fixes and config-dependent CVEs don't survive being flattened into version ranges, so every alert says to check the vendor's advisory. Very fresh CVEs may lag in the feeds (CVE-2026-123158 wasn't in OSV's Alpine data days after publication) — the specialized detectors still cover those.
  • Engineering note: the blocking HTTP runs on a blocking thread via a process-wide monitor (reqwest::blocking panics when its runtime is dropped inside an async context — how the sniff loop calls this). KEV is fetched only when something matched.
  • Verified: 477+8+155 tests (7 new), plus an opt-in live test that runs the whole path against the real OSV API (passes in 1.4s). clippy -D warnings and fmt clean. Config: STACKDOG_CVE_WATCH_ENABLED, default on.

vuln.cve-watch closes the gap the two CVE-specific detectors leave: a
CVE published tonight is checked on the next refresh without a Stackdog
release. Every package on the host and in running containers is matched
against OSV, and results are cross-referenced with CISA's KEV catalog.

Feed work, all verified against the live APIs while building:
- OSV matches Debian and Ubuntu versions server-side (the full deb
  version, epoch included); Alpine versions must be sent without the
  -rN package revision, which is what their data carries. querybatch
  does one request per refresh instead of one per package.
- CISA's canonical KEV URL rejects datacenter clients (403), so the
  cisagov/kev-data mirror is tried first. KEV entries are Critical;
  everything else carries the advisory's own severity label.
- Answers are cached in SQLite for 24h (surviving restarts), and a
  match is alerted once per owner, package and CVE.

The blocking HTTP runs on a blocking thread via a process-wide monitor:
reqwest::blocking panics when its runtime is dropped inside an async
context, which is how the sniff loop calls this. KEV is fetched only
when something matched.

Findings are alert-only (vuln.cve-watch joins the never-ban list): a
version names no attacker. Alerts say to check the vendor's advisory —
distributions backport fixes, and config-dependent CVEs do not survive
being flattened into a version range. 50 findings per pass, KEV first.

Tests: 477+8+155 pass (7 new: apk revision stripping, os-release to
ecosystem mapping, CVE-alias preference, KEV severity, batch alignment,
inventory under a root, reported-never-again), plus an opt-in live test
against the real OSV API. clippy -D warnings and fmt clean.
@vsilent
vsilent merged commit 9f887da into main Nov 13, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants