Skip to content

fix(global-settings): a hard deny beats every prompt; git aliases that push need the phrase (v2.7.3) - #813

Merged
WilcoLouwerse merged 1 commit into
mainfrom
fix/global-settings-hard-deny-before-ask
Oct 2, 2026
Merged

WilcoLouwerse merged 1 commit into
mainfrom
fix/global-settings-hard-deny-before-ask

Conversation

@WilcoLouwerse

Copy link
Copy Markdown
Contributor

Problem

ask() in block-write-commands.sh printed its prompt and exited. Every hard deny placed lower in the file was never reached for a chained command, so one approval of the first part also ran the rest:

Command v2.7.2 v2.7.3
gh pr create … && date -s 2020-01-01 prompt for gh denied (date -s)
rm build.log && npm ci --legacy-peer-deps prompt for rm denied (--legacy-peer-deps)
curl -X POST … && ln -s /tmp/x ~/.claude/settings.json prompt for curl denied (link to a config file)
docker compose up -d && bash /tmp/writes-claude-config.sh prompt for docker denied (script-body scan)
git -C repo commit -m x && cd /mnt/c / powershell.exe / wsl prompt for git -C denied (WSL boundary)
date -s … && gh pr create … (deny first) prompt for gh (file order, not command order) denied

v2.7.2 fixed this for git push only, by moving that one check up. This fixes the cause.

Fix

  • ask() only records its reason. Every guard runs, a hard deny anywhere wins, and the prompt is raised once at the end, with the first guard's reason, which is the one users saw before.
  • git push in more forms, without the phrase:
    • A git alias that resolves to push. The hook runs git config --get alias.<name> in the -C directory or the session cwd (cwd from the payload) and follows alias chains. Aliases that shadow a git command are ignored, as git ignores them.
    • Defining such an alias, e.g. git config alias.p push, git -c alias.p=push p, or an [alias] section written into .gitconfig / .git/config (heredoc included).
  • push must end the word, so git push-notes is no longer denied as a push.

Not covered, and documented in the README: config set via GIT_CONFIG_* env vars or --config-env, and a push inside a script an alias runs.

Tests

Suite Result
test-block-write-commands.sh 8672 / 8672
same suite against v2.7.2 8021 / 8672 (651 new checks fail, as expected)
test-block-config-tool-writes.sh 80 / 80
test-block-polling.sh 29 / 29
test-check-settings-version.sh 81 / 81

The new checks:

  • Ordering matrix: 29 prompting commands × 11 hard-denied commands, each chained &&, ; and deny-first.
  • Prompt behaviour: asks with the first reason, printed once.
  • Push word: push-notes, push;, (git push), $(git push).
  • Alias definitions: the forms listed above.
  • Alias resolution: against a fixture git config via GIT_CONFIG_GLOBAL, with and without the phrase. This covers chains, shell aliases, options inside an alias, a shadowed name, a repo-local alias and -C.

ShellCheck is not installed locally; CI runs it.

Version

VERSION 2.7.2 → 2.7.3. The README has a new section, A hard deny always beats a prompt (v2.7.3), and the push section covers aliases. docs/claude/global-claude-settings.md is updated to match.

🤖 Generated with Claude Code

…t push need the phrase (v2.7.3)

ask() printed its prompt and exited, so every hard deny placed below an
ask guard was never reached for a chained command. One approval of the
first part also ran `date -s`, `--legacy-peer-deps`, a link to a
~/.claude config file, a script that writes one, or a step out of WSL:
`gh pr create … && date -s 2020-01-01` only prompted for gh. ask() now
records the first reason and the prompt is raised at the end of the
hook, after every guard; the order of the parts no longer matters.

git push in more forms:
- a git alias that resolves to push (`git config --get alias.<name>` in
  the -C directory or the session cwd, alias chains followed, aliases
  that shadow a git command ignored);
- defining one: `git config alias.p push`, `git -c alias.p=push p`, an
  [alias] section written into a git config file;
- `push` must end the word, so `git push-notes` is no longer denied as
  a push.

Tests: every prompting guard chained with every hard-denied command in
three orders, ask-reason checks, push-word, alias definition and alias
resolution against a fixture git config. 651 of 8672 checks fail
against v2.7.2.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@WilcoLouwerse
WilcoLouwerse merged commit 54b4edb into main Oct 2, 2026
48 checks passed
@WilcoLouwerse
WilcoLouwerse deleted the fix/global-settings-hard-deny-before-ask branch October 2, 2026 15:03
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