An advisory lands in the morning. By lunch there is a list of every repository where the affected package appears, and each entry carries the same severity. The list is accurate and useless: it says where the package is, not where it matters.
Presence is not exposure
A software composition scan reports every vulnerable package in a dependency graph. Many of the hits are development-only, pulled in transitively and never loaded, or declared in a manifest and forgotten. A handful are packages that a request handler calls on every request. A scanner that cannot tell those apart reports them all the same way, so the team triages by hand.
Three questions turn the list into a plan:
- Which releases ship it? Exposure is a question about what is running, not what is in a repository. The answer needs a bill of materials per release, so "version 4.2 of the billing service" is an answer and "the billing repository" is not.
- Can first-party code reach it? A package that is imported and called is a different finding from one that is imported and never called, or never referenced at all.
- How likely is exploitation? CVSS is a severity; the Exploit Prediction Scoring System (EPSS) is a probability, and a listing on the CISA Known Exploited Vulnerabilities Catalog is a fact. Together they order the work.
A bill of materials per release
Generate a CycloneDX SBOM in CI for every release and keep it with the release. It is a small file, and it is the only artefact that answers the first question without reconstructing deployment history. Promote the current release to "latest" so the dashboard always answers for what is running now.
Reachability, where the tooling supports it
Package-level reachability annotates each vulnerability with what first-party code does with the package: imports and calls it, imports it without calling it, or never references it. The companion tool that feeds Dependably Packages does this for npm and NuGet; Python projects get the SBOM and the vulnerabilities without a verdict. That limit is worth stating plainly, because a reachability verdict that is quietly absent reads like "not reachable".
Priority buckets, then a rollup
With a score, a KEV flag, an EPSS probability and a reachability verdict per finding, each advisory falls into one of three buckets: act now, attend this sprint, or track until something changes. Rolled up across projects, that gives the number that a count of findings never could: which service to patch first, and which ones can wait for the next scheduled release.
The vulnerability exposure solution page shows the same flow end to end, and the malicious packages page covers the earlier gate: keeping the version out of the build in the first place.