Generate an execution report from Python

Use the report-aware Python API when an embedding needs the same versioned summary on Windows, Linux, or macOS. The Python API constructs and validates the report, but your application owns file publication.

Prepare the checkout

The checked-in example uses local test fixtures and makes no network requests. Run it from the repository root after installing the locked environment:

uv sync --frozen

The example imports only from vexcalibur.api. It handles SBOM, findings, rendering, package-metadata, and report-parse failures separately.

 1#!/usr/bin/env python3
 2"""Generate matching VEX and execution-report bytes with the Python API."""
 3
 4import argparse
 5import os
 6from pathlib import Path
 7
 8from vexcalibur.api import (
 9    GenerationExecutionReportParseError,
10    GenerationReportMetadataError,
11    LocalFindingsError,
12    SbomError,
13    VexRenderError,
14    generate_vex_from_local_findings_result,
15    parse_generation_execution_report,
16)
17
18
19def _write_new_file_exclusively(path: Path, content: bytes) -> None:
20    flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL
21    flags |= getattr(os, "O_BINARY", 0)
22    flags |= getattr(os, "O_CLOEXEC", 0)
23    flags |= getattr(os, "O_NOFOLLOW", 0)
24    descriptor = os.open(path, flags, 0o600)
25    try:
26        if os.name != "nt":
27            os.fchmod(descriptor, 0o600)
28        with os.fdopen(descriptor, "wb", closefd=False) as stream:
29            stream.write(content)
30            stream.flush()
31            os.fsync(stream.fileno())
32    finally:
33        os.close(descriptor)
34
35
36def main(output_directory: Path) -> None:
37    try:
38        result = generate_vex_from_local_findings_result(
39            input_file=Path("tests/fixtures/sbom/cyclonedx-json-simple.json"),
40            findings_file=Path("tests/fixtures/findings/all-analysis-states.json"),
41        )
42    except GenerationReportMetadataError as exc:
43        raise SystemExit(f"package metadata cannot identify the report: {exc}") from exc
44    except (SbomError, LocalFindingsError, VexRenderError) as exc:
45        raise SystemExit(f"generation failed: {exc}") from exc
46
47    try:
48        report = result.execution_report()
49        serialized_report = report.to_json()
50        if parse_generation_execution_report(serialized_report) != report:
51            raise SystemExit("execution report did not round-trip through its parser")
52    except GenerationReportMetadataError as exc:
53        raise SystemExit(f"package metadata cannot identify the report: {exc}") from exc
54    except GenerationExecutionReportParseError as exc:
55        raise SystemExit(f"generated execution report is invalid: {exc}") from exc
56    except ValueError as exc:
57        raise SystemExit(f"generation facts cannot produce a report: {exc}") from exc
58
59    if not output_directory.parent.is_dir():
60        raise SystemExit("output parent directory must already exist")
61    output_directory.mkdir(mode=0o700)
62    if os.name != "nt":
63        output_directory.chmod(0o700)
64    document_path = output_directory / "vex.json"
65    report_path = output_directory / "execution-report.json"
66    _write_new_file_exclusively(document_path, result.rendered_bytes)
67    _write_new_file_exclusively(
68        report_path,
69        serialized_report.encode("utf-8"),
70    )
71    print(f"wrote {document_path} and {report_path}")
72
73
74def _output_directory() -> Path:
75    parser = argparse.ArgumentParser()
76    parser.add_argument("output_directory", type=Path)
77    return parser.parse_args().output_directory
78
79
80if __name__ == "__main__":
81    main(_output_directory())

Generate and validate both files

Choose a new child of the repository root so the parent already exists. The example refuses to replace the directory or either output file.

On POSIX, the example creates the directory with mode 0700 and each file with mode 0600. On Windows, Python inherits access control lists (ACLs) from the parent; the example does not make an existing parent private. Run it only from a directory whose ACL already restricts access to the intended user.

The two final-path writes are independent and are not atomic. A write or fsync failure can leave a partial VEX file or execution report. Use the CLI instead when a POSIX embedding needs coordinated publication.

Run this one-line command from Bash or PowerShell:

uv run --frozen python docs/examples/generate_execution_report.py vexcalibur-python-report

A successful run prints both paths:

wrote vexcalibur-python-report/vex.json and vexcalibur-python-report/execution-report.json

The example parses the serialized report through parse_generation_execution_report() before either file is written. A parse failure stops the run.

Use result.rendered_bytes for the VEX file and serialize the report from the same GenerationResult. Don’t render again or calculate counts from the VEX document; either step can make the report describe different bytes.

Handle publication in the embedding

The example uses exclusive creation and refuses to replace existing paths. On Windows, privacy depends on the parent ACL described above. On every platform, a failed direct write can leave a partial file, and a report failure can leave the VEX file behind.

Applications that need coordinated replacement on Linux or macOS should invoke the CLI with --execution-report. The CLI publishes VEX first and the report last under its destination locks. The supported Python facade does not expose that filesystem transaction.

Catch GenerationReportMetadataError when installed package metadata cannot identify the loaded code. Catch GenerationExecutionReportParseError when validating report bytes received from another process. The Python API reference lists the complete report types and failure contracts; the execution report reference defines the JSON fields and security boundary.