Skip to content

ci: pin every action to an immutable SHA; stop persisting checkout credentials - #57

Merged
Anarchid merged 1 commit into
mainfrom
ci/pin-actions
Aug 10, 2026
Merged

ci: pin every action to an immutable SHA; stop persisting checkout credentials#57
Anarchid merged 1 commit into
mainfrom
ci/pin-actions

Conversation

@Anarchid

@Anarchid Anarchid commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Problem

A Greptile review on the membrane rollout of this same contribution policy
(antra-tess/membrane#45)
flagged the github-release job for running actions/checkout at a mutable
major-version ref. That job holds contents: write; retargeting the tag would
change the code running with permission to create and edit releases.

This repo shipped that publish.yml in an earlier rollout, so the finding
applies 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 a
public 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:

  • The npm publish job holds id-token: write for 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/checkout defaults to persist-credentials: true, writing the
    job token into .git/config, where every later step in that job can read
    it. In a contents: write or id-token: write job that is a durable
    ambient credential sitting behind whatever the remaining steps do.

Changes

  • Every action in every workflow pinned to an immutable commit SHA
    across changelog.yml, ci.yml and publish.yml. Not just the flagged
    line: a half-pinned repo invites the same finding next time, and the sharper
    job was the unflagged one.
  • persist-credentials: false on all checkouts. Nothing here pushes over
    git — publish authenticates via OIDC, the release job via gh with
    GH_TOKEN — so no step needs the credential left behind.
  • Version tags kept as trailing comments (@<sha> # v4.4.0) so the pins stay
    readable 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

  • The workflows have not been executed on this branch beyond what push-time
    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.
  • No behavioral diff was taken between the old floating tags and the pinned
    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
.node binaries which upload-artifact hands to the OIDC publish job. That is
a supply-chain path straight into a signed npm artifact.


  • CHANGELOG.md updated under ## Unreleased — or this change is
    internal-only / test-only / docs-only (apply the no-changelog label).

CI-config only, with no consumer-visible behavior change, which the policy
explicitly excludes from needing an entry. The changelog check will pass on
its own since no src/ file is touched.

🤖 Generated with Claude Code

…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>
@Anarchid

Anarchid commented Aug 7, 2026

Copy link
Copy Markdown
Contributor Author

The red ubuntu job is inherited, not from this PR — but it does mean main is red

This PR changes three files, all under .github/workflows/ (gh pr diff 57 --name-only confirms). Nothing it touches can affect test outcomes, and the pinned SHAs are each the current head of the tag they replace, so the jobs resolve the same action code they did before.

The failures are the kv-stable demand (issue #56) family:

not ok 49 - kv-stable demand (issue #56): escalation with uncovered chunks emits an L1 produce op
not ok 50 - kv-stable demand (issue #56): covered / pinned / locked chunks split the uncovered runs
not ok 51 - kv-stable demand (issue #56): head/tail chunks are never demanded

Evidence that this is the branch point and not the branch:

The reason nobody has noticed: main's last completed CI run was 73e028cb (2026-08-06 11:44, green). The next push run, bea2e39e at 17:01, ended cancelled — and 0e4beac and 8781cfe landed after it without a completed run of their own. So main's ubuntu status has been unverified since 11:44 on 2026-08-06, and the first thing to actually run those tests against current main was a PR that has nothing to do with them.

Worth a separate issue against the kv-stable/#56 work rather than anything on this branch. Flagging rather than fixing, since diagnosing a platform-split failure in the demand path is not something to smuggle into a CI-hardening PR.

The changelog check and macos both pass here, and this PR's own subject matter — the action pins — is verified in the PR body.

@Anarchid
Anarchid merged commit 9238e3e into main Aug 10, 2026
3 of 5 checks passed
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.

1 participant