Picture this: a scanner tears through your SBOM and flags log4j-core 2.14.1, and up pops CVE-2021-44228 — Log4Shell, CVSS 10.0. Your security team's pager goes off. But the component is bundled into a batch-processing service that never touches untrusted input, runs with log4j2.formatMsgNoLookups=true, and can't reach an LDAP server even if it wanted to. The vulnerability exists in your inventory. It does not exist in your risk.
This gap — between "the component is present" and "the component is exploitable" — is exactly what a Software Bill of Materials cannot tell you on its own. An SBOM is an ingredient list. It is silent on whether the recipe actually uses the ingredient in a way that matters. That silence is what VEX, the Vulnerability Exploitability eXchange, was built to fill. Without it, teams drown in CVEs that SBOMs surface but never resolve.
What Is a VEX Document?
A VEX document is a machine-readable statement, issued by a software supplier, declaring whether a specific known vulnerability actually affects a specific product. It answers a question SBOMs structurally cannot: not "what's in here" but "does this particular CVE matter, here, in this build." The concept was formalized by CISA and the NTIA's software component transparency working group starting in 2021, as a companion artifact to the SBOM minimum-elements guidance published that July under Executive Order 14028.
A VEX statement is narrow by design. It ties one product identifier to one vulnerability identifier (a CVE or GHSA ID) and one status. It doesn't replace a full vulnerability report — it's a triage signal, meant to be machine-parsed at scale across thousands of components, not read one at a time by an analyst.
Why Doesn't an SBOM Already Tell You This?
Because an SBOM's job is inventory, not risk assessment, and conflating the two is what causes alert fatigue. A modern cloud-native application easily pulls in 500-1,000+ transitive dependencies. Run that SBOM against the NVD and you'll routinely get back hundreds of CVE matches — many for code paths that are never invoked, features that are compiled out, or versions patched via backport that the version string doesn't reflect.
Log4Shell put a number on this problem. In the weeks after its December 10, 2021 disclosure, Google's security team estimated over 35,000 Java packages — roughly 8% of Maven Central — included a vulnerable log4j-core version, either directly or transitively. But industry telemetry from that period also showed a large share of those inclusions were non-exploitable: the JNDI lookup feature was unused, network egress was blocked, or the app never deserialized attacker-controlled strings into log messages. Security teams that could only see the SBOM had no way to distinguish those cases from a genuinely exploitable one without manual analysis of every affected artifact. VEX exists to let the supplier do that analysis once and publish the answer.
What Are the Four VEX Status Values?
Every VEX statement resolves to one of four standardized states: not_affected, affected, fixed, or under_investigation. This vocabulary comes directly from CISA's VEX use cases documentation and is shared across the two dominant VEX implementations.
not_affected— the vulnerability exists in the component but not in a way that impacts the product. This status requires a justification, such asvulnerable_code_not_present,vulnerable_code_not_in_execute_path, orinline_mitigations_already_exist.affected— the vulnerability is real and exploitable in this product; action is needed.fixed— the vulnerability was addressed in a specific version or patch.under_investigation— the supplier has acknowledged the CVE but hasn't finished determining impact.
That justification field is the actual payload. "Not affected: vulnerable_code_not_in_execute_path" is a statement a scanner can act on automatically — suppress the alert, but keep it logged for audit. Without a justification code, "not affected" is just an assertion, and auditors (rightly) won't accept it.
Which VEX Formats Actually Exist Today?
Two formats dominate: CycloneDX VEX and OASIS CSAF, and most tooling now supports both. CycloneDX added native VEX support in version 1.4 (2022), letting a VEX statement travel either embedded inside an SBOM or as a standalone linked document referencing components by PURL. It's the format most commonly paired with CycloneDX SBOMs already produced by build pipelines, since the identifiers and tooling overlap directly.
CSAF (Common Security Advisory Framework) 2.0 was published as an OASIS standard in November 2022 and is the format CISA and major vendors — including Red Hat, which has issued CSAF VEX advisories since 2020 predating the formal standard — use for large-scale advisory distribution. CSAF documents are heavier and more formally structured, built for suppliers publishing thousands of advisories a year rather than a single project attaching VEX to its own SBOM. Red Hat alone publishes machine-readable CSAF VEX for every CVE affecting its products, covering tens of thousands of package-version combinations.
The practical rule: if you're generating SBOMs with Syft, Trivy, or CycloneDX tooling, CycloneDX VEX slots in with the least friction. If you're consuming advisories from large upstream vendors or a government feed, expect CSAF.
Does VEX Actually Change Outcomes in a Real Incident?
Yes — the xz-utils backdoor in March 2024 is the clearest recent case of why exploitability context matters as much as presence. When Andres Freund discovered the malicious backdoor in xz-utils versions 5.6.0 and 5.6.1 (CVE-2024-3094) on March 29, 2024, the SBOM answer was binary and unhelpful in isolation: either the exact malicious version was present or it wasn't. But downstream, the actual risk varied enormously — the backdoor targeted SSH authentication via a patched liblzma linked into sshd through systemd's notify mechanism, specifically on glibc-based Linux distributions using particular build flags. Debian stable, Amazon Linux, and Alpine were never affected because they didn't ship the compromised versions; several rolling-release distros were.
Suppliers who could rapidly issue not_affected statements — citing the specific build configuration, or fixed statements citing a rollback to 5.4.x — gave downstream consumers a way to close tickets in minutes instead of re-auditing every image that merely listed xz in its SBOM. Organizations without VEX tooling spent days manually confirming which of their thousands of container images used the malicious build path.
How Do You Get VEX Data Without a Manual Process for Every CVE?
You don't generate it manually at scale — you need it either produced automatically from your own build and runtime metadata, or ingested continuously from upstream suppliers, and this is where Safeguard fits into the pipeline. Safeguard treats VEX as a first-class artifact alongside SBOM generation, not an afterthought:
- Automated
not_affectedjustification. Safeguard correlates SBOM component data with static reachability analysis and runtime call-graph signals to automatically propose VEX statements — for example, flagging a vulnerable function asvulnerable_code_not_in_execute_pathwhen it's never invoked in your actual code paths, with the justification and evidence trail attached for audit. - Upstream VEX ingestion. Safeguard pulls and normalizes CSAF and CycloneDX VEX feeds from major suppliers (Red Hat, Linux distro maintainers, key open-source projects) and reconciles them against your SBOM inventory automatically, so a vendor's
not_affectedstatement suppresses the matching alert in your pipeline without manual copy-paste. - CVE noise reduction with an audit trail. Every suppressed finding retains its VEX justification, issuing party, and timestamp, so when a SOC 2 or FedRAMP auditor asks why a Critical CVE isn't in your open findings, the answer is a documented, machine-verifiable statement rather than a Slack message from six months ago.
- VEX-aware policy gates. CI/CD gates can be configured to block on
affectedstatus specifically, rather than on raw CVSS score, cutting false-positive build failures without weakening the gate against genuinely exploitable issues. - Continuous re-evaluation. As new code paths are introduced or dependencies shift, Safeguard re-runs reachability analysis and flags any
not_affectedstatement that no longer holds — VEX status isn't a one-time stamp, it's a claim that has to be revalidated as the product changes.
An SBOM tells you what's in the box. A VEX document tells you whether it's loaded. Treating them as a pair — generated, ingested, and revalidated continuously rather than produced once and forgotten — is what turns a compliance artifact into something a security team can actually act on.