Pin every action to a commit SHA #371

Closed
opened 2026-09-16 05:12:41 +00:00 by jcoffey-dev · 0 comments
Owner

Every uses: in .github/workflows/ now carries a full 40-character commit SHA with its release in a trailing comment.

A tag is a mutable pointer. actions/checkout@v7 is whatever the publisher last moved v7 to, so using one is not trusting the version that was reviewed — it is trusting every future version, including whatever gets pushed by whoever compromises the publisher's account. That is exactly the shape of the tj-actions/changed-files compromise: no repository changed a line, the tags moved underneath them, and the action started dumping runner memory into public build logs.

Action Pinned to Release
actions/checkout 3d3c42e5… v7.0.1
actions/setup-node 82076278… v7.0.0
actions/upload-artifact 043fb46d… v7.0.1
actions/download-artifact 3e5f45b2… v8.0.1
docker/setup-buildx-action 594f3bf4… v4.4.0
docker/login-action dbcb8138… v4.6.0
docker/build-push-action c3c9e263… v7.4.0

Each SHA was resolved from the major tag through the GitHub API and then checked against the exact release tag — every major tag currently points at the release named beside it, so no pin silently moves anyone to a different version than they were already running. This is a no-op for behavior.

dataaxiom/ghcr-cleanup-action was already pinned — it is the one third-party action here, and it is handed packages: write and deletes things — so it only picks up the trailing-version convention. Its comment loses the "pinned rather than a moving major tag" framing, which stops being what distinguishes it once everything else is pinned too.

The two uses: ./.github/workflows/… entries in release.yml and publish.yml are local paths, not actions. They always resolve inside the commit already running, and there is no SHA to pin.

Dependabot already covers this. .github/dependabot.yml has a weekly github-actions entry, and Dependabot updates the SHA and the version comment together, so staying current costs nothing.

Follow-on, not in this PR: once this merges, the repository's sha_pinning_required setting gets turned on, which makes an unpinned ref fail the run instead of quietly resolving. The order matters — flipping it first would break main on the next push. The comment at the top of ci.yml is written for that end state.

Translations: adds none. No user-visible strings — workflow files only.

Testing: all four workflows re-parse as YAML. ci.yml is exercised by this PR's own build check, which is the real test of the checkout and setup-node pins. publish.yml, release.yml and cleanup.yml do not run on a PR; their pins are verified by resolution against the API rather than by execution, and the next weekly release is the first live run.

Merged 2026-09-15 as coffey-labs/ihasmail@0bde2df69d

Rebuilt from: GH Archive, git history, session transcript.

Every `uses:` in `.github/workflows/` now carries a full 40-character commit SHA with its release in a trailing comment. A tag is a mutable pointer. `actions/checkout@v7` is whatever the publisher last moved `v7` to, so using one is not trusting the version that was reviewed — it is trusting every future version, including whatever gets pushed by whoever compromises the publisher's account. That is exactly the shape of the tj-actions/changed-files compromise: no repository changed a line, the tags moved underneath them, and the action started dumping runner memory into public build logs. | Action | Pinned to | Release | | --- | --- | --- | | `actions/checkout` | `3d3c42e5…` | v7.0.1 | | `actions/setup-node` | `82076278…` | v7.0.0 | | `actions/upload-artifact` | `043fb46d…` | v7.0.1 | | `actions/download-artifact` | `3e5f45b2…` | v8.0.1 | | `docker/setup-buildx-action` | `594f3bf4…` | v4.4.0 | | `docker/login-action` | `dbcb8138…` | v4.6.0 | | `docker/build-push-action` | `c3c9e263…` | v7.4.0 | Each SHA was resolved from the major tag through the GitHub API and then checked against the exact release tag — every major tag currently points at the release named beside it, so no pin silently moves anyone to a different version than they were already running. This is a no-op for behavior. `dataaxiom/ghcr-cleanup-action` was already pinned — it is the one third-party action here, and it is handed `packages: write` and deletes things — so it only picks up the trailing-version convention. Its comment loses the "pinned rather than a moving major tag" framing, which stops being what distinguishes it once everything else is pinned too. The two `uses: ./.github/workflows/…` entries in `release.yml` and `publish.yml` are local paths, not actions. They always resolve inside the commit already running, and there is no SHA to pin. **Dependabot already covers this.** `.github/dependabot.yml` has a weekly `github-actions` entry, and Dependabot updates the SHA and the version comment together, so staying current costs nothing. **Follow-on, not in this PR:** once this merges, the repository's `sha_pinning_required` setting gets turned on, which makes an unpinned ref fail the run instead of quietly resolving. The order matters — flipping it first would break `main` on the next push. The comment at the top of `ci.yml` is written for that end state. **Translations:** adds none. No user-visible strings — workflow files only. **Testing:** all four workflows re-parse as YAML. `ci.yml` is exercised by this PR's own `build` check, which is the real test of the `checkout` and `setup-node` pins. `publish.yml`, `release.yml` and `cleanup.yml` do not run on a PR; their pins are verified by resolution against the API rather than by execution, and the next weekly release is the first live run. **Merged** 2026-09-15 as coffey-labs/ihasmail@0bde2df69d9b <sub>Rebuilt from: GH Archive, git history, session transcript.</sub>
This repo is archived. You cannot comment on issues.