Permission rules should (optionally) evaluate against the original input when a PreToolUse hook returns updatedInput

Open 💬 1 comment Opened Jun 4, 2026 by sunfmin

Use case

A PreToolUse hook that transparently wraps Bash commands in a stall-detection watchdog via hookSpecificOutput.updatedInput — e.g. rewriting npm test into /path/to/stall-guard -c 'npm test'. The wrapper is semantically transparent: same command, same exit codes, plus inactivity detection.

Problem (verified on 2.1.162)

Permission rules are evaluated against the rewritten command, so installing such a hook silently breaks the user's permission configuration:

  1. Allow rules stop matching. With "allow": ["Bash(touch:*)"] in .claude/settings.local.json: unwrapped touch a.txt is auto-allowed; the same command rewritten by the hook gets "This command requires approval". Every allowlist entry the user has accumulated starts prompting again.
  2. Deny rules degrade to ask. A Bash(rm -rf:*) deny rule doesn't match the wrapped command, so instead of a hard block the user gets a confirmation prompt.

Ask — either would solve it

  1. An opt-in field on the PreToolUse response, e.g. "evaluatePermissionsAgainst": "original", so rule evaluation (allow/deny/ask) runs against the pre-rewrite input while the rewritten command is what executes (and is shown in the prompt); or
  2. User-configurable transparent wrappers for Bash rule matching — the docs already strip a fixed list (timeout, time, nice, nohup, stdbuf); a setting like "permissions": {"transparentWrappers": ["/path/to/stall-guard -c"]} would generalize the existing mechanism.

Current workaround

The hook re-implements the documented rule-matching semantics against the original command and emits permissionDecision: "allow"/"deny" alongside updatedInput (verified working). It's viable but duplicates the permission matcher, with obvious divergence risk — and rule sources the hook can't read (CLI --allowedTools, session-scoped always-allow) stay broken.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗