For engineers

Buyers asking for VEX? CycloneDX, OpenVEX and CSAF compared

By Manish SharmaPublished 7 min read

When a vulnerability makes the news, buyers ask whether your product is affected. CycloneDX, OpenVEX and CSAF all say the same thing, that a product is affected, not affected, fixed or under investigation for a vulnerability. But they name the product, the vulnerability and the reason differently. Map the four statuses one to one, and carry any justification label the target format lacks as text instead of guessing. Identify the product so the customer's tools can match it, and keep non-CVE identifiers out of fields that accept only CVEs.

In short

  • CycloneDX, OpenVEX and CSAF all say the same thing: a product is affected, not affected, fixed or under investigation for a vulnerability. The four statuses map one to one.
  • They differ in how they name the product and the vulnerability, and in their justification labels. Every problem we hit in our own exports was in those details.
  • Never invent a justification label the target format lacks; carry it as text. Name the product so the customer's tools can match it.

Same sentence, different details

All three formats carry the same sentence: this product, for this vulnerability, has this status, for this reason. The four statuses map one to one. What differs is how each names the product and the vulnerability, which justification labels it has and what it insists on. In our own export work, every problem we hit was in those details, not in the status.

This post sets the formats side by side, gives the mappings we use in our own exports and lists the mistakes that cost us time.

What a VEX statement must say

CISA's Minimum Requirements for VEX (April 2023) describe a VEX document as a set of statements. Each ties a product and a vulnerability to one of four statuses: not affected, affected, fixed or under investigation. A not affected statement needs a justification or an impact statement, and an affected one needs an action statement.

CISA's VEX Status Justifications (June 2022) define five machine-readable reasons for not affected:

  • component_not_present
  • vulnerable_code_not_present
  • vulnerable_code_not_in_execute_path
  • vulnerable_code_cannot_be_controlled_by_adversary
  • inline_mitigations_already_exist

CERT-In's SBOM guidelines (version 2.0, July 2025) ask suppliers for the same four statuses, and recommend a CSAF advisory after the VEX.

The three formats side by side

The three formats side by side
CycloneDXOpenVEXCSAF
Steward and versionOWASP and Ecma TC54; 1.6 is what we export, 1.7 the latest releaseOpenVEX project; v0.2.0OASIS; 2.0 is the standard, 2.1 a committee draft
Where the status livesvulnerabilities[].analysis.statestatements[].statusvulnerabilities[].product_status lists of product ids
How the product is namedaffects[].ref: a bom-ref in the same BOM, or a BOM-Link to anotherproducts[].@id: an IRI, usually a purl, with optional subcomponentsproduct_id, a token local to the document, defined in product_tree with optional identification helpers
How the vulnerability is namedid with a source (name and URL)vulnerability.name with aliasescve (CVE format only) or ids with system_name and text
Not affected needsA justification "should be specified"A justification label or an impact statement (MUST)A flag or a threat of category impact (VEX profile)
Affected needsNothing mandatory; response and recommendation availableAn action statement (MUST)A remediation as action statement (VEX profile)
GranularityPer componentPer statement, usually per productPer product id

Mapping the statuses

The statuses map cleanly, and these are the values we write.

Mapping the statuses
StatusCycloneDX analysis.stateOpenVEX statusCSAF product_status
Not affectednot_affectednot_affectedknown_not_affected
Affectedexploitableaffectedknown_affected
Fixedresolvedfixedfixed
Under investigationin_triageunder_investigationunder_investigation

CycloneDX has two more states. resolved_with_pedigree is a fixed with evidence of the change, and maps to fixed. false_positive says the vulnerability was wrongly associated with the component. The other formats have no word for it, so if you use it, carry the reason as text in a not affected statement.

Mapping the justifications

The justifications do not map cleanly. OpenVEX and CSAF flags share CISA's five labels. CycloneDX has nine of its own, and only two have exact twins.

Mapping the justifications
CycloneDX justificationCISA label (OpenVEX, CSAF flag)How we export it
code_not_presentvulnerable_code_not_presentLabel to label, both ways
code_not_reachablevulnerable_code_not_in_execute_pathLabel to label, both ways
requires_configuration, requires_dependency, requires_environmentNo exact twinKept as text in the impact statement
protected_by_compiler, protected_at_runtime, protected_at_perimeterNo exact twinKept as text in the impact statement
code_not_presentAlso written for component_not_presentFrom CISA labels into CycloneDX only
protected_by_mitigating_controlWritten for inline_mitigations_already_exist and vulnerable_code_cannot_be_controlled_by_adversaryFrom CISA labels into CycloneDX only

Going into CycloneDX, three CISA labels land on CycloneDX's broader neighbours. Coming out of CycloneDX, we never invent a CISA label: a justification with no exact twin travels as words in the impact statement.

A customer's policy engine may act on vulnerable_code_cannot_be_controlled_by_adversary very differently from requires_environment. Quietly turning one into the other changes your statement.

The mistakes we hit

Product identifiers that nobody can match

A firmware release is not a package, so it has no natural purl. Each format handles that differently, and each has a trap.

In CycloneDX, a VEX entry points at a bom-ref. If the VEX sits in a separate file from the SBOM, a bare bom-ref points at nothing. Use a BOM-Link (urn:cdx: with the SBOM's serial number, version and the reference) so the consumer can join the two.

In OpenVEX, @id should be something the customer can look up. A purl works for a package, but for a firmware product, use an IRI you control and keep it stable across releases.

In CSAF, product_id is only a token inside the document, and CSAFPID-0001 means nothing to an asset database. The product_identification_helper is what a customer matches on: CPE, purl, hashes, model numbers, SKUs, serial numbers or SBOM URLs. Our own CSAF export names the product and the release. If your customers match devices by model number or image hash, add those helpers before you publish.

Identifiers that are not CVEs

Plenty of vulnerabilities in open source packages carry a GitHub, OSV or ecosystem identifier first and a CVE later, or never. CSAF's cve field accepts only the CVE pattern, and a document that puts a GHSA identifier there fails validation. Such identifiers belong in ids with a system_name.

CycloneDX takes any id but needs a source so the reader knows where to look, and we write NVD for CVEs and OSV for the rest. OpenVEX takes any name and lists the others under aliases. When a CVE exists, make it the main name, because that is what most customers' scanners match.

One vulnerability, two components, two statuses

CycloneDX states exploitability per component, so the same CVE can be not affected in one library and affected in another inside the same image.

OpenVEX and CSAF statements in our exports speak for the whole product, and CSAF's mandatory test 6.1.23 forbids one CVE in two vulnerability items. So the product needs one status per vulnerability. We take the most urgent: affected, then under investigation, then fixed, then not affected. Taking the least urgent would be the dangerous mistake.

Statements missing their required parts

The commonest validation failure is a not affected statement with neither a label nor an impact statement, followed by an affected statement with no action. CSAF's VEX profile tests both. Its mandatory tests in section 6.1.27 check for an impact statement on every known not affected product, and an action statement on every known affected one.

Write the impact statement even when you have a label. A sentence a person wrote is what a customer's security reviewer reads.

A CSAF draft that says it is final

CSAF's test 6.1.17 requires the tracking status to be draft when the document version is 0 or a pre-release. Publish version 1 or later as final, and keep the revision history in step with every change.

Status that never moves

Under investigation is a promise. A VEX with statements stuck there for months tells the customer nobody looked. Version your VEX documents (OpenVEX's version, CSAF's revision history, CycloneDX's BOM version) and reissue them when the status changes.

A short checklist before you publish

  • Every product named in a way the customer can match: purl, CPE, model number, SKU or hash.
  • Every vulnerability under its CVE when one exists, with the other identifiers as aliases.
  • One status per vulnerability per product, the most urgent when components disagree.
  • Every not affected with a label the format really has, or words; every affected with an action.
  • The document validated against its schema, and for CSAF against the VEX profile tests.
  • A version number and a date, and a plan to revisit everything under investigation.

Writing VEX once, exporting it three ways

In FDIE by Magdox, your people set the VEX status and justification for each vulnerability in a release, and the app never decides them. From that one record it exports CycloneDX, OpenVEX and CSAF 2.0, with the mappings above, so the three files cannot disagree with each other.

The export buttons on the VEX statements page in FDIE, for CycloneDX VEX, OpenVEX and CSAF.

Questions

What is the difference between VEX and an SBOM?

An SBOM lists the components in a product. A VEX states, for a given vulnerability, whether the product is affected, not affected, fixed or under investigation, and why.

Which VEX format should I use?

Use what your customers' tools read. CycloneDX VEX fits teams exchanging CycloneDX SBOMs, OpenVEX is small and easy to generate, and CSAF 2.0 is the OASIS standard for security advisories that many vendors and CERTs publish. Exporting all three from one source of truth avoids the choice.

Can a CSAF document describe a vulnerability that has no CVE?

Yes. The cve field accepts only identifiers of the form CVE-year-number. Anything else, such as a GitHub or OSV identifier, goes in the ids list with a system name and the identifier text.

What must a not affected statement include?

OpenVEX requires a justification label or an impact statement. CSAF's VEX profile requires a flag or a threat of category impact for every known not affected product. CycloneDX says a justification should be given for every not_affected case.

Who should write VEX statements?

The people responsible for the product. A VEX is the manufacturer's word to its customers about exploitability, so tools can propose evidence, but a person should set the status.

Sources

  1. CISA, Minimum Requirements for Vulnerability Exploitability eXchange (VEX), April 2023 cisa.gov
  2. CISA, VEX Status Justifications, June 2022 (PDF) cisa.gov
  3. OpenVEX specification v0.2.0 github.com
  4. CycloneDX specification overview cyclonedx.org
  5. CycloneDX 1.6 JSON reference cyclonedx.org
  6. OASIS Common Security Advisory Framework (CSAF) Version 2.0, OASIS Standard, 18 November 2022 docs.oasis-open.org
  7. OASIS CSAF Version 2.1, Committee Specification Draft 03, 11 September 2026 docs.oasis-open.org
  8. CERT-In Technical Guidelines on SBOM, QBOM and CBOM, AIBOM and HBOM, version 2.0, 9 July 2025 (PDF) cert-in.org.in
  9. Package URL (purl) specification github.com
  10. OSV schema ossf.github.io

Product and project names are trademarks of their owners, used only to identify the public firmware, software and standards discussed. MAGDOX is not affiliated with or endorsed by them.

  • For leaders

    Buyers now ask for your SBOM. Here is what they check

    Buyers and regulators now ask device makers for an SBOM before they buy. A software bill of materials is now a condition of sale. BIS asks for one with every CCTV registration, and CERT-In recommends that public buyers require one in SPDX or CycloneDX. The EU Cyber Resilience Act puts one in every technical file. Buyers check that it names each component with its version and supplier, and that it matches the firmware you actually ship. They also want a VEX statement explaining which listed vulnerabilities do not affect the product.

    · 5 min read

  • For leaders

    Choosing a firmware security platform: questions to ask first

    Firmware security tools are easy to compare on feature names and hard to compare on evidence. Choose a firmware security platform by what it can prove on your own images. Look at the formats and processors it reads, how it reports what it could not read, which SBOM and VEX formats it exports and which markets' rules it maps to. On the vendors' own pages, ONEKEY publishes a longer list of firmware formats, exports more CycloneDX versions and holds ISO/IEC 27001. FDIE by Magdox covers India's ER-01, NCCS ITSAR and TEC 31318, exports VEX in three formats and publishes measured rates. Both import supplier SBOMs.

    · 6 min read

  • For leaders

    Exporting devices to the EU? What the CRA asks of you

    Indian makers who sell into the EU are covered by the Cyber Resilience Act even without an EU office. The Cyber Resilience Act applies to Indian companies whose connected products are sold in the EU, through their own brand or through an importer. Since 11 September 2026 they must report actively exploited vulnerabilities within 24 hours. From 11 December 2027, every product they place on the EU market must meet the essential requirements, with a technical file kept for at least ten years.

    · 6 min read

Find out what the lab would flag in your firmware

A 45-minute call with our team. Bring one firmware image.

Request access