Skip to content

Fix rm hook bypass via inline shell and script execution#31

Open
yurukusa wants to merge 1 commit into
disler:mainfrom
yurukusa:fix/rm-hook-bypass-and-false-positive
Open

Fix rm hook bypass via inline shell and script execution#31
yurukusa wants to merge 1 commit into
disler:mainfrom
yurukusa:fix/rm-hook-bypass-and-false-positive

Conversation

@yurukusa

Copy link
Copy Markdown

Summary

Fixes #4 and #28 by rewriting is_dangerous_rm_command() in pre_tool_use.py.

Problem 1 — Bypass (#4): The hook only inspected the top-level command string for rm patterns. Wrapping rm in bash -c "rm -rf /", writing it to a script file, or using source/. completely bypassed detection.

Problem 2 — False positives (#28): The regex \brm\s+.*-[a-z]*r matched path components like -enrollment as recursive flags, blocking safe commands like rm /path/soft-hold-enrollment/file.rb.

Changes

  • Replace regex flag detection with shlex.split tokenization — flags are identified by token position, not substring matching. Path components like -enrollment are correctly treated as operands, not flags.
  • Detect inline shell wrappersbash -c "...", sh -c "...", zsh -c "...", dash -c "..." are parsed and the inline script is recursively checked.
  • Detect script file executionbash script.sh, sh script.sh, source script.sh, . script.sh, and ./script.sh trigger inspection of the script file contents. Fails open (allows execution) if the file doesn't exist or can't be read.
  • Handle chained commands&&, ||, ;, | separators are split so echo hi && rm -rf / is caught.
  • Respect -- end-of-optionsrm -- -file-starting-with-dash correctly treats the operand as a filename.

Test plan

  • Direct rm -rf / — blocked
  • bash -c "rm -rf /" — blocked (was bypassing before)
  • sh -c "rm -r ~" — blocked (was bypassing before)
  • Script file containing rm -rf / executed via bash script.sh — blocked (was bypassing before)
  • source script.sh and . script.sh with dangerous rm — blocked
  • echo hi && rm -rf / — blocked
  • rm /path/soft-hold-enrollment/migrate/foo.rb — allowed (was falsely blocked before)
  • rm file1.txt file2.txt — allowed
  • bash -c "echo hello" — allowed
  • bash nonexistent-script.sh — allowed (fail open)

Replace regex-based flag detection with shlex.split tokenization to
properly distinguish flags from path components (fixes disler#28), and add
detection for indirect rm execution patterns (fixes disler#4):

- bash/sh/zsh/dash -c "rm -rf /" (inline shell wrappers)
- bash/sh script.sh where the script contains dangerous rm commands
- source script.sh and . script.sh patterns
- Chained commands via &&, ||, ;, and |

The old regex approach matched substrings like '-enrollment' inside
path names as recursive flags, causing false positives. The new
token-based approach only interprets actual option tokens as flags.

Script file inspection fails open (allows execution) when the file
doesn't exist or can't be read, avoiding false blocks on legitimate
commands.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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.

Hook bypass: rm commands work when inside shell scripts or inline command

1 participant