Pin actions by SHA and drop job permissions to least privilege - #1
Merged
Conversation
`uses: owner/repo@v2` resolves a mutable git ref when the job starts. Whoever owns that repository can repoint the tag at any commit, and the next run executes it with this workflow's GITHUB_TOKEN. What this repository ships is provisioning scripts a reader runs as root on their own hardware, so an action that changes under its tag can alter what a reader is told to execute. Both references now name a 40-character commit SHA with a trailing version comment, so the code reviewed is the code that runs and a reader can still see which release a SHA is. Both checkouts set persist-credentials: false. The default leaves the job token in .git/config, where any later step, including the link checker's own tooling, can read it and act as this workflow. Each job declares contents: read rather than inheriting the repository default, which on many repositories is read-write. scripts/check-workflow-pins.js enforces both properties on every push and pull request, so a pin that decays back to a tag fails the build instead of passing review as an ordinary line in a diff. It is stdlib-only and reads no YAML: a guard on the supply chain that runs third-party code to do its job has moved the problem rather than solved it. scripts/check.sh runs it too, because that script promises the same checks as CI and a pin is cheaper to fix before the push.
CI's Shellcheck step has failed on every run since 2026-07-03. Both hits are SC2034 on `for i in $(seq 1 N)` retry loops where the counter is genuinely unused, which is idiomatic; the underscore is shellcheck's own convention for a variable that exists only to make the loop repeat. A lint gate that is permanently red teaches everyone to ignore it, which costs more than the warnings it reports. Signed-off-by: reesebuilt <126643625+reesepj@users.noreply.github.com>
repsecure
force-pushed
the
harden/pin-actions-least-privilege
branch
from
August 5, 2026 15:54
6d04834 to
0a3ba1e
Compare
repsecure
added a commit
that referenced
this pull request
Aug 5, 2026
Pin actions by SHA and drop job permissions to least privilege
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.
Brings this repo to the same workflow supply-chain standard as agentwall.
Every action is now pinned to a full commit SHA with the precise version in a trailing comment. A mutable tag can be repointed at arbitrary code that then runs holding the job's credentials, and
softprops/action-gh-release@v2was running that way in a job withcontents: write.Checkouts set
persist-credentials: false, so the token stops living in.git/configfor the remainder of the job. Every job declares explicit least-privilege permissions instead of inheriting the repository default.A
workflowsjob now runs a dependency-free pin checker, so the property is enforced rather than remembered.Two changes beyond the brief, both worth calling out:
desktopjob heldcontents: writewhile runningnpm ciand electron-builder across three operating systems. Permissions are per-job, so it is split intodesktop(build and upload artifacts,contents: read) andrelease(attach them,contents: write). It reuses artifacts the workflow already uploaded, andif-no-files-found: errorprevents the split from producing an empty release.GH_TOKENwas removed from the build step. All three dist scripts pass--publish never, so electron-builder has nothing to publish, and a token there is readable by every process the build spawns.scripts/check.shalso runs the pin check now, because it promises the same checks as CI and would otherwise have been making a false claim.