Skip to content

Scope releaser permissions and bind the tag expression - #6263

Merged
ChrisJBurns merged 2 commits into
mainfrom
harden-releaser-workflow
Aug 10, 2026
Merged

Scope releaser permissions and bind the tag expression#6263
ChrisJBurns merged 2 commits into
mainfrom
harden-releaser-workflow

Conversation

@ChrisJBurns

Copy link
Copy Markdown
Collaborator

First of the release-workflow changes tracked in #6253. Deliberately the smallest and most contained one, because nothing here can be exercised by a pull requestreleaser.yml runs only on release: published.

Summary

  • The workflow handed contents: write to jobs that only read. It was declared at workflow level, so every job that did not declare its own inherited it — verify-release and compute-build-flags, both of which just check out and read git locally. The default is now contents: read and those two declare it explicitly, so nothing inherits.
  • A tag name was interpolated into a command substitution. gh release view "${{ github.ref_name }}" inside $( ). Git ref names may contain ;, $, backticks and | — unlike, say, a repository name — so this one is a real sink rather than a stylistic nit. It is still gated behind repo write access, since creating a release requires it.

Why the permission change is safe

A job-level permissions: block replaces the workflow default rather than extending it, so the jobs that write were never relying on the workflow-level grant:

Job Permissions Effect
verify-release inherited → now explicit contents: read only reads
compute-build-flags inherited → now explicit contents: read only reads
release-binaries declares contents: write, id-token: write unchanged
image-build-and-push declares its own unchanged
skills-build-and-push declares its own unchanged
publish-helm declares its own unchanged
notify-release-failure declares {} unchanged

Every job in the file now states its own permissions, so this cannot drift back by someone adding a job that quietly inherits write.

Effect

releaser.yml goes from 4 High findings to 1; repo-wide High goes 10 → 7. The remaining one is github-app — the Homebrew tap token inheriting blanket installation permissions — which is deliberately left for a later change, because scoping app tokens is the part most likely to break a release and wants the App's actual installation permissions checked first.

Part of #6253

Type of change

  • Bug fix
  • New feature
  • Refactoring (no behavior change)
  • Dependency update
  • Documentation
  • Other (describe): CI configuration

Test plan

  • Unit tests (task test)

  • E2E tests (task test-e2e)

  • Linting (task lint-fix)

  • Manual testing (describe below)

  • Enumerated every job in the file and confirmed which inherited the workflow-level permission, rather than assuming — only the two read-only jobs did.

  • Asserted programmatically that no job inherits any more.

  • Workflow parses as YAML; actionlint reports nothing new.

  • zizmor on this file: excessive-permissions 1 → 0 and template-injection 2 → 0.

This cannot be verified before merge. releaser.yml runs only on release: published, so the first real exercise is the next release. That is why this change is limited to a permissions default and a quoting fix, and why the app-token work is not in it.

Does this introduce a user-facing change?

No.

Special notes for reviewers

  • Best merged just after a release rather than just before, to maximise the window for noticing anything wrong.
  • The --json assets cleanup step is a re-run convenience; if the quoting change were wrong it would fail that step rather than corrupt a release, which is part of why it was chosen to go first.
  • Unrelated pre-existing bug spotted while here, not fixed: line 354 references needs.extract-release-actor.outputs.triggered_by, but no extract-release-actor job exists in this workflow. The expression resolves to null and falls through to || github.actor, so the failure-notification "Triggered by" line silently never shows the preserved release triggerer that create-release-tag.yml goes to the trouble of extracting. Worth its own issue; changing it here would mean altering release behaviour in a pull request that cannot test it.

Generated with Claude Code

The workflow granted contents: write to every job that did not declare
its own, which was verify-release and compute-build-flags — both only
check out and read. The default is now read and those two say so
explicitly, so no job inherits. The jobs that write already declared
their own permissions, which replace the workflow default rather than
extend it, so they are untouched.

The asset-cleanup step interpolated github.ref_name into a command
substitution. Git ref names may contain ';', '$', backticks and '|', so
the value is now bound through the step's existing env: block.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@ChrisJBurns
ChrisJBurns requested a review from JAORMX as a code owner August 10, 2026 17:54
@github-actions github-actions Bot added the size/XS Extra small PR: < 100 lines changed label Aug 10, 2026
@codecov

codecov Bot commented Aug 10, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 72.72%. Comparing base (ba2cbcb) to head (d07a2bf).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #6263      +/-   ##
==========================================
+ Coverage   72.70%   72.72%   +0.01%     
==========================================
  Files         742      742              
  Lines       77299    77299              
==========================================
+ Hits        56202    56217      +15     
+ Misses      17133    17118      -15     
  Partials     3964     3964              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@ChrisJBurns
ChrisJBurns merged commit 094a621 into main Aug 10, 2026
15 checks passed
@ChrisJBurns
ChrisJBurns deleted the harden-releaser-workflow branch August 10, 2026 19:10
@github-actions github-actions Bot added size/XS Extra small PR: < 100 lines changed and removed size/XS Extra small PR: < 100 lines changed labels Aug 10, 2026
@github-actions github-actions Bot mentioned this pull request Aug 14, 2026
2 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/XS Extra small PR: < 100 lines changed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants