Skip to content

ci: sign releases with cosign and generate SLSA provenance#222

Merged
bomly-guy merged 3 commits into
mainfrom
ci/signed-releases-slsa-provenance
Jun 30, 2026
Merged

ci: sign releases with cosign and generate SLSA provenance#222
bomly-guy merged 3 commits into
mainfrom
ci/signed-releases-slsa-provenance

Conversation

@bomly-guy

Copy link
Copy Markdown
Collaborator

Summary

Addresses the Scorecard Signed-Releases check (currently 0/10). Scorecard scans the last five releases' assets for specific signature/provenance filename patterns — it doesn't verify trust chains itself, just presence — so this adds both tiers:

  • 8/10 tier.goreleaser.yaml gets a signs: block that signs SHA256SUMS keylessly with cosign via GitHub OIDC (no managed keys, no secrets to rotate), producing SHA256SUMS.sigstore.json.
  • 10/10 tierrelease.yml adds a provenance job calling slsa-framework/slsa-github-generator's generic builder over the release artifact hashes, producing a SLSA Build Level 3 multiple.intoto.jsonl attestation, auto-uploaded to the same GitHub release.

A note on Pinned-Dependencies

The SLSA generator's uses: line must stay pinned to a v2.1.0 tag, not a commit SHA — slsa-verifier resolves the trusted builder's identity from that exact tag ref, and SHA-pinning it breaks that resolution (this is the generator's own documented requirement, not a choice we're making). This is a known, narrow exception to the repo's usual SHA-pinning convention from #217/#220. Added .github/pinact.yaml with an ignore_actions rule so a future pinact run doesn't "fix" this into a SHA and silently break provenance verification. Scorecard's Pinned-Dependencies check may ding this one line slightly, but the net Scorecard score moves up substantially once Signed-Releases jumps from 0 to ~10.

Verification

  • goreleaser check accepts the new signs: config (only pre-existing, unrelated brews deprecation warning remains — already tracked in a comment in .goreleaser.yaml).
  • goreleaser release --snapshot --clean --skip=sign,publish,announce run locally — full build/archive/checksum pipeline still produces all platform archives, Linux packages, and SHA256SUMS correctly; confirms my changes don't break the build.
  • Confirmed slsa-framework/slsa-github-generator@v2.1.0 is the current stable release and sigstore/cosign-installer@v4.1.2's SHA was resolved directly from GitHub's API.
  • make test passes.

What I could not test locally: cosign's keyless OIDC signing and the SLSA generator's provenance generation both require a real GitHub Actions OIDC token — neither can run outside Actions. The actual signing + provenance flow needs to be verified on the next real tagged release (or via a workflow_dispatch run against a tagged commit).

Test plan

  • goreleaser check / snapshot build pass locally
  • make test passes
  • Next tagged release: confirm SHA256SUMS.sigstore.json and multiple.intoto.jsonl appear as release assets
  • Next tagged release: cosign verify-blob and slsa-verifier verify-artifact (commands now documented in docs/INSTALLATION.md) succeed against the published assets
  • Next weekly Scorecard run shows Signed-Releases improved from 0/10

🤖 Generated with Claude Code

bomly-guy and others added 2 commits June 29, 2026 23:52
The Scorecard report still flagged the inline `pip install --upgrade
pip pipenv poetry` in smoke.yml and update-smoke-goldens.yml as
unpinned. Replace it with a hash-locked, fully-resolved requirements
file (pip-compile --generate-hashes) installed via `pip install
--require-hashes`, covering pip/pipenv/poetry and their full
transitive dependency graph (45 packages). Verified the lockfile
installs cleanly under Python 3.12 with --require-hashes and that the
resolved wheels include manylinux builds for the Linux CI runner.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Addresses the Scorecard Signed-Releases check, which scans GitHub
release assets for signature/provenance file patterns and currently
scores 0/10.

- .goreleaser.yaml: add a `signs:` block that signs SHA256SUMS
  keylessly with cosign (GitHub OIDC identity, no managed keys),
  producing SHA256SUMS.sigstore.json. Satisfies the check's 8/10 tier.
- release.yml: install cosign before the GoReleaser step, grant
  `id-token: write` for keyless signing, and add a `provenance` job
  that calls slsa-framework/slsa-github-generator's generic builder
  over the release artifact hashes to produce a SLSA Build Level 3
  `multiple.intoto.jsonl`, uploaded to the same release. Satisfies the
  check's 10/10 tier.
- .github/pinact.yaml: exclude the SLSA generator's `uses:` line from
  automated re-pinning. It must stay pinned to a vX.Y.Z tag (not a
  commit SHA) for slsa-verifier's builder-identity check to resolve —
  SHA-pinning it would break verification. This is a known, intentional
  exception to repo's usual SHA-pinning convention.
- docs/INSTALLATION.md: add cosign and slsa-verifier commands so users
  can verify a release's signature and provenance, not just checksums.
- dev-docs/CI.md: document the new release-pipeline steps and the
  pinning exception.

Validated locally: `goreleaser check` accepts the signs config, and a
`goreleaser release --snapshot --clean --skip=sign,publish,announce`
run confirms the build/archive/checksum pipeline is unaffected. The
cosign OIDC signing and SLSA provenance steps themselves only run in
GitHub Actions and need verification on the next real tagged release.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@bomly-guy, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 14 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: ba4fed13-a050-47db-910e-e186ad692e6e

📥 Commits

Reviewing files that changed from the base of the PR and between 62d961b and 8884f47.

📒 Files selected for processing (5)
  • .github/pinact.yaml
  • .github/workflows/release.yml
  • .goreleaser.yaml
  • dev-docs/CI.md
  • docs/INSTALLATION.md
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ci/signed-releases-slsa-provenance

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

# shorter tag — slsa-verifier resolves the builder identity from this exact ref,
# and SHA-pinning breaks that resolution. See .github/pinact.yaml, which excludes
# this line from automated re-pinning.
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.1.0
@github-actions

github-actions Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Bomly Diff Summary

Compared 62d961b5854dece1509ade042c26e86d8d885ab2 to 8884f474d3f9800d17e4853e39b7bbaeb405ff60.

Overview

Status Manifests Dependencies Findings Duration
⚠️ Warnings +0 / ~1 / -0 +2 / ~0 / -0 2 introduced / 0 persisted / 0 resolved 1m 5s

Dependency Changes

Summary: 2 added, 0 changed, 0 removed.

Added Dependencies

Change Package Version Direct? Scope Licenses
added sigstore:cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 6f9f17788090df1f26f669e9d70d6ae9567deba6 Yes runtime -
added slsa-framework:slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.1.0 v2.1.0 Yes runtime -

Vulnerabilities

✅ No vulnerability changes.

License Changes

✅ No license changes.

Project Posture

✅ No project posture changes (--matchers +scorecard was not selected).

Policy Findings

Summary: 2 introduced, 0 persisted, 0 resolved.

Introduced Findings

Status Category Severity ID Package Fixed In Title
⚠️ introduced license WARNING UNKNOWN-aycg-l4zd-f5ap slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.1.0 - Package license is unknown
⚠️ introduced license WARNING UNKNOWN-tfa2-vy2v-nhab cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 - Package license is unknown

Legend: ✅ resolved · ❌ failing · ⚠️ warning

@bomly-guy bomly-guy merged commit f2a9212 into main Jun 30, 2026
13 checks passed
@bomly-guy bomly-guy deleted the ci/signed-releases-slsa-provenance branch June 30, 2026 07:55
bomly-guy added a commit that referenced this pull request Jun 30, 2026
…se upload failure (#223)

* ci: keep release draft until provenance lands, fixing immutable-release upload failure

The v0.15.3 release run (#222's first real release) failed: the SLSA
provenance job tried to upload multiple.intoto.jsonl to the release
after GoReleaser had already published it, and GitHub's immutable
releases feature (GA Oct 2025) rejects asset uploads to any release
once published — "Cannot upload assets to an immutable release."

Per GitHub's own guidance, the fix is to keep the release as a draft
until every asset is attached, and publish last:

- .goreleaser.yaml: release.draft is now true. GoReleaser creates the
  release and uploads its assets (archives, packages, checksums,
  signature) without publishing.
- release.yml: the provenance job now uploads to the still-draft
  release (drafts are exempt from the immutability restriction). A new
  `publish` job, gated on `needs: [release, provenance]`, mints a fresh
  release-bot app token and runs `gh release edit --draft=false` once
  both are done — preserving the existing requirement that publishing
  use an app-attributed token so `release: published` cascades to
  notify-landing-yank.yml.
- dev-docs/CI.md: documents the new draft -> provenance -> publish
  sequencing and the immutable-releases constraint driving it.

Known minor tradeoff: the Homebrew/Scoop/WinGet manifest PRs are
opened by GoReleaser before publish, so their release-asset URLs are
briefly (well under a minute) not yet publicly downloadable until the
`publish` job finishes. Documented in dev-docs/CI.md; not fixed here
since it's a narrow timing window with low practical impact.

Validated locally: `goreleaser check` accepts the config (only the
pre-existing, unrelated `brews` deprecation warning remains), and
`make test` passes. The draft/publish/token-cascade behavior itself
can only be verified by a real tagged release in GitHub Actions.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* ci: avoid template-injection in release publish step

Address CodeRabbit/zizmor finding on #223: github.ref_name was
interpolated directly into the run: shell command, which expands
before Bash parses the line. Pass it through env instead and reference
it as a quoted shell variable, so a crafted tag name can't inject
shell commands.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants