[BUG] rm -rf with unexpanded ${LOCALAPPDATA} in bypassPermissions deletes real system directory
Description
Claude Code (v2.1.84, Opus 4.6, VS Code extension) executed rm -rf on what it thought was a local project folder named ${LOCALAPPDATA}/. Bash expanded the variable to the real Windows system directory AppData\Local, deleting 50+ application folders.
This is a supply-chain failure between two AI agent sessions — distinct from NTFS junction traversal (#36339, #29249) or variable folder creation alone (#13138).
Root Cause Chain
Stage 1 — C# bug creates the booby-trap (Session A):
Session A was building an MCP server framework. Its C# SecurityPolicyEngine had config defaults like "${LOCALAPPDATA}\PAC-X\logs". An ExpandPath() method existed but was only called when loading a config file. When no file existed (the default case), Directory.CreateDirectory() used the literal string, creating a real folder named ${LOCALAPPDATA}/ in the working directory.
Stage 2 — MCP server plants it in client workspace (cross-session):
Session B was building a plugin that USES Session A's MCP server as a client. The MCP server launches AutoCAD, which sets its working directory to Session B's project folder. The literal ${LOCALAPPDATA}/ folder was created there.
Stage 3 — Client AI triggers the trap (Session B):
git status in Session B showed ?? ${LOCALAPPDATA}/ as untracked. Session B's AI, cleaning up the project, included it in a batch delete:
rm -rf build_out/ pacx_debug/ tests/dwg/ "${LOCALAPPDATA}/"
Bash expanded "${LOCALAPPDATA}" to C:\Users\<user>\AppData\Local, deleting the entire directory.
Stage 4 — Server AI diagnoses and repairs (Session A):
Session A (still running, unaware of the destruction) investigated, found the C# bug in code it had written, fixed it, backed up surviving data, and assisted with recovery. Same model, same day.
Environment
- OS: Windows 11 (10.0.26100)
- Claude Code: v2.1.84 (VS Code extension in Cursor)
- Model: claude-opus-4-6
- Shell: Git Bash
- Permission mode: bypassPermissions
Impact
- 50+ folders deleted from AppData\Local
- Applications installed in Local\Programs destroyed (Cursor, VS Code — both needed reinstall)
- Browser local data, Docker data, dev tool caches all gone
- Only ~17 folders survived (locked by running processes)
- AppData\Roaming was NOT affected
The Safety Gap
Claude Code does not validate rm -rf targets after shell expansion. The AI treated ${LOCALAPPDATA}/ as a literal local folder name. Three failures:
- No expansion check: Arguments containing
${},~, or%VAR%are not resolved before execution to detect dangerous targets. - No system path blocklist: Paths resolving to AppData, Users, Program Files, home directories, etc. are not blocked from
rm -rf. - bypassPermissions too permissive: Even in bypass mode, destructive operations on system directories should require confirmation.
Recommendations
- Pre-expansion validation: Before executing any
rm/rmdircommand, resolve shell variables and check if the resulting path is inside the workspace. If not, refuse with a clear error.
- System directory blocklist: Hard-block
rm -rfon paths matching known system directories (AppData,Users,Program Files,Windows,System32,$HOME,~/, etc.).
- Bypass mode scoping:
bypassPermissionsshould mean "trust me with project files", not "trust me with my entire filesystem". System-level destructive ops should always confirm.
- git status warning: Entries containing
${},%VAR%, or~likely represent unexpanded environment variables from buggy code — flag them as suspicious before cleanup.
Reproduction
- In a git repo on Windows with Git Bash:
``bash``
mkdir '${LOCALAPPDATA}'
git status # shows: ?? ${LOCALAPPDATA}/
- Run Claude Code with bypassPermissions
- Ask it to "clean up untracked files" or "remove build artifacts"
- Observe that it may include
"${LOCALAPPDATA}/"inrm -rf— bash expands it to the real system path
Why This Is Not a Duplicate
| Issue | Root cause | This issue |
|-------|-----------|------------|
| #36339 | NTFS junction traversal in pnpm | No junctions — shell variable expansion |
| #29249 | NTFS junction traversal in pnpm worktrees | No junctions — shell variable expansion |
| #13138 | Folders created with literal variable names | Same C# symptom, but #13138 doesn't address the Claude Code rm -rf safety gap |
The core request — pre-expansion validation of rm -rf targets — is not covered by any existing issue.
This issue has 5 comments on GitHub. Read the full discussion on GitHub ↗