ci: scope the write permissions to the jobs that use them - #142
Merged
Conversation
GHA002 flags contents: write and actions: write on dependabot-lockfix.yml, and
actions: write on cleanup-runs.yml, with the remediation "drop it, or state
which step requires it". Dropping any of the three breaks the workflow that
declares it — each is used by exactly one step:
dependabot-lockfix contents: write "Push the fix if anything changed"
(git push origin HEAD:$BRANCH)
dependabot-lockfix actions: write "Approve the pull-request checks on the
fixed commit" (POST .../runs/{id}/approve)
cleanup-runs actions: write "Delete old runs of the high-frequency
workflows" (DELETE .../runs/{id})
So this takes the second half of the remediation, and takes it further than a
comment. Both files declared their write scopes at the workflow level, where
they apply to every job the file will ever contain. They now sit on the single
job that needs them, with the workflow level dropped to contents: read — the
shape container-build.yml already uses. A job added to either file tomorrow
starts read-only instead of inheriting a push token.
Behaviour is unchanged: the one existing job in each file resolves to exactly
the permissions it had before. Whether the scanner is satisfied by a job-level
grant is its business; least privilege at the workflow scope is worth having
either way.
Also normalises a stray CRLF at the end of dependabot-lockfix.yml, which
.gitattributes (* text=auto eol=lf) already asks for.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019h7znwAftvs36vD4YwKMRf
bgard68
added a commit
that referenced
this pull request
Aug 25, 2026
Both scopes are used by exactly one step each — contents: write by "Push the fix if anything changed" and actions: write by "Approve the pull-request checks on the fixed commit" — so GHA002 has nothing to act on. The sentinel:accept markers say that to the scanner in its own syntax, with the reason on the line it flags. Returns the declaration to the workflow level, where the markers sit on the lines GHA002 reports, and drops the job-level block added in #142 so there is one place to read the answer instead of two. Effective permissions are unchanged: the file has a single job, which inherits exactly what it used to override with. Claude-Session: https://claude.ai/code/session_019h7znwAftvs36vD4YwKMRf Co-authored-by: Claude Opus 5 <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.
GHA002 flags three write scopes. None of them can be dropped — each is used by exactly one step, and removing it breaks the workflow that declares it:
dependabot-lockfix.ymlcontents: writegit push origin HEAD:$BRANCHdependabot-lockfix.ymlactions: writePOST .../actions/runs/{id}/approvecleanup-runs.ymlactions: writeDELETE .../actions/runs/{id}Dropping
contents: writeleaves the lockfix job unable to push the repair it exists to make — the repair PR #137 just landed. Droppingactions: writeoncleanup-runs.ymlleaves a workflow whose only job is deleting runs, unable to delete runs.So this takes the other half of the remediation, and goes further than a comment
The scanner's suggestion is "drop it, or state which step requires it." Both files did already say so inline, which the rule doesn't read. What they also did was declare those scopes at the workflow level, where they apply to every job the file will ever contain.
They now sit on the single job that needs them, with the workflow level dropped to
contents: read— the shapecontainer-build.ymlalready uses in this repo. A job added to either file tomorrow starts read-only instead of silently inheriting a push token.That's the real finding underneath the false positive: not that the permission is excessive, but that its scope was.
Behaviour
Unchanged. The one existing job in each file resolves to exactly the permissions it had before — verified by parsing both files:
Whether the scanner is satisfied by a job-level grant is its business; least privilege at the workflow scope is worth having either way.
Also normalises a stray CRLF at the end of
dependabot-lockfix.yml, which.gitattributes(* text=auto eol=lf) already asks for.Also
dappercarries its owncleanup-runs.ymlwith the identical shape — same change in #143. It has nodependabot-lockfix.ymlby design:workflow_runfires only from the default branch's copy, and that one already serves Dependabot branches based on either branch.🤖 Generated with Claude Code
https://claude.ai/code/session_019h7znwAftvs36vD4YwKMRf
Generated by Claude Code