Vulnerabilities
VEX: what a Vulnerability Exploitability eXchange document says
Match an SBOM against a vulnerability database and you get a long list of CVEs, many of which cannot be exploited in the product. VEX is how a supplier says which ones apply: a machine-readable statement, per vulnerability, of whether the product is affected and why.
Updated
What is VEX?
VEX, short for Vulnerability Exploitability eXchange, is a machine-readable statement about whether a product is affected by a specific known vulnerability. A supplier publishes it so that the people who use the product know which vulnerabilities need action and which do not. In most organizations that supplier work sits with the product security incident response team (PSIRT).
CISA's Minimum Requirements for VEX (April 2023) define it independently of any file format: a VEX document is a container for one or more VEX statements, and each statement gives one status for one vulnerability in one or more products. VEX is designed to work with SBOMs, vulnerability databases and security advisories, but needs none of them.
Why VEX exists: SBOM noise
An SBOM lists every component in a product, including transitive dependencies. A scanner matches those components against advisories and reports every CVE in every version it finds. Many of those findings do not apply: the vulnerable function is never called, a build option compiles it out, or the product's configuration blocks the attack. Without VEX, each customer has to work that out alone, or file a support ticket asking the supplier.
VEX moves that analysis to the party that can do it once, for everyone: the supplier. The customer's scanner reads the VEX document and suppresses the findings the supplier has marked as not affecting the product.
The four VEX statuses
Every VEX statement carries exactly one of four statuses:
| Status | Meaning | Also required |
|---|---|---|
not_affected | No remediation or mitigation is required. The vulnerability does not affect the listed products. | A justification, or an impact statement explaining why |
affected | The vulnerability affects the listed products, and the author recommends action. | An action statement: what users should do |
fixed | The listed product versions contain a fix. | Nothing extra |
under_investigation | The author is investigating and has not yet decided. | Nothing extra; expected to change |
VEX justifications for not_affected
A not_affected statement has to say why. CISA's requirements say it SHOULD use one of five
justifications, and MUST give a free-text impact statement if it does not. OpenVEX and CSAF use the same five:
-
component_not_present: the vulnerable component is not included in the product. -
vulnerable_code_not_present: the component is included, but the vulnerable code is not, typically because of how it was configured or built. -
vulnerable_code_not_in_execute_path: the vulnerable code is present but the product never calls it. -
vulnerable_code_cannot_be_controlled_by_adversary: the code is present and used, but an attacker cannot control it to exploit the vulnerability. -
inline_mitigations_already_exist: built-in protections that users cannot disable prevent exploitation.
The justifications differ in what evidence they need. component_not_present needs an accurate record
of what the product contains. The middle three need code analysis, such as reachability analysis or a manual
review, and are the ones customers most often question.
What a VEX statement contains
CISA's minimum requirements list these elements:
- Document metadata: a document ID, a version that increases with every change, the author (a person or organization, not a tool), and when it was first issued and last updated.
- Product details: one or more product identifiers, ideally the same identifiers the product's SBOM uses, such as a package URL (purl). A statement may also name the subcomponent that carries the vulnerability.
- Vulnerability details: one vulnerability ID per statement, usually a CVE, and a description or a link to one.
- Status, with the justification, impact statement or action statement it requires.
- Statement metadata: an ID, a version and timestamps for the statement itself.
An example VEX document
This OpenVEX document says that a product, example-api 3.2.0, ships a vulnerable version of
log4j-core but is not affected by Log4Shell, because the vulnerable class is removed during the build:
{
"@context": "https://openvex.dev/ns/v0.2.0",
"@id": "https://example.com/vex/2026-0042",
"author": "Example Corp PSIRT",
"timestamp": "2026-10-02T09:00:00Z",
"version": 1,
"statements": [
{
"vulnerability": { "name": "CVE-2021-44228" },
"products": [
{
"@id": "pkg:generic/example/[email protected]",
"subcomponents": [
{ "@id": "pkg:maven/org.apache.logging.log4j/[email protected]" }
]
}
],
"status": "not_affected",
"justification": "vulnerable_code_not_present",
"impact_statement": "The JndiLookup class is removed from log4j-core at build time."
}
]
} The product and its subcomponent are identified by package URLs, so a scanner can match them against the components in the product's SBOM. The same statement in CycloneDX or CSAF carries the same facts in a different structure.
VEX formats: OpenVEX, CycloneDX VEX and CSAF VEX
VEX is a set of requirements, not a file format. CISA names three formats that can carry it, and they are not interchangeable:
| OpenVEX | CycloneDX VEX | CSAF VEX | |
|---|---|---|---|
| What it is | A small JSON format made only for VEX | The vulnerabilities section of a CycloneDX document | The csaf_vex profile of the CSAF advisory standard |
| Standard | Open specification, v0.2.0 | ECMA-424 (CycloneDX 1.6 and later) | OASIS Standard (CSAF 2.0, 2022) |
| Statuses | The four VEX statuses |
Its own analysis states: not_affected, exploitable, in_triage,
resolved, resolved_with_pedigree, false_positive |
Product status lists: known_not_affected, known_affected, fixed,
under_investigation |
| Justifications | The five CISA justifications |
Nine of its own, such as code_not_reachable and requires_configuration | The five CISA justifications, as flags |
| Fits teams that | Want the smallest possible VEX file | Already ship CycloneDX SBOMs | Already publish CSAF security advisories |
| Read by Trivy | Yes | Yes, for SBOM scans | Yes |
| Read by Grype | Yes | Not listed | Yes |
OpenVEX is the lightest: a document with an author, a timestamp, a version and a list of statements. Its specification treats VEX as a sequence of statements, each one overriding or adding to the ones before it.
CycloneDX VEX uses the same vulnerabilities structure CycloneDX uses for everything
else, with an analysis object holding a state, a justification, response values such as
update or will_not_fix, and free-text detail. The VEX data can sit inside the SBOM or in a
separate CycloneDX document that points into the SBOM with BOM-Link. Note that its justification vocabulary is
not the CISA one, so converting to or from OpenVEX or CSAF needs a mapping. See
CycloneDX vs SPDX for the SBOM side.
CSAF VEX is a full CSAF document with the category csaf_vex. It requires a product
tree, a CVE or other ID, notes, and for each product an impact statement (if not affected) or a remediation (if
affected). It is heavier than OpenVEX, but it fits organizations that already distribute
CSAF advisories through a CSAF provider.
VEX vs VDR
A Vulnerability Disclosure Report (VDR) lists the vulnerabilities that do affect a product, together with their analysis and remediation. NIST SP 800-161 recommends VDRs as a supply chain practice, and CycloneDX supports both. The difference, as the CycloneDX examples put it:
- VDR says which vulnerabilities affect the product. It can cover vulnerabilities found in your own code that were never published elsewhere.
- VEX is mainly a negative statement: which known vulnerabilities do not affect the product, and why. It covers known vulnerabilities only.
Many suppliers publish both: the VDR answers "what do I need to fix", the VEX answers "what can I ignore".
How scanners consume VEX
A VEX document only saves work if the customer's tools read it. Two widely used open source scanners do:
- Trivy takes VEX through its
--vexoption (marked experimental) from a local file, a VEX repository, an OCI attestation or a reference in the SBOM. It reads OpenVEX, CycloneDX VEX for SBOM scans, and CSAF VEX, and stops reporting vulnerabilities markednot_affected. - Grype takes OpenVEX and CSAF VEX through
--vex. It movesnot_affectedandfixedmatches to an ignored list, shown with--show-suppressed, and can optionally addaffectedandunder_investigationstatements to the results.
Matching depends on identifiers. If the VEX names the product with a different identifier than the SBOM uses, the scanner cannot connect them and the finding stays.
Who publishes VEX
VEX usually comes from the supplier: the vendor, the open source distribution or the device manufacturer that ships the product. Inside the supplier, it is a PSIRT task, part of the same vulnerability management lifecycle that produces advisories and fixes. Red Hat, for example, publishes CSAF VEX documents in a dedicated directory of its CSAF feed, next to its advisories. For what PSIRT engineers do day to day, see PSIRT role.
Customers also write VEX for their own use, recording triage decisions about a supplier's product so their scanner stops raising them. Regulations such as the EU Cyber Resilience Act ask manufacturers to handle vulnerabilities in the components they ship, and VEX is one way to tell users which of them apply.
Keeping VEX current
- Statuses move. A statement typically starts as
under_investigationand ends asnot_affected, or asaffectedand thenfixed. Publish each change; CISA requires the version to increase every time. - New releases need new statements. A
not_affectedverdict for 3.2.0 says nothing about 3.3.0. Re-check it whenever the component, its version or the build configuration changes. - Keep VEX separate from the SBOM. The SBOM describes a release and stays fixed. VEX keeps changing after the release, so publishing it as its own document avoids reissuing SBOMs.
- Publish where tools look. Next to the SBOM, as an attestation on the container image, or in your advisory feed, and say where in your disclosure policy. See coordinated vulnerability disclosure.
Limits of VEX
- It is a claim, not proof. A
not_affectedstatement is the author's assessment. CISA recommends tying the author's identity to the document cryptographically, but most consumers still trust VEX because they trust the supplier. - Silence means nothing. VEX implies no default status. A vulnerability with no statement is unknown, not unaffected.
- Formats do not map one to one. CycloneDX uses different states and justifications from OpenVEX and CSAF, so conversions lose detail.
- It is only as good as the inventory. Saying a component is not present requires knowing exactly what the product contains, including transitive dependencies and anything the build downloaded outside the package manager.
VEX starts with an accurate SBOM
Every VEX statement points at components, and the easiest justification to defend,
component_not_present, is only true if the inventory is complete. An SBOM generated by
scanning lockfiles after the fact can list development dependencies that never ship and miss packages the build
fetched some other way.
CRACI runs GitHub Actions builds on its own runners and records the packages each job actually fetched, including transitive dependencies, packages restored from caches and dependencies no lockfile lists. Each SBOM carries a completeness state and exports as CycloneDX or SPDX. Monitored SBOMs are re-evaluated continuously, build history shows which past builds contained a vulnerable dependency, and CRACI supports VEX. See SBOM generation and vulnerability tracking.
VEX: frequently asked questions
What does VEX stand for?
VEX stands for Vulnerability Exploitability eXchange. It is a machine-readable statement of whether a product is affected by a specific known vulnerability, published so that users know which reported vulnerabilities need action.
What is the difference between an SBOM and VEX?
An SBOM lists the components in a product. VEX says, for a given vulnerability, whether that product is affected. The SBOM rarely changes after a release; VEX statements change as the supplier investigates and fixes. That is why VEX is usually published as a separate document that refers to the product, rather than baked into the SBOM.
What are the four VEX statuses?
not_affected, affected, fixed and under_investigation. A not_affected statement needs a justification or an impact statement, and an affected statement needs an action statement saying what users should do.
Which VEX format should I use?
Use the one your consumers and tools read. OpenVEX is the smallest and works with Trivy and Grype. CycloneDX VEX suits teams that already ship CycloneDX SBOMs. CSAF VEX suits organizations that already publish CSAF security advisories, which is common in industrial and enterprise software. Many suppliers end up converting between them.
Is VEX the same as a security advisory?
No. A security advisory explains a vulnerability, who is affected and how to fix it, mainly for people. VEX is a narrow, machine-readable status per product and vulnerability, mainly for tools. The two often travel together: the CSAF standard has both a security advisory profile and a VEX profile.
Who writes VEX documents?
Usually the supplier of the product, often its product security incident response team (PSIRT), because it knows how the product is built and configured. CISA's minimum requirements also allow third parties to author VEX, and every document must name its author.
Do vulnerability scanners read VEX?
Several do. Trivy accepts OpenVEX, CycloneDX and CSAF VEX through its --vex option and drops findings marked not_affected. Grype accepts OpenVEX and CSAF VEX through --vex and moves not_affected and fixed findings to an ignored list.
Know which vulnerable packages your builds contain
A VEX statement is only as good as the inventory behind it. Book a demo to see the packages each GitHub Actions build actually fetched.
Book a demo