…edentials
A Greptile review on the membrane rollout of this same policy
(antra-tess/membrane#45) flagged the github-release job for running
actions/checkout at a mutable major-version ref: retargeting that tag would
change the code running with permission to create and edit releases.
This repo shipped that publish.yml in an earlier rollout of the policy, so
the finding lands here identically — and it is live on main rather than
sitting in a PR. Fixed across every workflow, not just the flagged line:
8 actions pinned to immutable SHAs across changelog.yml, ci.yml and
publish.yml, version tags kept as trailing comments so the pins stay
readable and Dependabot can still bump them.
The review named the least privileged of the write-capable jobs. Two things
it did not:
- The npm publish job holds `id-token: write` for OIDC trusted publishing.
A swapped action there can reach a live publish credential — a worse
outcome than edited release notes.
- checkout defaults to persist-credentials: true, writing the job token into
.git/config where every later step in the job can read it. Nothing here
pushes over git — publish uses OIDC, the release job uses gh with GH_TOKEN
— so all 4 checkouts now set it false.
No behavior change intended: each pinned SHA is the current head of the tag
it replaces.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Problem
A Greptile review on the membrane rollout of this same contribution policy
(antra-tess/membrane#45)
flagged the
github-releasejob for runningactions/checkoutat a mutablemajor-version ref. That job holds
contents: write; retargeting the tag wouldchange the code running with permission to create and edit releases.
This repo shipped that
publish.ymlin an earlier rollout, so the findingapplies here identically — with the difference that here it is live on
main, not sitting in a PR. And now that the analysis is written up in apublic review thread, the reasoning is available to anyone who reads it, which
is reason enough to close it rather than file it.
The review named the least privileged of the write-capable jobs. Two things it
did not:
id-token: writefor OIDC trusted publishing,and ran the same mutable ref. A swapped action there can reach a live
publish credential — a considerably worse outcome than edited release notes.
actions/checkoutdefaults topersist-credentials: true, writing thejob token into
.git/config, where every later step in that job can readit. In a
contents: writeorid-token: writejob that is a durableambient credential sitting behind whatever the remaining steps do.
Changes
across
changelog.yml,ci.ymlandpublish.yml. Not just the flaggedline: a half-pinned repo invites the same finding next time, and the sharper
job was the unflagged one.
persist-credentials: falseon all checkouts. Nothing here pushes overgit — publish authenticates via OIDC, the release job via
ghwithGH_TOKEN— so no step needs the credential left behind.@<sha> # v4.4.0) so the pins stayreadable and Dependabot can still bump them.
Each pinned SHA is the current head of the tag it replaces, so no behavior
change is intended.
Tests
All workflow files parse as YAML, and a sweep for
uses:refs not matching@<40-hex>returns nothing — no action is left on a mutable ref.The pins were resolved through the GitHub API by dereferencing each tag to its
commit, not copied from another repo.
Not verified
CI runs. The publish and release jobs are tag-gated and cannot run until
the next release; the pin is a like-for-like ref swap, but it stays
unexercised on real release infrastructure until then. This is the same
exposure the policy rollout PRs already carried.
SHAs beyond confirming each SHA is that tag's current head — if a tag had
already been retargeted before today, this pins the current state rather
than an audited one.
Companion PRs
The same fix landed on the three open policy PRs, which carry the same
publish.yml:Safe to merge in any order — no shared files, and each repo's workflows are
independent.
Worth flagging from that batch: chronicle was the worst of the set. Its build
matrix ran
dtolnay/rust-toolchain@stable— a branch ref, not a version tag,so not merely mutable but expected to move — and that job cross-compiles the
.nodebinaries whichupload-artifacthands to the OIDC publish job. That isa supply-chain path straight into a signed npm artifact.
CHANGELOG.mdupdated under## Unreleased— or this change isinternal-only / test-only / docs-only (apply the
no-changeloglabel).CI-config only, with no consumer-visible behavior change, which the policy
explicitly excludes from needing an entry. The
changelogcheck will pass onits own since no
src/file is touched.🤖 Generated with Claude Code