fix: keep gh api auto-approval read-only in Claude Code settings - #4558
fix: keep gh api auto-approval read-only in Claude Code settings#4558dennishavermans wants to merge 3 commits into
Conversation
The allow-list approves Bash(gh api repos*) so the review-pr skill can fetch PR comments, reviews, and commits. A prefix rule cannot see the HTTP method: measured on Claude Code 2.1.238, the rule as written auto-approves `gh api repos/<owner>/<repo> -X DELETE`, `... -f description=x` (gh switches GET to POST the moment a parameter is added), and `... /git/refs/heads/main -X DELETE`. The deny list already blocks `git reset --hard` and `rm -rf`, so remote state was clearly not meant to be less protected than local state. Adds a PreToolUse guard in the same shape as guard-protected-files.sh: reads pass through untouched (the skill's three documented calls still auto-approve; verified against a real PR), while -X/--method and the parameter flags that imply POST exit 2 with an explanation. The flag scan runs on the raw hook input rather than an extracted string, so a quote inside the command cannot hide a flag from the guard. Signed-off-by: Dennis Havermans <dennishvrmans@gmail.com>
PR SummaryLow Risk Overview
Reviewed by Cursor Bugbot for commit 4032ef2. Bugbot is set up for automated code reviews on this repo. Configure here. |
Bugbot is right: gh accepts -iX DELETE and a quoted "-X" DELETE as readily as -X DELETE — measured, --verbose shows the DELETE request line for each — and the space-anchored patterns saw neither. Long flags are now matched as substrings, since the shell strips the quotes but the raw hook input keeps them, and --method is --method either way. Short flags are matched as clusters: any short group containing X, f, or F blocks, anchored so a path segment like r-Xtra stays innocent. Re-ran the battery: twelve write spellings block, the review-pr reads pass, including one carrying eight harmless flags. Signed-off-by: Dennis Havermans <dennishvrmans@gmail.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Reviewed by Cursor Bugbot for commit f9f4653. Configure here.
pflag, the option parser gh is built on, documents three value spellings for a short flag: separate (-f key=value), equals (-f=key=value), and attached (-fkey=value), each also valid at the end of a boolean cluster (-ifkey=value). The guard required a non-letter after f/F, so the attached spellings passed as reads while gh made a POST: measured with --verbose, -fkey=value, -Fkey=value, and -ifkey=value each show the POST request line. gh even reads -friendly as -f riendly and answers "invalid key: riendly". The f/F pattern now matches any short cluster containing f or F with no trailing constraint, the same shape as the -X pattern beside it. The battery grows to 36 cases: 30 write spellings block, covering every pflag-documented form of -X, -f, and -F, the long flags with and without equals, quoted spellings, and clusters; 6 reads pass, including the three commands the review-pr skill documents. Verified end to end through Claude Code: the read executes, the attached-form write is blocked by the hook. Signed-off-by: Dennis Havermans <dennishvrmans@gmail.com>

Summary
Bash(gh api repos*)in.claude/settings.jsonwas added so thereview-prskill can fetch PR comments, reviews, and commits, and it sits in a block of otherwise read-onlyghrules. A prefix rule cannot see the HTTP method, so the same rule also auto-approves writes. This PR adds a PreToolUse guard, in the same shape as the existingguard-protected-files.sh, that letsgh apireads through untouched and blocks anything that changes the request method.Everything below was measured on Claude Code 2.1.238 before opening this PR (each rule set in a scratch
settings.jsonpassed via--settings, rawstream-jsontranscripts, probes against a repo name that does not exist so the API returns 404 and nothing changes):gh api repos/linuxfoundation/crowd.dev/pulls/4548/comments --paginategh api repos/<owner>/<repo> -X DELETEgh api repos/<owner>/<repo> -f description=xgh api repos/<owner>/<repo>/git/refs/heads/main -X DELETETwo details worth knowing:
-X: gh's own help says "adding request parameters will automatically switch the request method to POST", so any-f/-Fride-along turns an approved read into a write.gh auth logintoken (reposcope, nodelete_repo), repository deletion itself would fail at the API. What thereposcope does cover through this rule: deleting branches viagit/refs, editing repository settings via PATCH, and deleting comments and releases.The deny list already blocks
git reset --hard*andrm -rf*, so this brings remote state in line with the protection local state already has.Changes
.claude/hooks/guard-gh-api.sh: blocks-X/--methodand the parameter flags that imply POST (-f,-F,--field,--raw-field,--input) ongh apicommands; exits 2 with an explanation. The flag scan runs on the raw hook input rather than an extracted command string, so a quote inside the command cannot hide a flag from the guard (unit-tested against a--jq '"' -X DELETEsmuggle)..claude/settings.json: wires the guard intoPreToolUsewith aBashmatcher, next to the existingEdit|Write|MultiEditguard.The
review-prskill's three documentedgh apicalls (comments, reviews, commits) were re-run under the guarded config and still auto-approve.Type of change
JIRA ticket
n/a (external contribution; happy to reference one if a maintainer files it)
Notes for the reviewer
One deliberate trade-off: the guard blocks
gh apiwrites outright rather than downgrading them to an approval prompt, matching the hard-deny style of your deny list. If you would rather keep human-approvable writes in interactive sessions, the exit-2 block can be swapped for a hook JSONpermissionDecision: "ask"; I kept the measured, simpler contract. An alternative to all of this is deleting theBash(gh api repos*)line and letting the three skill calls prompt each review session; the hook keeps the skill friction-free, which seemed closer to the intent of #4122.Found while testing a static analysis tool for agent configuration files; every claim above was then verified by hand against Claude Code before this PR was opened.