An SBOM Is Not an Alarm: Build a VEX Decision Pipeline
The ticket contains one sentence: “The scanner found a critical library in the production image. Can we close this before the release review?”
The package name matches. The version matches. The CVE is real. None of that answers the question.
In this composite scenario, the first useful clue is not the CVSS score. It is the image digest. The second is the dependency path. The third is whether the vulnerable function survived compilation and can receive attacker-controlled data. A software bill of materials can lead the investigation to those questions, but it cannot answer all of them by itself.
That distinction matters now. On July 29, 2026, CISA, NSA, FBI and international partners published the 2026 Minimum Elements for a Software Bill of Materials. The guidance replaces the 2021 NTIA baseline, defines 17 minimum data fields and applies the baseline to open source, firmware, AI systems, SaaS and cloud-hosted software. It also says something operational teams should keep close: an SBOM is a necessary building block for risk-informed decisions, not a complete verdict.
This article builds the missing decision layer. The objective is not to generate one more JSON artifact and archive it. It is to bind an inventory to the exact release, correlate it with a vulnerability, test the exploitability conditions, publish a time-bound VEX assertion and preserve enough evidence for another team to challenge the result.
The scanner is answering a smaller question
Most software composition analysis starts with a set intersection:
components in artifact ∩ versions named by advisory = candidate findings
That is useful. It is also deliberately conservative. A match can mean any of the following:
- the vulnerable component and code are present, reachable and exposed to hostile input;
- the component is present, but the vulnerable function was removed or never built;
- the code exists, but no execution path reaches it in this product configuration;
- the code is reachable, but the attacker cannot control the required input;
- a runtime or perimeter control reduces exploitability without removing the defect;
- the inventory refers to a source tree while the deployed binary contains something else.
The first case deserves urgent remediation. The others require evidence, not a reflexive “false positive” label. A stale exception attached to a package name is especially dangerous because the next build may change features, dependencies or exposure while retaining the same human-readable application version.
This is the same control-plane lesson behind the recent analysis of CVE-2026-63077 in TeamCity. A dependency inventory can tell you what entered the software factory. It does not prove that the factory produced the intended artifact, nor that a vulnerable path is or is not exploitable after the build.
What the 2026 baseline changes in practice
The CISA guidance separates SBOM metadata from component data. Its ten new elements are SBOM author signature, data format name and version, generation context, tool name and version, SBOM version, component hash value, hash algorithm and component license. It also strengthens component identifiers and clarifies that unknown information must be identified explicitly.
Four changes alter how a security team should design its pipeline.
Generation context is security context
An SBOM generated from a repository, a package lockfile, a build directory and a final container filesystem can produce four different inventories. Each may be accurate for its observation point. Only one describes what is actually deployed.
Record the lifecycle phase and acquisition method. If the release policy accepts a build-time SBOM, say so. If binary analysis augments a supplier SBOM, preserve both rather than silently replacing the supplier’s assertion. CISA explicitly places process-based verification of SBOM accuracy and completeness outside the minimum-elements scope. A signature proves who signed data and whether it changed after signing. It does not prove that the data was complete.
Identifiers have to survive system boundaries
A component name such as core, requests or json is not enough for machine correlation. Use a common software identifier, typically a Package URL for ecosystem packages, and retain version, producer and hashes where available.
pkg:maven/org.example/[email protected]
pkg:npm/%40example/[email protected]
pkg:pypi/[email protected]
The identifier used in the SBOM must be the identifier used in the VEX statement. If a VEX issuer describes a product by a broad family name while your policy engine evaluates a release digest, the match is ambiguous and automation should fail closed.
Hashes bind analysis to bytes
The new component hash value and algorithm fields make a simple control possible: reject an exploitability decision if its evidence was collected against bytes other than the deployment candidate. A package version is metadata. A digest is an artifact identity.
This distinction also limits blast radius after a build-system incident. If a trusted builder is compromised, the source version and package list may look normal while the output bytes have changed. The threat model should therefore connect SBOM, build provenance and artifact verification. The capability-oriented method in Advanced Threat Modeling is useful here because it follows identities and transitive authority instead of stopping at component boxes.
Version the SBOM as evidence evolves
A release can remain byte-for-byte identical while its security context changes because a new vulnerability is published or a previous assertion is corrected. Version the SBOM document independently from the product. Timestamp VEX statements and retain superseded versions. The decision record is a timeline, not a checkbox.
SBOM, provenance and VEX answer different questions
These artifacts are related, but they are not interchangeable.
| Artifact | Primary question | What it does not prove |
|---|---|---|
| SBOM | What components and dependency relationships are represented? | That the inventory is complete, authentic or exploitable |
| SLSA provenance | Where, when and how was this artifact produced? | That the source or dependencies are benign |
| VEX | What is the product’s status for a specific vulnerability? | That the issuer’s analysis is correct or still current |
The SLSA 1.2 specification describes increasing supply-chain guarantees and recommends provenance formats that trace artifacts to their build process. OpenVEX represents an exploitability assertion as the intersection of product, vulnerability and status. One tells you how the artifact came into existence. The other tells you what an issuer currently claims about a specific vulnerability in that artifact.
Neither should be treated as an oracle. Trust policy belongs outside both documents.

A lab pipeline that produces reviewable evidence
The following workflow is intentionally local and uses placeholder identifiers. It demonstrates the control logic without probing a third-party system or claiming that a sample component is actually vulnerable.
1. Generate the inventory from the release candidate
Generate an SBOM from the final artifact whenever the toolchain supports it. For a container image in an authorized lab, a typical flow is:
IMAGE='registry.lab.example/payments@sha256:<release-digest>'
syft "$IMAGE" \
-o cyclonedx-json=payments.cdx.json
jq -e '
.bomFormat == "CycloneDX" and
(.specVersion | length > 0) and
(.metadata.timestamp | length > 0) and
(.components | type == "array")
' payments.cdx.json
Pin the scanner version in CI and record it in the SBOM metadata. Store the release digest, generated SBOM and build provenance under the same immutable release record. If the scanner cannot observe dynamically downloaded plugins or runtime-mounted code, state that coverage gap explicitly.
2. Correlate without discarding ambiguity
A correlation job should produce candidates, not verdicts. Preserve the advisory source, affected version range, match rule and dependency path. Do not collapse “unknown package identity” into “not affected.”
jq -r '
.components[]
| select(.purl != null)
| [.purl, (.hashes // [] | map(.alg + ":" + .content) | join(","))]
| @tsv
' payments.cdx.json > component-evidence.tsv
A high-severity match enters triage with status under_investigation. That status is not indecision. It is an honest machine-readable statement that prevents a dashboard from silently treating missing analysis as either safe or exploitable.
3. Test the exploitability predicates
For each candidate, separate five questions:
- Presence: Is the component in the deployed artifact, not merely in source or a build cache?
- Code: Is the vulnerable code present after compilation, linking, tree shaking or vendor patching?
- Reachability: Can an application execution path invoke it?
- Controllability: Can a relevant attacker influence the values required by the vulnerable path?
- Mitigation: Does a control prevent exploitation, and can that control be bypassed or disabled?
Use the strongest available evidence: binary symbols, call graphs, tests with code coverage, configuration inspection, request traces and runtime telemetry. A static reachability result alone may miss reflection, dynamic loading, native calls, feature flags or framework dispatch. A dynamic test proves only the paths exercised.
The human detail is important. The analyst should record why a hypothesis changed. “Function not observed” is weaker than “the symbol is absent from the release binary.” “Endpoint disabled in staging” is weaker than “the route is absent from the production configuration and deployment admission rejects enabling it.”
4. Issue a narrow VEX statement
OpenVEX 0.2.0 defines four status labels: not_affected, affected, fixed and under_investigation. A not_affected statement must include a machine-readable justification or an impact statement. Prefer the fixed justification label, then add concise human evidence.
{
"@context": "https://openvex.dev/ns/v0.2.0",
"@id": "https://security.lab.example/vex/payments/42",
"author": "Example Product Security",
"timestamp": "2026-08-05T13:00:00Z",
"version": 1,
"statements": [{
"vulnerability": {"name": "CVE-20XX-12345"},
"products": [{
"@id": "pkg:oci/payments@sha256%3A<release-digest>"
}],
"status": "not_affected",
"justification": "vulnerable_code_not_in_execute_path",
"impact_statement": "The vulnerable function is present, but the release call graph and route tests show no path from product inputs. Reassess when the artifact digest or routing configuration changes."
}]
}
The statement is deliberately specific to one artifact. Do not claim that all future versions are unaffected. CISA’s VEX use-case guidance warns that assertions over unbounded future versions can become misleading when implementation changes.
5. Apply issuer and freshness policy
A VEX-aware scanner may suppress findings. Your deployment gate should be more conservative. Evaluate at least:
accept_not_affected = (
signature_valid
and issuer in trusted_issuers
and product_id == release_id
and statement_timestamp >= sbom_timestamp
and justification in allowed_justifications
and evidence_reference_is_retrievable
)
For internet-facing services, active exploitation or vulnerabilities in security boundaries, require human approval even when the expression evaluates true. Expire assertions when the artifact, configuration, advisory or threat intelligence changes. “Not affected” should reduce queue noise only after it increases evidence quality.
How an attacker benefits from weak SBOM operations
The attack chain is less dramatic than a zero-day and often more reliable.
- A supplier publishes an SBOM keyed only by names and versions.
- The consumer imports it without verifying the author signature or binding it to delivered bytes.
- A scanner creates a large queue of low-context findings.
- Teams add broad suppressions to restore delivery velocity.
- A later build enables a vulnerable feature or replaces a component while inheriting the old exception.
- The exploitable condition arrives in production without a fresh decision.
A compromised automation path can make this worse. The GitLost analysis showed why a legitimate capability can become a confused deputy when untrusted input controls sensitive reads and public writes. Apply the same reasoning to SBOM enrichment agents. An advisory description, repository issue or package metadata field is untrusted content. It must not instruct an agent to retrieve secrets, alter release policy or publish internal component data merely because the agent can access those systems.
Detection and defensive controls
Monitor the decision pipeline as a security system, not a reporting feature.
- Artifact mismatch: alert when the SBOM subject, release manifest and deployed digest diverge.
- Unsigned replacement: detect when a previously signed SBOM or VEX document is replaced by an unsigned version.
- Coverage regression: compare component counts, ecosystems and dependency depth between adjacent builds. A sudden drop can indicate scanner failure.
- Exception inheritance: block reuse of a
not_affectedassertion across a new artifact digest unless policy explicitly permits and records the equivalence proof. - Issuer drift: alert on a new signing identity, unexpected tool version or generation context.
- Status churn: investigate repeated transitions between
affectedandnot_affected, especially without evidence updates. - Stale investigation: set an SLA for
under_investigationbased on exposure and threat intelligence.
Red Teams can test these controls without weaponizing a real vulnerability. Introduce a synthetic component with a lab advisory, generate a valid SBOM for build A, change the feature set in build B and attempt to reuse build A’s VEX statement. The defensive objective is to reject the stale assertion because the artifact identity changed. A second test can tamper with the SBOM after signing and verify that ingestion fails before correlation.
Strategic takeaways
The 2026 minimum elements move SBOM practice in the right direction: stronger identity, explicit generation context, signatures, hashes, tool metadata, licenses and versioned documents. Those fields make better automation possible. They do not make automated conclusions automatically trustworthy.
For producers, generate from the closest observable point to the shipped artifact, sign the document, state coverage limits and publish VEX as a versioned security advisory. For consumers, verify the issuer and artifact binding before importing any assertion. For security teams, preserve under_investigation as a real state and demand concrete evidence before not_affected.
The ticket can then close for the right reason. Not because a scanner was noisy. Not because a deadline was close. Because the component, bytes, code path, attacker influence and decision record all point to the same conclusion, and the next build will have to prove it again.
Primary references
- CISA et al., 2026 Minimum Elements for a Software Bill of Materials, July 29, 2026.
- NSA release on the updated SBOM minimum elements, July 29, 2026.
- OpenVEX Specification v0.2.0.
- CycloneDX vulnerability exploitability use case.
- SLSA Specification v1.2.
💜 Enjoyed this content? Support the blog with USDT (TRC20):
TX7obcjHQbDUXb4mGqoASEu1QFTKT2CFGG
