Inline-shell rm check (2.1.288) asks on zsh -c scripts that contain no rm
Summary
Claude Code 2.1.288 added a check that looks inside sh -c / bash -c scripts for dangerous rm (#96300). It also applies to zsh -c, and it appears to parse the script as bash. When a zsh -c script evaluates a command that uses ordinary zsh syntax, the check asks:
This shell -c script runs rm and could not be checked
The script contains no rm. This prompt cannot be approved by a permission rule, so it stops auto mode and accept-edits mode, and an unattended session denies it after the timeout.
What happens
Claude Code 2.1.288, macOS, zsh 5.9.2. Each row is one Bash tool call.
| Command | Result |
|---|---|
| zsh -c 'eval "$1"' _ '[[ 12 == <-> ]] && echo num' | asks |
| zsh -c 'eval "$1"' _ 'echo num' | runs |
| zsh -c '[[ 12 == <-> ]] && echo num' | runs |
| bash -c 'eval "$1"' _ '[[ 12 =~ ^[0-9]+$ ]] && echo num' | runs |
<-> is zsh's numeric glob (matches any non-negative integer). It is not valid bash.
The same happens when a PreToolUse hook wraps every Bash command as zsh -c '…; eval "$1"' _ '<command>' (a common way to run commands in a configured zsh). Through such a wrapper we also saw:
| Wrapped command | Result |
|---|---|
| [[ 12 == <-> ]] && print num | asks |
| print -r -- '.{0,600}x' (single quote + brace pattern) | asks |
| print -r -- ".{0,600}x" | runs |
| print -r -- a{1,2} | runs |
| print -r -- $'x' | runs |
| print -r -- rm | runs |
The word rm plays no part: the same rows ask with or without it.
Expected
A zsh -c script is not judged by bash grammar. Either it is parsed as zsh, or a failure to parse zsh-only syntax does not by itself lead to "runs rm".
Why it matters
- zsh is the default shell on macOS, and
zsh -cwith zsh syntax is ordinary. - The prompt says the script runs
rm. A user approving it is told something that is not true of the script. - No permission rule can approve it, so a session that wraps commands in zsh is interrupted on ordinary commands with no supported way to say they are fine.
Not claimed
- Which internal branch fires.
- Why a quoted brace pattern asks through the wrapper, although quoted
{0,600}is valid bash.
Related: #96300.