Bash safety check says a `sh -c` script "runs rm" when it does not (only with a `cd … &&` prefix)
Summary
The Bash safety check reports "This shell -c script runs rm" for a sh -c script that contains no rm at all, as soon as the command starts with cd <dir> &&. The same command without the cd prefix passes. In bypass-permissions mode this still opens a permission prompt (the dangerous-removal checks are not auto-allowed), so the user is asked to approve an "rm" that does not exist.
Environment
- Claude Code 2.1.295 (native install), macOS 26.x (Darwin 27.2.0), zsh
- Reproduced in
-pmode on an unmodified install and interactively (prompt text quoted below)
Reproduction
Headless, with permissions bypassed so only the safety check can intervene:
claude -p --dangerously-skip-permissions --output-format stream-json --verbose \
"Run exactly this Bash command, verbatim, then reply with one word: cd /tmp && sh -c 'echo \"\$0\"' /etc/hosts"
Tool result (is_error: true):
This command passes a shell -c script that runs rm, and Claude Code could not check the script for dangerous removals. Approve only if you have read the script.
Interactively the prompt reads:
This shell -c script runs rm and could not be checked
⚠ Claude Code will automatically deny this request in 1:49, to avoid blocking progress on an unattended session
Do you want to proceed?
Same result for cd ~ && sh -c 'echo "$0"' /etc/hosts and for longer pipelines ending in xargs -I{} sh -c '…' "$file".
These pass with no prompt:
sh -c 'echo "$0"' /etc/hosts
printf 5 | xargs -I{} sh -c 'sed -n "$(({}-1))p" "$0"' /etc/hosts
sh -c 'echo hi | python3 -c "import sys; print(sys.stdin.read())"'
So the cd <dir> && prefix is what flips the verdict, and the script that gets flagged is echo "$0".
Expected
Either no prompt (the script is echo "$0"), or a prompt that says what the check could not verify. A prompt that states the script "runs rm" when it does not is misleading, and it trains users to click through the real rm warnings.
Notes from the bundle
The inline-shell check (sh -c/bash -c scripts) has one fallback result whose text is fixed to "passes a shell -c script that runs rm … could not check the script for dangerous removals", returned from several branches (parse failure of the inner script, nesting depth, the $'…' escape case) whether or not any heuristic saw an rm. With the cd … && prefix the command goes through a different branch than the bare sh -c form, which is why only the prefixed form reaches that fallback. The message should say which check failed, and only mention rm when the script contains one.