[MODEL] Major Data loss. Agent-constructed test payload with $(...) executed for real due to bash double-quote handling — rm -rf ~ ran against live home directory

Open 💬 3 comments Opened Jul 9, 2026 by robertemorgan

Preflight Checklist

  • [x] I have searched existing issues for similar behavior reports
  • [x] This report does NOT contain sensitive information (API keys, passwords, etc.)

Type of Behavior Issue

Other unexpected behavior

What You Asked Claude to Do

Summary: While drafting and self-testing a permission-hook script, Claude Code executed rm -rf against the user's real home directory (/Users/user1), deleting the contents of ~/Desktop and ~/Pictures and partially affecting ~/Music, with ~/Documents status unconfirmed. Data was not recoverable, local APFS snapshots (none existed), or iCloud (nothing to recover). Partial recovery was possible via OneDrive's web recycle bin.

What Claude Actually Did

Root cause: The agent was writing a test case to verify that a draft PreToolUse hook correctly detects and rejects dangerous command substitution ($(...)). To build the test's JSON input, it ran:

python3 -c "import json; print(json.dumps({'tool_input':{'command':'echo \"$(rm -rf ~)\"'}}))" | ./allow-readonly-compound.sh

The outer python3 -c "..." argument was wrapped in double quotes. Bash does not suppress $(...) command substitution inside double quotes (only single quotes do this). As a result, bash evaluated $(rm -rf ~) immediately — as a live subprocess — before python3 ever ran and before the string reached the hook script being tested. The "test data" was never inert; it was live shell code the moment the outer command line was parsed.

Compounding this: the agent chose ~ (home directory) as the target inside this adversarial example specifically because it needed to look like a realistic dangerous pattern for the detection logic to catch — rather than using a guaranteed-harmless target (e.g. a nonexistent path under /tmp) that would pose zero risk even if the quoting were wrong.

The command ran as a backgrounded long-running process and continued executing for ~21+ seconds before the agent noticed it in output and killed it.

Expected Behavior

testing within the projects directory that is version controlled.
not touching files or folders outside of the working directory
properly escaping code and not doing something so stupid.

Files Affected

the build of the systems home directory

Permission Mode

Accept Edits was ON (auto-accepting changes)

Can You Reproduce This?

Haven't tried to reproduce

Steps to Reproduce

I won't be trying this again

Claude Model

Not sure

Relevant Conversation

Summary: While drafting and self-testing a permission-hook script, Claude Code executed rm -rf against the user's real home directory (/Users/user1), deleting the contents of ~/Desktop and ~/Pictures and partially affecting ~/Music, with ~/Documents status unconfirmed. Data was not recoverable via Time Machine (destination unreachable), local APFS snapshots (none existed), or iCloud (nothing to recover). Partial recovery was possible via OneDrive's web recycle bin.

Root cause: The agent was writing a test case to verify that a draft PreToolUse hook correctly detects and rejects dangerous command substitution ($(...)). To build the test's JSON input, it ran:

python3 -c "import json; print(json.dumps({'tool_input':{'command':'echo \"$(rm -rf ~)\"'}}))" | ./allow-readonly-compound.sh

The outer python3 -c "..." argument was wrapped in double quotes. Bash does not suppress $(...) command substitution inside double quotes (only single quotes do this). As a result, bash evaluated $(rm -rf ~) immediately — as a live subprocess — before python3 ever ran and before the string reached the hook script being tested. The "test data" was never inert; it was live shell code the moment the outer command line was parsed.

Compounding this: the agent chose ~ (home directory) as the target inside this adversarial example specifically because it needed to look like a realistic dangerous pattern for the detection logic to catch — rather than using a guaranteed-harmless target (e.g. a nonexistent path under /tmp) that would pose zero risk even if the quoting were wrong.

The command ran as a backgrounded long-running process and continued executing for ~21+ seconds before the agent noticed it in output and killed it.

Contributing factors worth Anthropic's attention:
1. No apparent distinction in tooling between "constructing a string that merely represents a shell command for analysis" vs. "a string that will itself be shell-evaluated" — the agent had to reason about bash quoting semantics correctly by hand, with no structural safeguard catching the mistake.
2. A destructive command targeting $HOME was allowed to execute and run for many seconds as a background task without any additional friction, despite the agent's own Bash tool call having gone through normal permission resolution moments earlier for unrelated commands — i.e. permission-checking happened, but not on the actual command that ended up running, since the dangerous part was constructed via substitution rather than typed directly.
3. No confirmation step, rate limit, or destructive-path guard specifically for rm/similar against $HOME or its immediate children, independent of whatever permission mode is active.

Impact: Permanent loss of user files in ~/Desktop and ~/Pictures; partial loss in ~/Music; unconfirmed status of ~/Documents. Partial recovery achieved via OneDrive's recycle bin; no recovery available via iCloud or Time Machine.

Impact

Critical - Data loss or corrupted project

Claude Code Version

unknow i'ts delete claude from my system

Platform

Anthropic API

Additional Context

_No response_

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗