Skip to content

CI: pin actions to commit SHAs and declare workflow permissions - #3881

Open
Totara-thib wants to merge 2 commits into
sharkdp:masterfrom
Totara-thib:ci-hardening
Open

CI: pin actions to commit SHAs and declare workflow permissions#3881
Totara-thib wants to merge 2 commits into
sharkdp:masterfrom
Totara-thib:ci-hardening

Conversation

@Totara-thib

Copy link
Copy Markdown

Hi, drive-by CI hardening in two commits, one logical change each, found with the Plumber CLI (https://github.com/getplumber/plumber) and verified on master.

Commit 1 pins every action to its commit sha, versions kept as comments. A tag like @v2 or a branch like @master is a movable pointer: whoever controls the action, or anyone who compromises it, can re-point it and your next run executes their code with the job's credentials. The paths that matter here: the build job publishes the release archives people download while holding a contents write token, and the winget job runs with WINGET_TOKEN. Same pattern as the tj-actions/changed-files incident (CVE-2025-30066). You already pin winget-releaser by sha, this finishes the job in the same spirit. rust-toolchain's tags are toolchain names, so all six sites point at the same master commit and the toolchain moves to an explicit toolchain: input (stable where the tag implied it, your msrv site already had one), zero behavior change. Your dependabot config already covers the github-actions ecosystem, so the pins keep themselves bumped with the comments in sync.

Commit 2 scopes the GITHUB_TOKEN per workflow, derived from what each job actually uses: everything drops to contents: read, except the build job which keeps contents: write to publish the release archives on tag builds. The winget job pushes with its own token and the changelog check reads the public API unauthenticated, so neither needs anything.

One heads up: if an Actions allowlist is configured in settings, patterns written against tags (like owner/action@v2) stop matching once refs are shas and workflows refuse to start. Entries need the owner/action@* form.

A CHANGELOG entry is included per CONTRIBUTING. I will also open a PR which adds the tool to CI so this does not quietly drift back; that one is a bonus, this PR stands on its own.

A tag like @v2 or a branch like @master is a movable pointer: whoever
controls the action repository can re-point it, and the next run
executes whatever it points at with the job's credentials. The paths
that matter here: the build job publishes the release archives people
download with a contents write token, and the winget job holds
WINGET_TOKEN. This is exactly how the tj-actions/changed-files
compromise spread (CVE-2025-30066).

winget-releaser is already pinned by sha, this finishes the job in
the same spirit for checkout, upload-artifact and rust-toolchain.
rust-toolchain's tags are toolchain names, so every site is pinned to
the same master commit and the toolchain moves to an explicit
'toolchain:' input (stable where the tag implied it, the msrv site
already had one), zero behavior change. Dependabot's github-actions
ecosystem is already configured here, so it keeps bumping the pins
with the comments in sync.

Every sha was resolved from the upstream repository and cross-checked
against its release tag.
Jobs without a permissions block inherit the repository default token
scope, and most of these jobs run pull request code while holding
that token. Scopes are derived from what each job actually does:
everything drops to contents: read, except the build job, which keeps
contents: write to publish the release archives on tag builds. The
winget job pushes with its own dedicated token and the changelog
check reads the public API unauthenticated, so both need nothing.
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.

1 participant