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.