Pin all third-party GitHub Actions to commit SHAs#2724
Conversation
Every `uses:` reference to a third-party GitHub Action in `.github/workflows/` was pinned only to a mutable tag (`@v4`, `@v2`, `@v30`, ...). A mutable tag lets whoever controls the upstream tag run arbitrary code in our CI the moment it moves -- the supply-chain hole that issue #620 ("pin all deps so we don't get ledgered") calls out, and the exact vector behind the tj-actions/changed-files compromise. Pin all 11 distinct third-party actions to their current full 40-hex commit SHA, keeping a `# vX` comment for readability: actions/checkout v4 / v2 actions/cache v4 actions/setup-node v4 nixbuild/nix-quick-install-action v30 cachix/cachix-action v15 nix-community/cache-nix-action v6 / v7 Swatinem/rust-cache v2 jlumbroso/free-disk-space v1.3.1 softprops/action-gh-release v2 First-party `rainlanguage/*` shared-CI refs (rainix / github-chore reusable workflows and actions) are intentionally left at `@main`: the org tracks those at `main` on purpose, so pinning them is an org-wide decision rather than this repo's. npm / cargo / nix dependency trees are already pinned by their committed lockfiles (package-lock.json, Cargo.lock, flake.lock, soldeer.lock); action refs were the one surface with no lock equivalent. Add a regression guard so a floating ref cannot creep back in: - `script/check-pinned-actions.sh` scans every workflow `uses:` line and prints `OK` iff each third-party ref is a 40-hex SHA, else `UNPINNED: <ref> ...` listing the offenders (skips `rainlanguage/*`, local `./` actions and `docker://` refs). Mirrors the existing `script/check-published-deploy-constants.sh` FFI-oracle pattern. - `test/PinnedActions.t.sol` runs that script via `vm.ffi` and asserts the output equals exactly `OK`. Reintroducing e.g. an unpinned `actions/checkout` reds the test with the exact offending ref. CI-only change: no `src/` content changes, so no soldeer publish, no deployed-bytecode change. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Warning Review limit reached
More reviews will be available in 37 minutes. Learn how PR review limits work. Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file). ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits. 🚦 How do rate limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, the refill rate gradually slows as usage increases. The highest same-day bursts are limited more strictly. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (16)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Rework the action-pinning approach: instead of inline-pinning the shared nix/cachix CI preamble actions (checkout, nix-quick-install, cachix-action, cache-nix-action, rust-cache, actions/cache, action-gh-release) in each raindex workflow, consume the rainix composite actions (rainlanguage/rainix/.github/actions/*) that already pin every one of those SHAs once. The pins are inherited and maintained in rainix. Twelve workflows now delegate their preamble to the composites: copilot-setup-steps, deploy-subgraph, manual-rs-release, npm-package-release, test-js-bindings, test-ui-components, test-webapp, vercel-docs-preview, vercel-docs-prod, wasm-artifacts, wasm-browser-test, wasm-test. Workflows that free disk space between checkout and nix-quick-install (or do an ssh-key checkout) pass checkout: false and run checkout themselves first; nix-cachix-setup's gc-max-store-size-linux carries each workflow's store budget. free-disk-space and setup-node have no rainix composite wrapper, so they stay inline SHA-pinned. The vercel-preview / vercel-prod webapp deploys keep their inline pins too; they are being migrated to the rainix-vercel reusable workflow separately. check-pinned-actions.sh / PinnedActions.t.sol require a SHA pin for every third-party action that remains inline and exempt first-party rainlanguage/* refs, so a workflow satisfies the invariant either by inline-pinning or by delegating to a rainix composite that pins it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Take main's simplified npm-blacklist job (checkout@v4, cache-nix@v7, gc-max-store-size 8G, npm install --no-check) in both vercel workflow files; the full vercel build is in the reusable rainix-vercel workflow. Co-Authored-By: Claude <noreply@anthropic.com>
…A refs in vercel-preview and vercel-prod npm-blacklist jobs
Fixes #620 (the GitHub Actions slice of "pin all deps so we don't get ledgered").
What the issue wanted
Pin floating dependency refs for supply-chain safety so a moved upstream
tag can't silently inject code into our pipeline.
What this does
The npm / cargo / nix dependency trees are already pinned by their
committed lockfiles (
package-lock.json,Cargo.lock,flake.lock,soldeer.lock). The one surface with no lock equivalent was GitHubAction
uses:refs, every one of which pointed at a mutable tag(
@v4,@v2,@v30, ...). That is the literal "get ledgered" vector(the tj-actions/changed-files compromise moved a
@v*tag).This pins all 11 distinct third-party actions to their current full
40-hex commit SHA, with a
# vXcomment for readability:actions/checkoutactions/cacheactions/setup-nodenixbuild/nix-quick-install-actioncachix/cachix-actionnix-community/cache-nix-actionSwatinem/rust-cachejlumbroso/free-disk-spacesoftprops/action-gh-releaseEach SHA is the current tip of the tag it replaces, so behavior is
unchanged;
v2checkouts stay on v2.First-party
rainlanguage/*shared-CI refs (rainix / github-chorereusable workflows + actions) are intentionally left at
@main— theorg tracks those at
mainon purpose, so pinning them is an org-wide callrather than this repo's.
Regression guard (discriminating test)
script/check-pinned-actions.shscans every workflowuses:line andprints
OKiff each third-party ref is a 40-hex SHA, elseUNPINNED: <ref> ...naming the offenders (skipsrainlanguage/*,local
./actions,docker://). Mirrors the existingscript/check-published-deploy-constants.shFFI-oracle pattern.test/PinnedActions.t.solruns it viavm.ffiand asserts the outputis exactly
OK. This is discriminating, not "it runs": reintroduceany floating ref and the test reds with that exact ref.
Verification
forge test --match-path test/PinnedActions.t.sol→[PASS] testEveryThirdPartyActionIsShaPinned.actions/checkout@v4makes the script printUNPINNED: actions/checkout@v4(so the test would red); restoring itreturns
OK.match.
src/content, so no soldeer publish and nodeployed-bytecode change.
Note: the full
forge testsuite has a pre-existing compile failureunrelated to this PR —
test/concrete/parser/RaindexV6SubParser.routingTokenDecimalsAndMasks.t.solimports
rainlang-0.1.2/...while the repo migrated torainlang-0.1.5(remappings,
foundry.toml,soldeer.lockall0.1.5). It is byte-identicalto
mainhere and untouched by this PR; the new test was run with thatfile isolated to confirm it passes.
🤖 Generated with Claude Code