Stop triaging CVEs that never touch your code
Every serious engineering org runs some form of software composition analysis. And almost every one of them has the same quiet failure mode: the scanner reports dozens of βhighβ findings, half of them are in development
Every serious engineering org runs some form of software composition analysis. And almost every one of them has the same quiet failure mode: the scanner reports dozens of βhighβ findings, half of them are in development tooling nobody imports, a few sit in transitive packages two layers down, and the team learns to click βdismissβ until a real issue slips through.
Dan Abramov wrote the definitive autopsy years ago β npm audit is broken by design β and the industry still mostly ships package-presence scanners. Presence is not risk.
What actually determines urgency
When I watch security and platform teams work a backlog, five questions decide the outcome:
- Is this version known-vulnerable? (OSV / advisories)
- Is it being exploited, or likely to be? (CISA KEV, FIRST EPSS)
- Does our code load the package at all? (import / require graph)
- Is it production or development? Direct or transitive?
- Is there a fixed version we can move to without drama?
Commercial SCA platforms answer some of these β especially reachability β behind a paywall. Free tools often stop at (1). That gap is where person-weeks go to die.
Introducing vulntriage
vulntriage is an open-source CLI and library that answers all five, then emits a decision you can put in a PR comment:
| Decision | Meaning |
|---|---|
FIX NOW |
High risk and reachable, or KEV on a production path |
FIX SOON |
Material risk β schedule the upgrade |
MONITOR |
Low urgency; re-check if usage or EPSS moves |
LIKELY NOISE |
Unimported / dev-only / low exploit signal |
Every score lists its factors. No model weights to mystify a review:
FIX_SOON 66 [email protected]
CVE-2021-23337 Β· EPSS 22.41% Β· imported Β· direct/production
Β· CVSS 8.1 (HIGH) β +28
Β· EPSS 22.41% (p97) β +13
Β· Package is imported in project source β +12
Β· Direct dependency β +4
Β· Production dependency β +5
Β· Fix available (4.18.0) β +3
And the counter-example that makes the tool worth running:
LIKELY_NOISE 0 [email protected]
CVE-2017-20165 Β· HIGH CVSS Β· not_imported Β· development
Β· Package is NOT imported in project source β β25
Β· Development-only dependency β β15
Same class of CVSS βHIGH.β Completely different action. That is the whole product.
How it works
- Parse lockfiles:
package-lock.json,pnpm-lock.yaml,yarn.lock,requirements.txt,poetry.lock,Pipfile.lock. - Batch-query OSV for known vulns.
- Enrich CVEs with EPSS and the CISA KEV catalog.
- Walk project sources and build an import index (JS/TS + Python).
- Score, decide, report (table / markdown / JSON / SARIF).
No API keys. No SaaS. Node 18+.
Try it
npx vulntriage .
vulntriage --format sarif -o report.sarif --fail-on fix_now
vulntriage --verbose --no-dev
- GitHub: SybilGambleyyu/vulntriage
- npm: vulntriage
- Site: vulntriage.pages.dev
- Canonical post: sybilgambleyyu.github.io
What comes next
- Deeper function-level reachability when OSV provides vulnerable symbols
- Go and Rust ecosystem reachability
- GitHub Action polish and monorepo configs
- Baseline / suppressions file with expiry dates (not eternal ignores)
If this saves your team one noisy afternoon, open an issue with a lockfile story that still hurts β that is the best way to make the prioritizer better.
Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes β full credit and traffic to the original publisher.