CSAF
CSAF 2.0 (Common Security Advisory Framework) is the OASIS standard for machine-readable vendor security advisories, and the format CISA has driven adoption of. Vendors including Red Hat, SUSE, Cisco and Siemens publish their advisories as CSAF.
Supported: CSAF 2.0 JSON. CSAF 1.x used the older CVRF schema and is rejected with a clear message rather than mis-parsed.
One finding per vulnerability and product
A single CSAF advisory routinely covers many products in different states — one patched, another still exposed, a third explicitly unaffected. The parser therefore emits one finding per (vulnerability, product) pair, so each product’s actual status is preserved.
★ known_not_affected products are imported inactive
product_status.known_not_affected is the vendor explicitly stating a product is not exposed.
Importing that as an active finding would turn the vendor’s “you are fine” into work, the same mistake a
mishandled VEX statement makes. Those findings are imported inactive and flagged as false positives,
with the status recorded in the description.
product_status bucket | Active? | Disposition |
|---|---|---|
known_affected, first_affected, last_affected | Yes | Active |
known_not_affected | No | false_p |
fixed, first_fixed, recommended | No | is_mitigated |
under_investigation | Yes | Active, verified=False |
| anything else | Yes | Active — a bucket a future revision adds must never silently suppress a real exposure |
Product tree resolution
A CSAF product id is never defined in one place. The parser indexes all three mechanisms:
full_product_names[]— a flat{product_id, name}list.branches[]— an arbitrarily deep tree. The version lives on aproduct_versionbranch, not in the leaf, so the tree is walked and the nearest enclosing version is carried down. Where the leaf name repeats the version (“Generic App 1.0.0”), it is stripped socomponent_nameis just “Generic App” — otherwise the same product would deduplicate differently depending on which shape the advisory used.relationships[]— a synthesised product (“library as a component of app”) whose ownfull_product_namedefines a new product id.
Field mapping
| CSAF | DefectDojo |
|---|---|
vulnerabilities[].cve (else ids[].text, for an embargoed issue) | vuln_id_from_tool, unsaved_vulnerability_ids |
scores[].cvss_v3.baseSeverity | severity |
scores[].cvss_v3.baseScore / vectorString | cvssv3_score / cvssv3 |
cwe.id | cwe |
remediations[] filtered to the product | mitigation (category, details, url, restart_required) |
notes[] of category description/summary/details | description |
references[] | references |
document.tracking.current_release_date | date |
scores[] entries are scoped to a product list, so the same CVE can be scored differently per product;
each finding gets its own product’s score. An advisory with no score at all becomes Medium, not
Info — a published vendor advisory is not informational, and Info would hide it behind a
minimum-severity setting.
Sample Scan Data
Sample CSAF files are available at unittests/scans/csaf.
Obtaining an importable file
Unlike a scanner report, CSAF documents are published, not generated by running a tool over a target. A CSAF advisory is authored by a product security team, so there is no command that produces one from your own environment. Obtain them from a vendor’s CSAF distribution:
- Vendors publish a
provider-metadata.jsonunder/.well-known/csaf/describing their feed. csaf_downloader(the reference Go implementation) mirrors a vendor’s advisories:
csaf_downloader --directory ./advisories https://vendor.example.com- Individual advisories can also be downloaded directly from a vendor’s security page.
Validate a document before importing with the same project’s validator:
csaf_validator ./advisories/2026/example-2026-0001.jsonThe fixtures committed with this parser were authored against the CSAF 2.0 schema and verified with
csaf_validator from github.com/gocsaf/csaf/v3 — all three pass schema validation.
Default deduplication hashcode fields
vuln_id_from_tool, component_name, component_version.