feat: pin actions to SHAs and gate the repo with zizmor - #3
Merged
Conversation
Every uses: reference here resolved a mutable name, in a public repo whose main branch has no protection and which 25 call sites across 8 repositories consume at @main. - inline get_head_commit_hash into build_and_push, removing the transitive @main reference a consumer could not pin past - pin the five docker/* actions to commit SHAs at their current major, so the change carries no behaviour difference; upgrades arrive as separate Dependabot PRs - add Dependabot with a 7-day cooldown, so a pinned SHA does not become a frozen vulnerable version and a fresh release is never proposed on the day it lands - hoist 11 shell interpolations of ${{ inputs.* }} into env: blocks - add zizmor as a blocking check, pinned to 1.30.0 so an upstream release adding an audit cannot turn the repo red on its own zizmor reports zero findings on this branch, online mode. get_head_commit_hash stays in place, deprecated: it has three external callers in pubky-app and pubky-stack. Refs: pubky/pubky-stack#281 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool. What Enabling Code Scanning Means:
For more information about GitHub Code Scanning, check out the documentation. |
The hardening commit rewrote shell in all four actions and swapped six action references for SHAs, but nothing in CI ever invoked them - only zizmor ran. This builds a trivial image through build_and_push with push disabled, on amd64 and on amd64+arm64, so the inlined commit-hash step, the cache-value shell, the QEMU path and every pinned action are executed rather than merely reviewed. check_if_image_exists is asserted both ways against a public image. get_head_commit_hash is covered because three consumers still call it. registry_login is not covered: it cannot run without real registry credentials. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of pubky/pubky-stack#281 — the "use hashes instead of versions" and "add scanners like zizmor" bullets.
Why
Every
uses:reference in this repo resolved a mutable name. A tag is a label its owner can move;@mainmoves on every push. That is thetj-actions/changed-filesfailure mode, where moved tags leaked secrets from tens of thousands of repositories.It matters more here than in a normal repo: 25
uses:references across 8 repositories consume this one at@main, andmaincurrently has no protection and no rulesets.What changed
Removed the transitive
@main.build_and_pushcalledget_head_commit_hash@main, so even a consumer pinningbuild_and_pushto a SHA would still have pulled a floating ref through it. Those four lines ofgit rev-parse HEADare now inline.get_head_commit_hashitself stays — it has three external callers (pubky-app×2,pubky-stack×1) — and is marked deprecated in its description rather than deleted.Pinned the five
docker/*actions to commit SHAs, at their current major rather than the latest. Every one of them is a full major behind (v3→v4.3.0,v5→v6.2.0,v6→v7.3.0), so pinning current means this PR carries zero behaviour change. The upgrades arrive afterwards as separate, reviewable Dependabot PRs instead of riding along inside a security change.Added Dependabot with a 7-day cooldown. A SHA pin without a bump bot is just a frozen vulnerable version; a bump bot without a cooldown proposes a freshly published release on the day it lands, which is the attack path above. (zizmor flagged the missing cooldown on my first attempt at this file — a fair demonstration of the check earning its place.)
Hoisted 11 shell interpolations out of
run:blocks.${{ inputs.x }}is pasted in as text before bash parses it, so whoever controls the value controls the script. All now pass throughenv:and are referenced as quoted variables.Added zizmor as a blocking check, pinned to
1.30.0. Notlatest— on a floating version, an upstream release that adds an audit turns this repo red with nobody having changed anything, and with a required check that is the merge queue down.Verification
zizmor reports zero findings on this branch in online mode, against 18 findings on
main(11template-injection, 7unpinned-uses, all High):The check running on this PR is itself the proof the gate works.
Follow-ups, not in this PR
mainhas no branch protection, so a required check can currently be pushed straight past. Filed separately — needs admin.security-events: write, which fork PRs do not receive. If the check fails on an outside contribution, that is why.directoriesglob only shows on the first scheduled run. Renovate is the fallback if it misses them.@mainfor now; tagging and migration are a later round.🤖 Generated with Claude Code