Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

stack-radar

Tell me only about the published vulnerabilities that affect the versions I actually run — and only the ones I haven't already seen.

A raw CVE feed is unusable: hundreds of entries a day, almost none of which concern you. Filtering by product is not enough either — an advisory about Django 6.0 is noise if you run 4.2.

Two things make a feed something you keep reading:

  1. Version matching that is actually correct. Harder than it looks, see below.
  2. The delta. Ninety-nine advisories is a report you close. Three new ones since yesterday is something you act on.

Use

pip install -r requirements.txt          # no dependencies beyond the stdlib

Write down what you run:

# inventory.txt
PyPI:django==4.2.0
PyPI:requests==2.30.0
npm:lodash==4.17.20
python -m stack_radar inventory.txt
3 components checked, 99 advisories, 99 new
NEW CVE-2024-42005       django@4.2.0  fixed in 5.0.8, 4.2.15
      Django SQL injection vulnerability
NEW CVE-2021-23337       lodash@4.17.20  fixed in 4.18.0
      lodash vulnerable to Code Injection via `_.template` imports key names
...

Run it again tomorrow, and only what appeared since is reported:

python -m stack_radar inventory.txt --new-only
3 components checked, 99 advisories, 0 new

Exit codes: 0 nothing to report · 2 findings · 1 error. So cron or CI acts on it directly:

python -m stack_radar inventory.txt --new-only || mail -s "new CVEs" me@example.com
Option
--new-only Report only what appeared since the last scan
--no-remember Do not record this scan — everything stays new
--state Where seen advisories live (default ~/.stack-radar/seen.json)

Ecosystems are whatever OSV supports: PyPI, npm, Go, crates.io, Maven, NuGet, Packagist, RubyGems, Debian, Alpine…


Why version matching is the whole problem

Compare versions as strings and "4.10" sorts before "4.9" — you miss the advisory that hits you. Ignore pre-release markers and 6.0a1 looks newer than 6.0 — you alert on a range you were never in.

Then there are two traps that catch code which handles both of those correctly.

One advisory, several branches

GHSA-2mcm-79hx-8fxw carries three affected blocks, one per maintained Django branch:

introduced 6.0a1  fixed 6.0.2
introduced 5.2a1  fixed 5.2.11
introduced 4.2a1  fixed 4.2.28

Read affected[0] — the obvious thing to write — and Django 4.2.0 reports as safe, because 4.2.0 is not in [6.0a1, 6.0.2). It is very much affected; the answer was in the third block.

One advisory, several packages

GHSA-6c3j-c64m-qhgq shows up in a Django query, because Django ships jQuery in its admin. It carries a jQuery range of [1.1.4, 3.4.0). Compare a Django version against it without checking which package the block describes, and you raise an alert about software the range never mentioned.

This one was not anticipated. It was found by cross-checking (below).

Verified against an oracle, not against intuition

OSV filters by version server-side. Query it twice — once with a version, once without — and you get a labelled dataset for free: every advisory returned for the version is a positive, every other one a negative.

Running our own matcher across four packages, 652 records:

First run 651 agree, 1 disagree
The disagreement the jQuery-inside-Django case above — a real bug
After the fix 652 agree, 0 disagree

That single disagreement is the entire justification for the cross-check. It was not a case anybody would have thought to write a test for, and it produced a false alert with no visible symptom.

Every advisory OSV returns is re-checked locally before being reported. OSV filters correctly — but a feed you act on should not depend on a remote filter you cannot inspect.

pytest -q     # 35 tests

What this does not do

  • It does not discover your inventory. You write it down. Auto-detection guesses, and a security feed built on guesses reports the wrong versions.
  • It does not recompute CVSS. Ordering is derived from the vector string (network reachable, no privileges, no interaction, high impact) as a triage aid. Read the advisory before deciding anything.
  • It does not cover what OSV does not cover. Commercial software and appliances are largely absent. It is strong on open-source dependencies.
  • It is not a scanner. It reads a list you maintain; it does not look at your machines.

Licence

MIT — see LICENSE.

About

Report only the published vulnerabilities that affect the versions you actually run - and only the ones you have not already seen.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages