"""The CycloneDX SBOM of an image, as Trivy reads it. bin/sign.sh attaches it to each published platform image as an attestation, next to the CIS result, so what base-os holds can be read from the registry. python -m image_tools.sbom Standard library only; Trivy runs as the container image_tools/verify.py pins. """ from __future__ import annotations import argparse import json import subprocess from pathlib import Path from image_tools.verify import TIMEOUT, TRIVY_IMAGE def cyclonedx(ref: str) -> dict: # pragma: no cover - container seam proc = subprocess.run( [ "docker", "run", "--rm", "-v", "/var/run/docker.sock:/var/run/docker.sock", "-e", "TRIVY_USERNAME", "-e", "TRIVY_PASSWORD", TRIVY_IMAGE, "image", "--quiet", "--disable-telemetry", "--skip-version-check", "--format", "cyclonedx", ref, ], capture_output=True, text=True, timeout=TIMEOUT, check=True, ) return json.loads(proc.stdout) def main() -> int: # pragma: no cover - CLI seam parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("ref") parser.add_argument("out") args = parser.parse_args() Path(args.out).write_text(json.dumps(cyclonedx(args.ref), indent=2)) return 0 if __name__ == "__main__": # pragma: no cover raise SystemExit(main())