[BUG] Built-in "dangerous rm" safety check is spelling-dependent: the MSYS /d/… form of a top-level path raises no approval gate
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
Claude Code's built-in "dangerous rm" safety check treats two spellings of the same on-disk location (per MSYS semantics, /d/x ≡ D:\x) differently: the Windows-form top-level path raises the approval gate; the Git-Bash/MSYS form raises no gate at all. The top-level-directory guard is therefore spelling-dependent — and the MSYS spelling bypasses even the human-approval prompt.
Verbatim predicate (from the bundled JS in claude.exe):
var Kne=/^[A-Za-z]:\/?$/, jne=/^[A-Za-z]:\/[^/]+$/, qne=/^[A-Za-z]:\//, Yne=/^[\\/]{2}(?:wsl\.localhost|wsl\$)[\\/]...
function ese(e){
if(e==="*"||e.endsWith("/*"))return true; // wildcard root
...; if(h==="/")return true; // "/"
if(Kne.test(h))return true; // drive root (D: / D:/)
...(home / canonical home / UNC spellings)...
if(jne.test(h))return true; // <- drive + EXACTLY ONE segment => top-level dir
return false;
}
The trigger is a conjunction (drive-letter form [A-Za-z]: AND a single segment). /d/… matches neither regex (no [A-Za-z]:), so ese returns false and the removal proceeds without any approval gate.
Coverage (measured + derived):
| form | gate raised? | basis |
|---|---|---|
| D:/<top> | yes (approval) | jne |
| D: / D:/ (drive root) | yes (approval) | Kne |
| / | yes | h === "/" |
| home (absolute) | yes | === home |
| /d/<top> (MSYS) | no | neither regex needs [A-Za-z]: |
| D:/a/b/c (deep) | no — except when it is/under the working dir | not critical-path; a separate workspace rule catches cwd/its ancestors |
| relative path | no — same workspace exception | same |
| ~ form | not verified | expansion timing outside the predicate |
| $VAR/x (possibly empty) | yes | a separate sibling rule |
Observed (what was run, not instructions to reproduce): the Windows-form spelling of a top-level directory was held for approval — verbatim: ...Only a person may approve such a removal, and no person did ... What was flagged: Dangerous rm operation detected: 'D:/hql-gateprobe'. The MSYS-form spelling of top-level directories, in the same session, ran to completion with no prompt (rc=0; the targeted directories were removed). ⛔ Do not re-run the MSYS form against any real path — it is ungated.
Note: the two runs used different target names; the equivalence /d/x ≡ D:\x is a property of MSYS that applies here — a same-name A/B was not run.
What Should Happen?
The critical-path test should reach the same verdict for equivalent spellings of the same location.
Suggested fix: normalize at the path-canonicalization layer that feeds this check, before the critical-path test — map the MSYS form (/^\/[a-z]\//) to the drive-letter form. The bundle already contains such a mapping — /^\/[a-z]\// matches 5 sites, 4 of them with the /i flag (e.g. if(O()==="windows"&&n.match(/^\/[a-z]\//i)) → DQ(n), DQ: /d/x → D:\x) — but ese does not call it. The goal is equivalent spellings ⇒ same verdict, not to widen/narrow which directories count as critical.
Blast radius: normalizing at the shared layer would also affect the sibling workspace/home checks (same path input); a narrower option is to canonicalize only for the critical-path test.
Precision note: DQ is a minified identifier in this bundle; its own body matches /^\/([A-Za-z])(\/|$)/ — slightly wider than the quoted regex. Key the fix on the existing canonicalizer at that layer, not on the quoted regex.
Error Messages/Logs
...denied by a built-in Claude Code safety check ... Only a person may approve such a removal, and no person did ... What was flagged: Dangerous rm operation detected: 'D:/hql-gateprobe'
Steps to Reproduce
The check inspects the command string before execution, so the two spellings can be compared without deleting anything: present the same non-existent top-level target once in each spelling and note whether the approval prompt appears — the MSYS spelling produces no prompt.
Deliberately omitted: the exact command lines. The MSYS spelling is ungated, so a pasted line could delete a real directory if the target happens to exist; the shape of the command is not needed to demonstrate the predicate above.
Claude Model
Other (a non-Claude model served through an Anthropic-compatible endpoint). This bug is in client-side bundled code and is model-independent.
Is this a regression?
I don't know
Last Working Version
(not tested on prior versions)
Claude Code Version
2.1.293 (Claude Code)
Platform
Other
Operating System
Windows
Terminal/Shell
Other (Git for Windows Bash — MSYS / MINGW64)
Additional Information
Related but different (found while searching; neither is this bug):
- #99193 — Windows: sub-agent
rm -rfon the 8.3 short-name alias of the home dir wiped ~116 GB. - #36339 — [Windows]
rm -rfon a pnpm project deleted entireC:\Usersvia NTFS junction traversal.
The vector reported here differs: the MSYS /d/… spelling of a top-level path is not matched by the critical-path predicate at all.
Environment: Windows 11 · shell = Git for Windows Bash (MSYS) · claude --version = 2.1.293.
Bundle coordinate: bin/claude.exe, size 256,155,808 B; function ese( @ byte 214486728; /^\/[a-z]\// ×5 (4 carry /i).
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗