Replace Bash(git *) blanket allow with an explicit git allowlist#208
Draft
ikuwow wants to merge 1 commit into
Draft
Replace Bash(git *) blanket allow with an explicit git allowlist#208ikuwow wants to merge 1 commit into
ikuwow wants to merge 1 commit into
Conversation
Replaces the catch-all "Bash(git *)" with an enumerated allowlist of safe read-only subcommands plus the routine writes the daily workflow needs (add, push, pull, fetch, checkout -b, switch, restore --staged, stash push/pop/apply, worktree add). Everything else falls through to the default ask (config writes, merge, rebase, reset, cherry-pick, revert, apply, am, rm, mv, branch -D, tag -d, worktree prune/move, inline -c key=val, and any unknown subcommand) — including the four arbitrary-code-execution vectors the issue flagged: git config core.pager / core.editor / core.sshCommand / core.hooksPath. Closes the deny gaps identified in #187: git push --delete, git push --mirror, the colon-prefix branch-delete refspec, and single-file git checkout -- <file>. Prunes the git ask claw-backs that became redundant once "Bash(git *)" was gone (merge, rebase, commit --amend, reset, clean, stash drop/ clear, branch -D, config, worktree prune/move/lock/unlock). They now default to ask via the same fallthrough that catches every other non-allowed subcommand. Closes #187. Co-authored-by: Claude Opus 4.6 <[email protected]>
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.
Closes #187. Picks the recommended approach from the issue (read-only allowlist + routine writes guarded by deny).
Why
Bash(git *)auto-approved every git subcommand, including the four ACE vectors the issue lists (git config core.pager/core.editor/core.sshCommand/core.hooksPath, plus inlinegit -c ...),git apply/amagainst untrusted patches, and any unknown or future subcommand. It also blocked the legitimate auto-approve ofgit config --getbecause thegit config *ask rule shadowed it. Both problems come from the same root: the blanket allow.What
Bash(git *)is removed. In its place: the enumerated read-only allowlist from the issue (status / log / show / diff / blame, branch reads by flag, tag / remote reads by flag, rev-parse family, ls-files / ls-tree / ls-remote / for-each-ref / cat-file, shortlog / grep / cherry / count-objects / fsck, verify-commit/tag, whatchanged, reflog,git config --get*family, stash list / show, worktree list).allow:git add *,git push/push *,git pull/pull *,git fetch*,git checkout -b *,git switch *,git restore --staged *,git stash/stash push/pop/apply,git worktree add *. Their destructive variants stay denied (or are added to deny — see below).git push --delete,git push --mirror,git push * :*(the colon-prefix branch-delete refspec),git checkout -- *(single-file discard).git *was gone are removed:git merge *,git rebase *,git commit --amend*(4 entries),git reset *,git clean *,git stash drop *,git stash clear *,git branch -D *(2 entries),git config *,git worktree prune */move */lock */unlock *. They all fall through to the default ask via the same path that catches every other non-allowlisted git subcommand.Notes
git commitis hook-approved byclaude/hooks/approve_git_gh_commands.pyindependent of these rules, so removinggit commit --amendfrom ask does not regressgit commititself; an--amendwill land in the default ask path along with the rest of the unsafe subset.git push * :*over-matches: it also blocksgit push remote master:main(a legitimate refspec push). That is acceptable for the personal-dotfiles use case — colon-style pushes are rare here, and the alternative is to leave the gap open.Bash(git config --get*)now auto-approves whilegit config user.email <value>(write) falls into ask — the concrete symptom in the issue is resolved.claude/settings.jsonis symlinked into~/.claude/, so this affects every project. The allowlist was sized to cover routine cross-project usage; if a workflow ever needs an additional subcommand auto-approved, add it explicitly rather than restoring the blanket allow.Verification
jq . claude/settings.jsonvalidates.git config core.pager,core.editor,core.sshCommand,core.hooksPathwrites do not match any allow pattern (allowlist is limited togit config --get*family).git -c core.pager=<cmd> statusdoes not matchBash(git status*)because the prefix requires literalgit status, notgit -c .... Both vectors are closed.git push * --force *and the newgit push * :*precede their allow counterparts in evaluation order per the Claude Code permission precedence (deny→ask→allow).git status/git log/git diff/git add <file>/git push/git pull/git checkout -b foo/git switch foorun without new prompts.git config --get user.emailauto-approves.git config user.email <x>prompts.git push --delete origin foo,git push origin :foo,git checkout -- file.txtare denied.git push --forcestill denied.git commitstill auto-approved by the hook.