CSAF output¶
vexcalibur generate --format csaf writes CSAF 2.0 JSON with the CSAF VEX
profile. A controlled timestamp and finding set produce repeatable output.
Vexcalibur implements the renderer directly without a CSAF runtime library.
Vexcalibur targets the CSAF 2.0 OASIS
Standard,
including the Approved Errata 01
context.
CSAF 2.1 remains Committee Specification Draft
02,
so this renderer does not silently opt users into it. --csaf-version accepts
only 2.0.
Document contract¶
Field |
Value |
|---|---|
|
|
|
|
|
Required |
|
Required |
|
Required normalized absolute publisher URL |
|
|
|
Required |
|
|
|
|
|
|
|
Installed Vexcalibur version |
|
Flat list of products referenced by assertions |
|
One object per vulnerability ID |
The initial release, current release, revision, and generator dates all use
--timestamp; when it is absent, generation uses current UTC. The sole
revision-history item has number 1, summary Initial version., and the same
date. A draft, final, or interim initial document still uses version 1.
The root does not contain $schema: strict CSAF 2.0 validation rejects that
additional property. Vexcalibur does not invent document.distribution, TLP,
or other publication policy.
JSON keys and arrays are deterministic. Products, vulnerabilities, status product IDs, references, notes, remediations, and threats have stable ordering.
CLI metadata rules¶
These values are required with --format csaf:
--csaf-document-id.--csaf-document-title.--csaf-publisher-name.--csaf-publisher-namespace.--csaf-publisher-category.
The namespace must be a normalized absolute HTTP or HTTPS URL controlled by the publisher. Supply an ASCII RFC 3986 URI. Encode an internationalized host with IDNA and percent-encode non-ASCII path characters. Malformed escapes and raw Unicode are rejected because they would fail CSAF schema validation.
The document ID cannot contain a line terminator. The category does not accept
translator, because Vexcalibur produces a new document rather than
translating an existing CSAF advisory.
--csaf-document-status defaults to draft and accepts draft, final, or
interim. --csaf-version has a semantic default of 2.0 and rejects other
versions. Every CSAF option is rejected for CycloneDX or OpenVEX output.
OpenVEX’s --author and --author-role are rejected for CSAF.
The CLI validates format metadata before fetching a GitHub SBOM or querying a finding service.
Product identity¶
Only products referenced by assertions appear in the output. Each distinct
canonical product package URL becomes one full_product_names entry with:
a human-readable name that includes the version.
a stable
CSAFPID-<uuid>derived from the canonical versioned package URL.product_identification_helper.purlset to that URL.
When a component has an unversioned package URL and a separate version, Vexcalibur adds the known version. It rejects an assertion when both fields are unversioned because that identity could apply to every version of the package.
Identical products collapse into one entry. Duplicate component references, unknown references, and finding package URLs that disagree with their component are rejected before rendering. One vulnerability and product may have only one effective CSAF status. Same-status provenance and evidence may be grouped, but contradictory statuses for that pair are a conflict.
The renderer deliberately uses a flat product tree. It does not emit product branches, groups, relationships, ranges, or a complete SBOM hierarchy.
Vulnerability identity and provenance¶
Assertions are grouped into one vulnerability object per vulnerability ID.
An ID matching ^CVE-[0-9]{4}-[0-9]{4,}$ appears in cve. Every other ID
appears in ids with its source name.
Distinct source URLs become external references. They must use the same ASCII
RFC 3986 URI encoding as the publisher namespace. Each vulnerability has at
least one details note. Notes preserve the applicable CSAF product IDs,
analysis detail, original Vexcalibur state, source, source-record modification
time, and confirmed fixed version when present.
CSAF 2.0 cannot bind vulnerability notes structurally to products. Vexcalibur therefore includes the applicable product IDs in note text. Product-scoped notes from CSAF 2.1 are outside this renderer’s contract.
A finding’s modified value describes an upstream source record. It stays in
notes and never becomes a CSAF document release, revision, or generator date.
State and evidence mapping¶
Vexcalibur state |
CSAF product status |
Required evidence |
Emitted structure |
|---|---|---|---|
|
|
|
Product ID in |
|
|
|
Product ID in |
|
|
None |
Product ID in |
|
|
|
Product ID in |
|
|
|
Product ID in |
The false_positive mapping is lossy because CSAF 2.0 has no separate status
for it. Keeping the original state and product IDs in notes lets a consumer see
why the broad status was selected.
An affected remediation uses the supplied action as details, the supplied
remediation category as category, and all covered products in product_ids.
Accepted categories are mitigation, no_fix_planned, none_available,
vendor_fix, and workaround.
An unaffected impact uses the supplied impact statement as a threat with
category impact and its covered products in product_ids.
Vexcalibur groups identical action or impact evidence across products. It does not infer a category, action, impact, or machine-readable status from prose. Every affected or unaffected product remains explicitly covered by its evidence object.
Filename contract¶
For direct file output, the basename is derived from
document.tracking.id:
Lowercase the ID.
Replace every run matching
[^+\-a-z0-9]+with one underscore. This includes existing underscores.Append
.json.
ACME VEX:2026/001 therefore requires acme_vex_2026_001.json. Directories
do not affect the comparison. The rule does not apply to standard output.
The CSAF document validator receives parsed JSON and cannot inspect an external filename, so the repository tests this rule separately.
Validation¶
The repository vendors the immutable OASIS Draft 2020-12 document schema from
the csaf-2.0-os tag at commit
a0b55d3b8a51f8e3d1ec94f03df3d48edf11c828. Its SHA-256 is
29c114b35b0a30831f1674f2ab8b3ed9b2890cfeaa63b924ac6ed9d70ef44262.
Approved Errata 01 uses a byte-identical document schema.
Schema validation is necessary but not sufficient. CSAF also defines
mandatory semantic tests. CI runs the golden and installed-wheel output
through @secvisogram/csaf-validator-lib
2.0.27
on Node 24. The pinned suite
executes 42 separately exported mandatory tests plus strict schema validation;
mandatory test 6.1.8 is covered by that schema test.
The validator release commit is
db0999f174b69e5857cef1434e1cbdf83a759b69. The committed npm lockfile records
integrity
sha512-QqpVNUs42BbgSR4k9cRIvOx33CX8cg5CuY8FpBwBKsimlz5aHL8m6Zc2SZ0mXSinBNqvAYD/pLZR6AjVFV9TwA==.
CI installs that lock with npm ci --ignore-scripts; it does not resolve a
mutable upstream branch.
The strict suite catches conditions that the official schema alone accepts, including unknown properties, contradictory product statuses, an affected product without a scoped remediation, and an unaffected product without a scoped impact threat.
The Node validator is a test dependency, not a Python runtime dependency. Vexcalibur enforces its renderer contract before serialization, while the two independent validation layers guard the committed output and packaged CLI.
Validation errors¶
Rendering stops with a format error when:
required document or publisher metadata is empty or invalid.
the publisher category or document status is unsupported.
a finding list is empty.
a finding references an unknown component or mismatched package URL.
an emitted product lacks a precise version.
a required action, impact, category, or fixed version is missing.
an evidence field appears on a state where it is not valid.
a fixed version differs from the emitted product version.
one vulnerability and product have contradictory effective statuses.
component references are duplicated.
direct output uses a basename that does not match the tracking ID.
Scope¶
CSAF support is output-only and creates an initial csaf_vex document. It
does not read CSAF, convert between VEX formats, model later revisions, produce
trusted-provider metadata, add distribution policy or TLP, sign documents, or
publish them.