Repository navigation
Security: pin GitHub Actions in release and CI workflows to commit SHAs - #10
pimfeltkamp wants to merge 1 commit into
Conversation
The tag-triggered publish job holds CARGO_REGISTRY_TOKEN and contents: write but ran dtolnay/rust-toolchain@stable (moving branch), softprops/action-gh-release@v3 (moving tag), actions/checkout@v6 and actions/cache@v5. A compromised action could install a trojaned cargo that reads the token or tampers with the published crate. ci.yml writes the same cargo cache the release job restores, so its actions are pinned too. Each pin resolves to the exact commit the old mutable ref pointed at today, so no behaviour changes. Dependabot's github-actions entry keeps them updated. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
pimfeltkamp
left a comment
There was a problem hiding this comment.
2 findings (high): dtolnay/rust-toolchain pinned to a SHA with no toolchain input will fail in ci.yml and release.yml (the release would never publish). Details are in the inline comments.
|
Review feedback round: no code changes.
Both findings (ci.yml line 18 and release.yml line 20) say
Caveat, noted in both threads: the I replied in both threads and left them open for the reviewer. |
pimfeltkamp
left a comment
There was a problem hiding this comment.
✅ Automated review passed — no issues found. (Would approve, but the reviewer account authored this PR and GitHub forbids self-approval.)
✅ Safe to merge
What was vulnerable
The tag-triggered
publishjob in.github/workflows/release.ymlruns withcontents: writeand exposesCARGO_REGISTRY_TOKENtocargo publish, yet it used third-party actions on mutable refs:dtolnay/rust-toolchain@stable(a moving branch),softprops/action-gh-release@v3(a moving tag), plusactions/checkout@v6andactions/cache@v5. If any of those upstream refs were compromised, the action could install a trojaned cargo that reads the crates.io token or tampers with the published crate.Review also showed that
ci.ymlwrites the same cargo cache family (${{ runner.os }}-cargo-) that the release job restores viarestore-keys, so an unpinned action in CI could poison registry sources or build outputs that the release job then consumes. CI is therefore pinned as well.What the fix does
Pins every
uses:in both workflows to a full 40-character commit SHA with a version comment:d23441a4…v6resolves to today)b725eda5…stable(2026-10-16)caa29612…v5resolves to today)efb35369…v3resolves to today)All SHAs were resolved live from GitHub in this run and independently re-verified by a second reviewer pass.
Why it is non-breaking
Each pin is byte-identical to the commit the old mutable ref currently points at, so the jobs run exactly the same action code as before.
dtolnay/rust-toolchainat that commit still runsrustup toolchain install stableon every run, so releases continue to build with the newest stable Rust; only the action's own script is frozen. No public API, crate contents or accepted input change..github/dependabot.ymlalready has agithub-actionsentry, so the three version-tagged pins will be bumped automatically. Note: thedtolnay/rust-toolchainpin is a branch head without a tag, so Dependabot will likely not bump it on its own; it will need an occasional manual refresh. This is low impact because the Rust version itself is not frozen.Tests run
The repository has no tests covering workflow files. Both files were syntax-checked:
No Rust code changed, so the cargo test suite was not run.
🤖 Automated security fix by claude-code-security. It is reviewed by the automated PR reviewer and auto-merged only if the review passes, CI is green, and the change is small and non-breaking.
🤖 Generated with Claude Code