Generate VEX from Python

Use vexcalibur.api when an application needs to generate VEX without invoking the command-line interface. This example stays offline: it reads a CycloneDX SBOM and reviewed findings from the Vexcalibur test fixtures.

Prepare the checkout

Run the example from a Vexcalibur source checkout. Install the locked project environment first:

uv sync --frozen

The example uses only the supported facade. It catches the documented input, source, and rendering errors before it writes output.

 1"""Generate and verify a VEX document through the supported Python API."""
 2
 3from __future__ import annotations
 4
 5import json
 6from pathlib import Path
 7
 8from vexcalibur.api import (
 9    LocalFindingsError,
10    SbomError,
11    VexRenderError,
12    generate_vex_from_local_findings,
13)
14
15REPOSITORY_ROOT = Path(__file__).resolve().parents[2]
16
17
18def main(output_path: Path = Path("build/vexcalibur-python-api.json")) -> None:
19    """Generate the example document and fail when its result is incomplete."""
20    try:
21        document = generate_vex_from_local_findings(
22            input_file=REPOSITORY_ROOT / "tests/fixtures/sbom/cyclonedx-json-simple.json",
23            findings_file=REPOSITORY_ROOT / "tests/fixtures/findings/all-analysis-states.json",
24        )
25    except (LocalFindingsError, SbomError, VexRenderError) as exc:
26        raise SystemExit(f"VEX generation failed: {exc}") from exc
27
28    parsed = json.loads(document)
29    vulnerabilities = parsed.get("vulnerabilities")
30    if not isinstance(vulnerabilities, list) or len(vulnerabilities) != 5:
31        raise SystemExit("VEX generation returned an unexpected finding count")
32
33    try:
34        output_path.parent.mkdir(parents=True, exist_ok=True)
35        output_path.write_text(document, encoding="utf-8")
36    except OSError as exc:
37        raise SystemExit(f"Could not write {output_path}: {exc}") from exc
38
39    print(f"Wrote {len(vulnerabilities)} findings to {output_path}")
40
41
42if __name__ == "__main__":
43    main()

Generate and verify the document

Run the checked-in example:

uv run --frozen python docs/examples/use_python_api.py

A successful run prints:

Wrote 5 findings to build/vexcalibur-python-api.json

Confirm that the output is CycloneDX 1.6 JSON with five vulnerability entries:

uv run --frozen python - <<'PY'
import json
from pathlib import Path

document = json.loads(
    Path("build/vexcalibur-python-api.json").read_text(encoding="utf-8")
)
assert document["bomFormat"] == "CycloneDX"
assert document["specVersion"] == "1.6"
assert len(document["vulnerabilities"]) == 5
print("CycloneDX 1.6 VEX contains 5 findings")
PY

Use application paths in place of the two fixture paths. Keep imports under vexcalibur.api; implementation modules can change outside the 1.x compatibility contract.

To query an OSV-compatible service instead of reviewed local findings, use generate_vex_from_sbom. Public OSV remains blocked until the call includes allow_public_osv=True. See Use a private OSV mirror before sending a private inventory to any service.

When the embedding also needs counts and a digest for the exact rendered bytes, follow Generate an execution report from Python.