verify: accept GitHub release immutability + Sigstore attestation as runtime-download trust anchor#887
Open
potiuk wants to merge 2 commits into
Open
verify: accept GitHub release immutability + Sigstore attestation as runtime-download trust anchor#887potiuk wants to merge 2 commits into
potiuk wants to merge 2 commits into
Conversation
…runtime-download trust anchor
When an action downloads a binary at runtime via tc.downloadTool /
fetch / etc. without an in-source checksum check, the verify
pipeline currently fails with "unverified binary download(s) detected
(no checksum/signature check in file)". For some upstreams that
finding is intractable on our side: the action's design delegates
trust to the publishing process, not to an inline check.
This patch adds a declarative per-action escape hatch. An entry in
the new TRUSTED_DOWNLOAD_PROVENANCE dict asserts that the action's
runtime downloads are anchored at the GitHub / Sigstore layer: the
configured release_repo must publish immutable releases AND emit
Sigstore attestations via actions/attest-build-provenance. Both
halves are verified at scan time before any failure is reclassified
— the config entry alone is not enough.
The runtime check:
1. GET releases/latest of the configured release_repo; confirm
release.immutable is true.
2. Download one small SLSA-attested asset (preferring .sbom.json
which actions/attest emits alongside binaries, ~340KB) and run
`gh attestation verify` against it (reuses the existing
_verify_via_gh_attestation helper used by the in-tree-binary
check).
On pass, all unverified-download failures for the action are
reclassified as warnings with the rationale appended. On fail (when
the config exists but the runtime check doesn't confirm both halves),
failures stay failures and the reason is printed so the auditor
sees why the escape hatch didn't apply.
Bootstrap entry: golangci/golangci-lint-action, with rationale
linking golangci/golangci-lint-action#1396
where the maintainer confirmed golangci-lint releases since v2.12.2
are immutable + actions/attest-signed, and the action itself is
immutable since v9.2.1.
Generated-by: Claude Code (Claude Opus 4.7)
This was referenced May 24, 2026
Companion to the previous commit that introduced
TRUSTED_DOWNLOAD_PROVENANCE for actions whose runtime downloads are
anchored at the GitHub release / Sigstore layer rather than via an
in-source checksum.
README: add a paragraph after the "Pre-compiled native binaries
shipped in-tree" bullet explaining the escape hatch — what an entry
asserts, what the runtime check confirms (immutable release + valid
Sigstore attestation on an attested asset), and that the config alone
is not enough.
Tests:
* TestVerifyTrustedDownloadProvenance — happy path, missing config
(returns no-opinion), network failure on release metadata, non-
immutable release, no asset downloadable, `gh attestation verify`
failure.
* TestFetchReleaseAssetBytes — asset-preference ordering picks the
cheapest valid probe (.sbom.json over .tar.gz); smallest-asset
fallback when no preference matches; empty assets returns (None,
None).
* TestAnalyzeBinaryDownloadsTrustedDownloadEscapeHatch — the call-
site branch: passing provenance reclassifies failures as warnings;
failing provenance preserves failures; no config entry skips the
branch entirely (verify_trusted_download_provenance never called).
12 new tests; full test_security.py (144 tests) passes.
Generated-by: Claude Code (Claude Opus 4.7)
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.
Summary
TRUSTED_DOWNLOAD_PROVENANCEdeclarative config +verify_trusted_download_provenance()toutils/verify_action_build/security.py. When an action downloads binaries at runtime without an in-source checksum check, an entry in the config asserts the trust anchor is at GitHub/Sigstore level (immutable release + SLSA attestation).gh attestation verifyagainst a small SLSA-attested asset, typically a.sbom.jsonfromactions/attest-build-provenance). Config entry alone is not enough._verify_via_gh_attestation()helper that backs the in-tree-binary check.golangci/golangci-lint-actionper maintainer @ldez's confirmation in installBin: download golangci-lint release binary without checksum/signature verification golangci/golangci-lint-action#1396 (releases since v2.12.2 immutable + SLSA-attested; action itself immutable since v9.2.1).Why
golangci-lint-action's
installBin()downloads thegolangci-lintbinary viatc.downloadTool(assetURL)with no in-source checksum — the verify pipeline correctly flagged this on every recent dependabot bump. The maintainer noted the trust anchor is GitHub release immutability + Sigstore attestation, and pointed out that an in-source checksum re-derived from the same publishing source adds no meaningful layer. This patch teaches the verify pipeline to actually verify that alternative trust anchor at scan time, rather than just suppressing the finding.Behaviour
Test plan
verify_trusted_download_provenance('golangci', 'golangci-lint-action')→passed=True, reason mentions immutable + attestedverify_trusted_download_provenance('some', 'other-action')→passed=False, empty reasonanalyze_binary_downloads('golangci', 'golangci-lint-action', '<v9.2.1 SHA>')returns(1 warning, 0 failures)— previously-failingtc.downloadToolfinding reclassifiedanalyze_binary_downloads('opentofu', 'setup-opentofu', '<v2.0.1 SHA>')still returns(0 warnings, 1 failure)— unchanged for actions without trust configverifycheck passes cleanlyOut of scope
opentofu/setup-opentofu: releases are not GitHub-immutable yet and use cosign keyless signatures rather than
actions/attest— different verification primitive (cosign verify-blob with OIDC identity regex). The upstream fix is opentofu/setup-opentofu#117. A cosign-based path here is possible but adds a CLI dependency and is deferred.🤖 Generated with Claude Code