Repository navigation
Phase C: evidence readers and stricter verification CI (in progress) - #77
Merged
Merged
Conversation
Each runs as its own job the PR Gate Summary requires: go test -race (Build verifies images concurrently), staticcheck v0.8.2, govulncheck v1.9.0, and zizmor 1.31.1 over the workflows and the action. The findings zizmor reported when it was added are kept in .github/zizmor.yml, each with its reason; the three pr-gate.yml checkouts it flagged now set persist-credentials: false instead. make analysis runs the four locally. A test now covers evidence discovery through the referrers tag (sha256-<hex>, what cosign and oras write on registries without the Referrers API): found and verified like referrers, without asking the API; a tag that can't be read (429, 500, 401) or holds an image instead of an index leaves evidence unknown, never missing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A cosign vuln attestation holding Trivy's report (trivy image --format cosign-vuln) verified, but its findings weren't read, so the image's vulnerabilities were unknown. They are now read like grype's, from verified attestations only: converted to matches, so they are counted, merged across platforms, and gated on by failOn and maxScanAgeDays, with vulnerabilities.scanner "trivy <version>". It is read only when the predicate's scanner.uri names Trivy and its result is Trivy's JSON report of schema version 2 with a Results list. A report without Results (what Trivy writes when it found nothing it could scan) stays unknown, never clean, as does an unnamed scanner, grype's shape under Trivy's URI, a finding with no ID or package, or another schema version. Every finding counts, whatever its Status. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…bom tags A team with existing evidence saw it as missing: estate verify read only cosign's bundles and tags and unsigned SBOM referrers. It now also finds: - Notation signatures: referrers of artifact type application/vnd.cncf.notary.signature (the OCI 1.1 form, or the config media type), source notation-signature, with the x509.subject the JWS envelope's certificate claims as the signer; - BuildKit's attestations in the image index (docker buildx build --sbom --provenance): the SPDX SBOM and SLSA provenance in each platform's attestation manifest, source buildx-attestation; - the SBOM in cosign's sha256-<digest>.sbom tag (cosign attach sbom), source cosign-sbom-tag. None is verified: each is present, nothing in it is read, none is handed to cosign, and none crowds out evidence cosign verified. Notation signatures aren't checked because no ClearCutt trust policy can name a Notation signer yet (the trust policy is shared with clearcutt-factory). A platform image with no SBOM or scan of its own is now judged by any attached to the image index (present: such a copy doesn't say which platform it describes), and is unknown rather than missing when the index's evidence, or BuildKit's attestation manifests, can't be read. Fixtures are built by hand from each format's specification. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
govulncheck's first CI run found GO-2026-6809 (crypto/x509, fixed in Go 1.26.10) in the standard library of the Go 1.26.9 the runners had cached: setup-go took the cached 1.26 patch release, for the CI jobs and for the release build alike. cli/go.mod now names toolchain go1.26.10, which setup-go (go-version-file) installs for every job, the release workflow, and the action's source build; the minimum Go stays 1.26. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A policy's vex: document in OpenVEX 0.0.1 was refused (its vulnerability is a string, which didn't unmarshal), while clearcutt-factory converts and applies it, so one file meant different things to the two tools. Statements are now converted on reading the way factory converts them: a string vulnerability is its name, and the subcomponents a 0.0.1 statement names beside its products are those of each product that names none of its own. The scoping rule is unchanged: subcomponents none of which can be read never widen a statement to every package, and grype is handed v0.2.0 statements. A test checks a 0.0.1 document scopes exactly like its v0.2.0 equivalent. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
northcutted
marked this pull request as draft
October 27, 2026 03:13
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.
Adds support for existing image evidence and makes estate verification easier to bootstrap and run: explicit trust, bounded external operations, progress, and an opt-in verification cache. Unknown or unsupported evidence never becomes a pass.
policy init (--refs FILE or --images FILE) --out DIRwrites a starter policy and inactive trust skeleton together. Candidates are unverified comments, never active trust. Incomplete discovery aborts.--verbose;--quietoverrides it.--registry-timeout 1m,--cosign-timeout 2m; registry scan--github-timeout 30s; estate verify--reproduce-timeout 30m,--grype-timeout 10m,--notation-timeout 2m.estate verify --cache-dir DIRreuses only successful Cosign results bound to pinned evidence/trust bytes and executable identities. Parser, policy, age checks, rescans and rebuilds stay fresh.notation: {trustedIdentities, trustRoots}verifies X.509 image signatures using--notation-path(default notation), Notation 1.3.2+ in the 1.3 series, and its experimental OCI-layout path. Each signature is pinned and checked under a generated strict, isolated policy with no plugins.metadata.vex.suppressedattributes exact existing ignored matches without suppressing anything new or changing counts/rescans.The only new estate-report field is optional
policy.notation: {trustedIdentities: string[], trustRoots: string[]}. Roots are resolved paths, not embedded certificates. Portal needs the matching additive schema update; northcutted/clearcutt-portal#13 handles trust display and recheck commands (246 local tests passed; optional scale gate runs in CI). JWS signer subjects are reported only for the verified envelope; COSE can have no parsed signer. Factory VEX attribution is the publisher's signed assertion, not independent verification of its VEX document.Cache scope is deliberately bounded to supported Cosign DSSE/offline-key paths. Unsupported or ambiguous paths verify live. Notation expiry/revocation is always evaluated live; missing/unsupported CLI remains present, and opaque failures/timeouts remain unknown. Use
--fail-on unverifiedto reject them. No new dependencies. Cancellation does not establish that a remote BuildKit task stopped.Validation: pinned Go 1.26.10; final vet/build/coverage (88.8%), race, staticcheck, govulncheck, docs/workflow guards, offline demo and actual Cosign/Notation local-registry integration. No public image or registry state was changed by these local checks. Fresh PR CI must pass before merge; the prior green head was 6e20431.
All approved Verify Phase C implementation items are complete. Merge waits for fresh all-job CI and the Portal compatibility update. Factory Phase C and the second automated showcase drill remain separate follow-up work.