OpenVEX Integration with DefectDojo
OpenVEX Integration with DefectDojo
OpenVEX is an open specification for Vulnerability Exploitability eXchange (VEX) documents: JSON statements in which a software producer says whether a product is actually affected by a known vulnerability. Each statement pairs a vulnerability with one or more products, usually identified by Package URL, and a status of not_affected, affected, fixed, or under_investigation, with a justification or action statement where the spec calls for one. The OpenVEX project publishes the spec and a reference CLI, vexctl. DefectDojo imports OpenVEX documents in both the v0.0.1 and v0.2.0 serializations.
OpenVEX Integration with DefectDojo
We import OpenVEX into DefectDojo because our SBOM scans flag CVEs that our suppliers have already looked at and declared harmless, and that context belongs next to the findings. The parser treats VEX for what it is: mostly a suppression signal. A supplier's not_affected statement arrives as an inactive finding with the justification in plain text, not as new work. The one statement that should create work, affected, arrives active with the supplier's action statement as the mitigation.
Why OpenVEX Matters
Software composition scanners match components to advisories. They can't tell whether vulnerable code is present, reachable, or exploitable in a given product. Producers often can.
- VEX lets a supplier say "this CVE does not affect our product" and why, in a machine-readable form.
- The spec's five justifications distinguish a missing component from unreachable code or code an attacker can't control, which matters for audit.
- An
affectedstatement can carry an action statement telling the consumer what to do. - Without a place to record these statements against the right component, teams triage the same supplier CVEs over and over.
Advantages of This Integration
- Statements that suppress don't become work.
not_affectedandfixedstatements import as inactive, so they never count toward open findings or SLA breaches. - Justifications map to real dispositions.
inline_mitigations_already_existmarks a finding mitigated; the three "vulnerable code" justifications mark it a false positive;component_not_present, or a missing justification, marks it out of scope. - Auditable reasoning. The justification, its explanation, any impact statement, the statement author, and the document ID are written into the description, so the supplier's claim can be reviewed later.
- Matching fields with SBOM findings. OpenVEX findings hash on
vuln_id_from_tool,component_name, andcomponent_version, the same fields DefectDojo uses for CycloneDX and SPDX imports. - Per-product precision. A statement naming several products becomes one finding per product, so a claim about one package version doesn't silently cover a sibling.
How This Integration Works
DefectDojo imports OpenVEX documents with the OpenVEX Scan scan type.
1. Produce or collect a VEX document. Suppliers may publish OpenVEX alongside their releases. To write one yourself with vexctl:
vexctl create --product="pkg:oci/payments-api@sha256:abc123" --vuln="CVE-2024-0001" --status="not_affected" --justification="vulnerable_code_not_present" > vex.openvex.json
The parser reads the document's statements. It accepts vulnerabilities and products as bare strings (v0.0.1) or as objects with name and @id (v0.2.0), and imports aliases on a vulnerability object as additional identifiers.
2. Import it. In the UI, open the Engagement, choose Import Scan Results, select OpenVEX Scan, and upload the document. For automation, use the API in Community Edition or DefectDojo Pro:
curl "https://YOUR_INSTANCE/api/v2/import-scan/"
-H "Authorization: Token $DD_API_TOKEN"
-F "scan_type=OpenVEX Scan"
-F "file=@vex.openvex.json"
-F "product_name=payments-api"
-F "engagement_name=Supplier VEX"
-F "auto_create_context=true"
DefectDojo Pro users can use Universal Importer:
universal-importer import
--defectdojo-url "https://YOUR_INSTANCE.cloud.defectdojo.com/"
--scan-type "OpenVEX Scan"
--report-path "./vex.openvex.json"
--product-name "payments-api"
--engagement-name "Supplier VEX"
--auto-create-context
3. Reimport when the supplier updates the document. VEX statements change as investigations finish. Reimport new versions into the same Test with /api/v2/reimport-scan/ so DefectDojo compares the new document with the last one: statements that disappeared are mitigated and new ones are added. When a statement changes status, for example from under_investigation to not_affected, check the matched finding's disposition by hand rather than assuming it flipped.
Data Granularity: What Gets Imported
| DefectDojo Field | Source in OpenVEX Document | Notes |
|---|---|---|
| Title | Product and vulnerability | Product name and version, a separator, then the vulnerability ID |
| Severity | status |
affected is Medium; every other status is Info. VEX carries no severity |
| Active | status |
not_affected and fixed inactive; affected, under_investigation, and unknown statuses active |
| Mitigated / False Positive / Out of Scope | justification |
Applied to not_affected statements; fixed is always mitigated |
| Verified | status |
under_investigation is explicitly unverified |
| Description | Statement summary, justification, impact and action statements, author, document @id |
Notes when a required justification is missing |
| Mitigation | action_statement or status text |
Action statement for affected; fixed text otherwise |
| References | Document @context, vulnerability @id |
|
| Component Name / Version | Product identifier | Split at @ after dropping PURL qualifiers |
| Vulnerability IDs | Vulnerability name and aliases |
|
| Vuln ID from Tool | Vulnerability name | |
| Unique ID from Tool | Product and vulnerability | Duplicate pairs within a file collapse to one |
| Date | Statement or document timestamp |
|
| Finding type | Static | |
| Deduplication | Hashcode | vuln_id_from_tool, component_name, component_version |
Use Cases
Supplier triage records: A vendor ships a container image and a VEX document saying a dozen flagged CVEs don't affect it. Importing the document into the image's Asset records each claim, with its justification, as an inactive finding the security team can review and cite.
Acting on affected statements: A supplier publishes an affected statement with an action statement telling consumers to change a configuration. That arrives as an active Medium finding with the instruction as the mitigation, assigned to the team that runs the component.
Tracking open investigations: Statements marked under_investigation import active, unverified, and Info. A weekly review of that list shows which supplier investigations are still open and which have been resolved by a newer document.
Publishing your own VEX: A product security team writes VEX with vexctl for its own releases and imports it, so DefectDojo holds the same exploitability position the team gives customers.
Operational Tips
- Keep VEX imports in their own Engagement or Test. That makes it obvious which findings are supplier statements and which are scanner results.
- The parser sets the status of the findings it creates. Check how VEX findings line up with your SBOM scanner's findings for the same component before relying on them to change triage decisions on the scanner side.
- Product identifiers matter. The component name and version come from the PURL, so a statement written against a different identifier from the one your SBOM uses will not line up.
- Don't set
minimum_severityabove Info on VEX imports. Every status exceptaffectedimports as Info, and filtering it out discards the suppression statements. - Watch for missing justifications. A
not_affectedstatement without one is marked out of scope and flagged as incomplete in the description; ask the supplier for a corrected document. - An unrecognized status stays active by design, so a future spec status can't hide a real vulnerability.