Skip to content

ci: pin every workflow action to a commit hash - #95

Open
MajorLift wants to merge 2 commits into
mainfrom
jongsun/ci/pin-actions-to-hashes
Open

ci: pin every workflow action to a commit hash#95
MajorLift wants to merge 2 commits into
mainfrom
jongsun/ci/pin-actions-to-hashes

Conversation

@MajorLift

@MajorLift MajorLift commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Overview

Pins every external GitHub Action reference to a commit hash — 16 references across 4 workflows — plus test/workflows.test.mjs to keep them pinned and .github/dependabot.yml to keep them current. No behavior change: each pin is the commit its tag resolved to.

Motivation

Code scanning enforces a blanket hash policy, but only against changed files — on #47 it fired on the workflow being touched and left the sixteen already in the tree unflagged.

A tag is a moving pointer, repointable with no diff or review: the shape of the tj-actions/changed-files compromise.

Showcase

These workflows publish releases; every hash was checked by hand against the action repo, not from a script.

  • node --test test/workflows.test.mjs — 3 pass; every pinned SHA's commit message matches its version comment
  • Both checks fire against an injected regression; a third asserts at least one external reference exists, so neither can pass vacuously

.github/dependabot.yml is the one part nothing above covers — that it keeps hash and comment in step is unverified until a tracked tag moves.

Code scanning enforces a blanket hash policy, but only against changed files —
so it fires on a workflow someone edits and stays silent on the sixteen
references already here. Every action in this repo floats on a tag.

A tag is a moving pointer. Whoever controls the action repo can repoint `@v3` at
new code, and every workflow picks it up on the next run with no diff and no
review. That is the shape of the tj-actions/changed-files compromise. A hash
cannot move.

All sixteen references across four workflows are pinned, each carrying its
version in a trailing comment so the file stays readable. Every hash was
resolved from its tag and verified against the action repo: the commit each one
points at has that version as its message.

Two things keep it that way. `test/workflows.test.mjs` fails on any external
`uses:` that is not a 40-character hash, or any hash without a version comment —
both verified to fire. And a Dependabot config, because pinning without it just
freezes actions at whatever they were, security fixes included; Dependabot opens
a PR when the tracked tag moves.
Comment thread .github/dependabot.yml Fixed
Pinning to a hash stops a tag moving underneath us, but Dependabot still
opens the bump PR the moment the tag does move — which is precisely when
a compromised release is freshest and least likely to have been caught.
A seven-day default lets the ecosystem find it first; the pin holds the
previous hash throughout.

Raised by zizmor on this PR: insufficient cooldown in Dependabot updates.
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