Free tool

VEX generator (CycloneDX + CSAF)

Turn a product, CVE and status into a CycloneDX VEX and an OASIS CSAF VEX document at once. Runs in your browser; nothing is uploaded.

Last updated:

This generator turns a product name, a CVE or vulnerability ID, a status and a justification into two machine-readable VEX documents at once: a CycloneDX VEX statement and an OASIS CSAF 2.0 VEX document, the two implementations CISA's VEX Use Cases guidance names alongside OpenVEX and SPDX 3.0. Everything runs in your browser with no file upload or server call, so it is safe to try with a real CVE before deciding which format your SBOM tooling or PSIRT pipeline actually consumes.

Describe the vulnerability and your assessment

Suggested CSAF flag for this justification: vulnerable_code_not_in_execute_path — CycloneDX justification "code_not_reachable" (Vulnerable code is not invoked at runtime) is the closest match to CSAF's "vulnerable_code_not_in_execute_path" flag.

Fill in both the product name and the CVE/vulnerability ID to generate valid documents.

CycloneDX VEX

{
  "bomFormat": "CycloneDX",
  "specVersion": "1.5",
  "version": 1,
  "metadata": {
    "timestamp": "2026-11-04T13:36:47.431Z",
    "component": {
      "type": "application",
      "name": "",
      "bom-ref": "product-1"
    }
  },
  "components": [
    {
      "type": "application",
      "name": "",
      "bom-ref": "product-1"
    }
  ],
  "vulnerabilities": [
    {
      "id": "",
      "analysis": {
        "state": "not_affected",
        "justification": "code_not_reachable"
      },
      "affects": [
        {
          "ref": "product-1"
        }
      ]
    }
  ]
}

CSAF 2.0 VEX

{
  "document": {
    "category": "csaf_vex",
    "csaf_version": "2.0",
    "title": "VEX: unspecified product — unspecified vulnerability",
    "publisher": {
      "category": "vendor",
      "name": "Example Vendor",
      "namespace": "https://example.com"
    },
    "tracking": {
      "id": "VEX-unspecified-unspecified",
      "status": "final",
      "version": "1",
      "initial_release_date": "2026-11-04T13:36:47.432Z",
      "current_release_date": "2026-11-04T13:36:47.432Z",
      "revision_history": [
        {
          "date": "2026-11-04T13:36:47.432Z",
          "number": "1",
          "summary": "Initial version."
        }
      ]
    }
  },
  "product_tree": {
    "full_product_names": [
      {
        "product_id": "CSAFPID-0001",
        "name": ""
      }
    ]
  },
  "vulnerabilities": [
    {
      "product_status": {
        "known_not_affected": [
          "CSAFPID-0001"
        ]
      },
      "flags": [
        {
          "label": "vulnerable_code_not_in_execute_path",
          "product_ids": [
            "CSAFPID-0001"
          ]
        }
      ]
    }
  ]
}

Both documents are built entirely in your browser from the fields above. Nothing you type here is sent to SoftWSP.

Need help setting up a repeatable VEX/PSIRT process?

Which VEX status and justification should I pick?

Pick Not affected only when you can point to a specific, defensible reason the vulnerable code cannot be exploited in your product as shipped — that reason is the justification field. Pick Affected when exploitation is possible and no fix exists yet, Fixed once a patched version is released, and Under investigation while triage is still in progress. These four map directly onto the status categories CISA's Minimum Requirements for VEX document defines, which is why both the CycloneDX and the CSAF document this tool generates can represent all four without losing information.

CycloneDX’s nine justification values are more granular than the five CSAF/CISA flag labels, so this generator maps each CycloneDX value to the closest CSAF flag for you and shows the mapping it used — a reason your code is simply absent maps to a code-presence flag, a reason exploitation needs a condition an attacker cannot set maps to the "cannot be controlled by adversary" flag, and any "protected by/at …" reason maps to "inline mitigations already exist". Both documents still carry your free-text impact statement in full, so the mapping never hides the original reasoning.

Sources

  1. CycloneDX: Vulnerability Exploitability (VEX) capability page
  2. OASIS: Common Security Advisory Framework Version 2.0 (CSAF, OASIS Standard)
  3. CISA: Minimum Requirements for Vulnerability Exploitability eXchange (VEX)
  4. CISA: VEX Use Cases
  5. CycloneDX: Specification version history
  6. CISA: Vulnerability Exploitability eXchange (VEX) – Status Justifications

Frequently asked questions

What is the difference between a CycloneDX VEX document and a CSAF VEX document?

Both say the same thing — whether a specific vulnerability actually affects a specific product — in two different JSON shapes. CycloneDX VEX is a `vulnerabilities[].analysis` object (state, justification, detail) attached to a CycloneDX Bill of Materials, built for SBOM tooling that already reads CycloneDX. CSAF 2.0 VEX is OASIS’s Profile 5 of its Common Security Advisory Framework, built for PSIRT advisory pipelines that already publish CSAF. CISA’s own VEX guidance lists both, alongside OpenVEX and SPDX 3.0, as VEX implementations in active use, so this generator outputs both from one form instead of picking a side.

When do I need to fill in the "justification" field?

Only when status is "Not affected". Both formats require a reason in that case: CycloneDX calls it `analysis.justification` with 9 possible values (code_not_present, code_not_reachable, requires_configuration, and so on); CSAF calls it a `flags[].label` with 5 coarser values (vulnerable_code_not_present, vulnerable_code_not_in_execute_path, vulnerable_code_cannot_be_controlled_by_adversary, inline_mitigations_already_exist, component_not_present). This generator asks for the CycloneDX justification once and maps it to the matching CSAF flag automatically.

Does the generated CSAF document need a real publisher namespace to be valid?

The CSAF 2.0 schema requires `document.publisher` to carry a category, a name and a namespace, so this generator always fills those in — from the "Supplier" and "Vendor namespace" fields if you set them, or from a clearly fake placeholder (Example Vendor / https://example.com) otherwise. Replace the placeholder with your own organisation’s identity before publishing the document to anyone else.

Can I use this generator’s output as my product’s official VEX feed?

Treat it as a starting draft, not a final publication pipeline. It produces one vulnerability per document from the fields you type in, which is enough to check the structure both formats expect or to hand a developer a correctly shaped example; a real VEX feed usually needs a stable publisher identity, a revision history across multiple releases, and (for CSAF) a discoverable provider-metadata endpoint, none of which a one-off browser form can maintain for you.

Why does the tool only ask for one product and one vulnerability?

Keeping the form to one product/one CVE at a time keeps every field directly traceable to a line in the output, which is what makes it useful for learning the schema or spot-checking a single advisory. Teams publishing VEX for an entire product line at scale typically generate it from their vulnerability-management system rather than typing it in by hand — this tool is for the one-off case and for understanding what that automation needs to produce.

Is a CVE required, or can I use another vulnerability identifier?

Either works. If the ID you enter matches the CVE-YYYY-NNNN pattern, the CycloneDX document uses it as the vulnerability `id` and the CSAF document places it in the dedicated `cve` field; any other identifier (a GHSA ID, a vendor advisory ID) is still accepted and is carried in CycloneDX’s `id` field and CSAF’s `ids[]` array instead, since CSAF’s `cve` field is specifically typed to the CVE format.