fix(engine): verify SLSA build provenance via sigstore-verify (ADR-0020) - #339
Merged
thejefflarson merged 2 commits intoAug 26, 2026
Merged
Conversation
The dashboard provenance column was blank on every row: the observer resolved Absent for every image, including protector's own signed+attested images. The feature had been inert since it shipped. Root cause is two independent dead ends in the sigstore crate (0.14, newest): its trusted_signature_layers hardcodes the cosign sign/v1 predicate and drops every SLSA attestation, and its bundle::verify::Verifier recomputes the Rekor DSSE envelopeHash from a proto round-trip that never matches what Rekor stored (offline always fails; online is unimplemented). Both confirmed against the live agent image; the primitives to hand-roll around them are pub(crate). Add the sigstore-verify crate (prefix-dev) for the provenance verify step only — it handles GitHub artifact attestation correctly and ships a built-in trust root. A new provenance_observer module fetches the image's OCI referrers directly, selects the slsa.dev/provenance bundle, verifies it offline against the same registry (no new egress), and feeds the unchanged classify_provenance pipeline. A registry without the referrers API resolves to Absent, not perpetual Checking. Validated live: protector-agent -> Verified (correct source + builder); redis / ollama / argocd -> Absent. Adds unit tests plus an ignored live integration test guarding the exact green-on-fixtures / dead-in-prod failure mode. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ries The audit gate flagged two newly-published advisories in pre-existing transitive deps: h2 (RUSTSEC-2026-0258, unbounded empty DATA frames) and webbrowser (RUSTSEC-2026-0257, Unix BROWSER argument injection). Both are lock-only, semver-compatible bumps. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
thejefflarson
enabled auto-merge (squash)
August 26, 2026 02:55
thejefflarson
deleted the
thejefflarson/provenance-verify-sigstore-verify
branch
August 26, 2026 03:02
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What was wrong
The dashboard's build-provenance column was blank on every row — the observer resolved
Absentfor every image, including protector's own signed+attested images. The provenance feature (ADR-0020 §5) had been inert since it shipped.Root cause — two independent dead ends in the
sigstorecrate (0.14, newest release)trusted_signature_layersfetches the SLSA bundle as an OCI referrer, thenfrom_sigstore_bundlerejects it — it hardcodes the cosignsign/v1predicate and errors onhttps://slsa.dev/provenance/v1. So the shared signing fetch can never surface a SLSA layer.bundle::verify::Verifier, recomputes the Rekor DSSEenvelopeHashfrom a proto round-trip that never equals the hash Rekor stored (a canonicalization bug) — offline DSSE always fails; the online path is an unimplemented stub.Both confirmed against the live agent image (
envelopeHash mismatch). No publishedsigstoreversion fixes either, and the Fulcio-chain / SCT primitives needed to hand-roll around them arepub(crate).Fix
Add
sigstore-verify(prefix-dev) for the provenance verify step only — a maintained verifier purpose-built for GitHub artifact attestation that handles the DSSE Rekor-entry consistency correctly and ships a built-in production trust root. Signing stays on thesigstorecrate, untouched.New
provenance_observermodule: fetch the image's OCI referrers directly, select theslsa.dev/provenancebundle, verify it offline against the same registry (no new egress destination, no transparency-log call), and feed the verified facts into the unchangedclassify_provenance/parse_slsa_predicatepipeline — so baseline, drift, and render are all untouched; only the source of the facts moved.Absent, notChecking(the manifest fetch already proved reachability), so mirrored base images stay calm.PROTECTOR_*flag — detection stays on by default.Validated live
protector-agent:0.14.1→Verified(source: github.com/thejefflarson/protector, builder: …/agent.yml@refs/tags/v0.14.1)redis(mirror),ollama,argocd→AbsentTests & gates
Full engine suite (1233) green · clippy clean (warnings-as-errors) · fmt clean · self-containment guard passes · files under the 1000-line cap. Adds 7 unit tests + 2
#[ignore]d live integration tests guarding the exact green-on-fixtures / dead-in-prod failure mode. ADR-0020 amended with the decision (and its two now-stale notes corrected).ADR-0042 note:
provenance_facts/predicate_from_paewere removed fromcosign.rs, butclassify_provenance/parse_slsa_predicategainedprovenance_observeras their live consumer — no shared layer dropped to zero.🤖 Generated with Claude Code