Skip to content

v0.9.3 — Supply-chain attestation — Sigstore + SBOMs + PEP 740

Choose a tag to compare

@Jo-Jo98 Jo-Jo98 released this 29 Apr 20:10
· 73 commits to main since this release

Supply-chain attestation — closes issue #14. From this release onwards every container image is Sigstore-signed (keyless) and carries CycloneDX + SPDX SBOM attestations; every PyPI distribution carries PEP 740 attestations. No long-lived signing keys; no separate key-management surface.

Note on v0.9.2 — TAGGED BUT NEVER PUBLISHED. v0.9.2 was the first attempt at this slice and shipped only partially: the cosign sign step succeeded (the GHCR image at the v0.9.2 SHA digest was correctly signed), but the SBOM-attestation step failed because mkdir -p sbom was missing from the new attest job. Concurrently, the PyPI publish job stalled on the pypi GitHub environment's required_reviewers rule (auto-cleared on previous releases for unclear reasons; this run actually blocked). Tag deleted from remote + local; v0.9.3 is the first successful release of this slice. Same pattern as v0.8.0 → v0.8.1.

Added

  • Sigstore keyless image signing. New attest job in release.yml runs cosign against the multi-arch GHCR image after publish. Signs by digest (immutable bytes) rather than tag (mutable) — a re-pushed :vX.Y.Z cannot replay a previous signature. Identity = the workflow that ran (Jo-Jo98/ciguard/.github/workflows/release.yml@refs/tags/v0.9.2); short-lived cert issued by Sigstore Fulcio over GitHub Actions OIDC; signature + cert recorded in Sigstore's Rekor public transparency log.
  • CycloneDX + SPDX SBOMs as cosign attestations on the image. Generated by syft (via anchore/sbom-action) against the actual built layers — high-fidelity package / version / license manifests. Both formats because consumers vary: CycloneDX is what most SAST / SCA tooling speaks; SPDX is what regulators (US EO 14028, ISO/IEC 5962) ask for. Producing both costs ~10 s and avoids future format conversion.
  • Python-package CycloneDX SBOM (via CycloneDX/gh-python-generate-sbom) generated from requirements.txt during the PyPI publish job. Uploaded as a workflow artifact (sbom-python-cyclonedx, 90-day retention).
  • PEP 740 PyPI attestations. Bumped pypa/gh-action-pypi-publish to v1.14.0 — every wheel + sdist now ships a Sigstore-signed PEP 740 attestation, visible at https://pypi.org/project/ciguard/#files. Verifies the distribution was built by this workflow, not republished by a compromised maintainer account.
  • SLSA build provenance via docker/build-push-action's provenance: true + sbom: true parameters. Adds in-toto SLSA provenance + a buildkit-generated SBOM as image attestations alongside the cosign signature — defence-in-depth from a different angle (build-tool-attested vs. workflow-attested).
  • README "Verifying releases (Sigstore + SBOMs)" section — copy-paste recipe for cosign verify + cosign verify-attestation (CycloneDX + SPDX), plus what each layer protects against. The 3-line cosign verify command is the headline: anyone consuming ciguard can prove cryptographically that the image they pulled was built by this workflow at this tag.

Internals

  • New attest job depends on ghcr (image build) + reads its digest output; trivy continues to depend on ghcr only (independent — runs in parallel with attest). Adds ~2 min to the release flow; releases are rare; the cost is justified by the verifiability gain.
  • The pypi job's SBOM is written to sbom/ (not dist/) — anything in dist/ gets uploaded by gh-action-pypi-publish as a release distribution and would break PyPI publishing on first run.

Why this matters strategically

ciguard's narrative is "static security auditor for CI/CD pipelines." From this release, ciguard signs and SBOMs its own releases — and a future ciguard rule will flag pipelines pushing to a registry without doing the same (issue #14's optional follow-up: "eats own dogfood"). Worth its own ship event + blog post.