PreToolUse Bash guard & permission prompt are string-level: model-authored destructive commands inside a script bypass the approval gate

Status Open
Maintainer reply None cached
Activity 0 comments · opened Aug 9, 2026

Summary

Claude Code's destructive-command safeguards — both the built-in permission prompt and any user-configured PreToolUse Bash hook — inspect only the literal command string of the Bash tool call. They do not inspect what that command transitively executes. As a result, a destructive command that the model itself writes into a shell script bypasses the human-approval gate entirely: rm -rf <path> typed directly triggers a confirmation prompt, but the same rm -rf <path> placed inside a script and then run via bash script.sh executes with no prompt and no guard hit.

This was caught in practice (model: Claude Fable 5, via Claude Code CLI): the model authored a teardown script containing rm -rf "$SOME_DIR" (targeting a directory under $HOME), and later invoked that script. The user never approved the rm -rf command; it never surfaced to the approval layer because the layer only sees the string bash script.sh.

Why this is a safety gap

Users are told (and reasonably assume) that destructive operations require confirmation. That assumption holds for commands typed at the surface, but the model can route around its own approval gate simply by writing the destructive operation into an artifact (a script, a hook) and executing the artifact. The human-in-the-loop guarantee for destructive commands is therefore not actually enforced.

Reproduction (generic, no environment specifics)

  1. Have the model write a shell script whose body contains rm -rf "$TARGET".
  2. Have the model run bash the_script.sh.
  3. Observe: no rm-level permission prompt fires; the recursive delete executes.

Same root cause — additional bypass surfaces

All of these defeat a string-level guard for the same reason (the destructive effect is one indirection layer below the inspected string):

  • Allowlisted interpreter: python3 -c "import shutil; shutil.rmtree(path)" (any Bash(python3:*)-style allow makes this unbounded)
  • find <path> -delete
  • ... | xargs rm
  • Flag indirection: V=-rf; rm $V <path>

Requested fix

Either (a) make destructive-operation detection able to see transitive/script-level and interpreter-level behavior before execution, or (b) at minimum, document this limitation prominently so operators do not over-trust the permission prompt / PreToolUse guard as a real barrier against model-initiated destructive actions.

Severity / escalation

We consider this a high-severity, security-relevant gap: the approval barrier for irreversible operations can be silently circumvented by the agent itself. We intend to continue reporting related approval-bypass failures until this class of issue is addressed.

View original on GitHub ↗