SBOM blast radius: answering 'where do we use it?' honestly
Updated
When a vulnerability lands, the first question is which repositories use the affected package below the fixed version. Answering it needs the version each repository actually resolved — which a lockfile records and a manifest does not. A declared range cannot be judged against a version bound at all.
That distinction is the whole difference between a useful answer and a confident wrong one. This guide covers what each substrate can and cannot establish, why a three-way answer beats a two-way one, and what a shared bill of materials does and does not prove.
Manifest versus lockfile: two different claims
A manifest (package.json, a .csproj, requirements.txt) records what a project ASKS FOR — often a range, like a caret or tilde constraint. A lockfile (package-lock.json, pnpm-lock.yaml, packages.lock.json, poetry.lock) records what was actually RESOLVED and installed, pinned to an exact version, including transitive dependencies the manifest never mentions.
For a blast-radius question, that difference is decisive. A manifest saying it wants a caret range covering 2.14 could have resolved to 2.14.1 or to 2.17.2. Answering 'affected' or 'not affected' from that range is a coin flip presented as a finding, and it is wrong in both directions: it will tell you to patch something already patched, and tell you something vulnerable is clean.
| Source | Version fidelity | Transitive coverage | Good for |
|---|---|---|---|
| Manifest (package.json, .csproj) | Declared intent — often a range, sometimes a pin. | Direct dependencies only. | Knowing which projects reference a package at all. |
| Lockfile (package-lock, pnpm-lock, poetry.lock) | Exact resolved version, as installed. | Full dependency graph, including transitive. | Judging a version bound; the only substrate that can. |
| Build-time SBOM (CycloneDX, SPDX) | Exact, as produced by the build that shipped. | Whatever the build included. | Evidence for a customer or auditor about a specific release. |
| Runtime inventory | What is actually loaded and running. | Only what executed. | Reachability — the question none of the above answers. |
Why the answer should have three values, not two
If your substrate cannot resolve every version, the honest output has three states rather than two: affected, not affected, and cannot be determined from this data. The third is not a failure of the tool — it is the actionable list. Those are precisely the repositories a responder needs to go and check by hand, and collapsing them into either of the other two buckets destroys that.
The failure mode of a two-state answer is asymmetric and bad in both directions. Fold the unknowns into 'not affected' and you have manufactured false assurance during an incident. Fold them into 'affected' and you have buried the genuinely affected repositories in noise, which produces the same outcome more slowly.
- Affected — an exact pinned version that satisfies the bound.
- Not affected — an exact pinned version that does not.
- Cannot determine — a range, a missing version, or an unresolvable spec. This is the go-check list.
What an SBOM proves, and what it does not
A software bill of materials in CycloneDX or SPDX format is an inventory of components in a piece of software. It is increasingly requested by enterprise customers and referenced in public-sector procurement, and it is genuinely useful evidence about a specific build. What it is not is a statement about exploitability.
Two limits are worth stating whenever an SBOM is handed over. First, an SBOM describes the build it was produced from — an SBOM generated from manifests rather than from a build is a weaker artifact wearing the same format, and the format itself implies a completeness it may not have. Second, presence is not reachability: a vulnerable component that is never invoked is a different risk from one on your request path, and no bill of materials distinguishes them.
- Say where the SBOM came from — a build, a lockfile, or a manifest. The format looks identical; the claims are not.
- A component list is not an exploitability assessment. Pair it with reachability where the stakes justify it.
- Date it. An SBOM is true of one build at one moment, and consumers will assume it is current unless told otherwise.
Making the question answerable before you need it
The morning a vulnerability lands is the wrong time to discover that your inventory cannot answer the version question. The preparation is unglamorous: commit lockfiles, generate an SBOM as part of the build rather than after it, and keep the inventory somewhere queryable across repositories rather than per-repository.
It is also worth rehearsing the query on a package you are not worried about. The exercise surfaces the gaps — repositories with no lockfile, ecosystems the inventory does not cover, transitive dependencies nothing records — while the answer does not matter yet.
Frequently asked questions
Can we answer a blast-radius question from manifests alone?
You can answer 'which repositories reference this package', which is genuinely useful for scoping. You cannot reliably answer 'which are below version X', because a declared range does not tell you what was installed. A tool that answers the version question from manifests alone is guessing; the honest output marks those repositories as undetermined and sends you to the lockfile.
Does an SBOM tell us whether we are exploitable?
No. An SBOM says a component is present. Exploitability depends on whether the vulnerable code path is reachable in your deployment and whether it is exposed to untrusted input, and no bill of materials records that. SBOMs are inventory evidence; reachability analysis is a separate and harder question.
CycloneDX or SPDX?
Both are widely supported and either satisfies most requests. CycloneDX originated in the OWASP community with a security-analysis emphasis; SPDX originated at the Linux Foundation with strong licence-compliance roots and is an ISO/IEC standard. If a customer or regulator names one, use that; otherwise pick the one your toolchain emits natively rather than converting between them.
How current does the inventory need to be?
Current enough that its answer is actionable during an incident, which in practice means regenerated on every build rather than on a schedule. An inventory refreshed weekly will, on a bad week, tell you about the estate as it was before the change that introduced the problem.
Published by ShipReady Metrics, an evidence-based technology and compliance intelligence platform. This guide is educational and vendor-neutral.