[BUG] Auto mode ran an unrequested wildcard `rm` in a user directory and deleted user files with no confirmation

Status Open
Reported on v2.1.154
Maintainer reply None cached
Activity 7 comments · opened Jun 1, 2026

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?

In a long session running in "auto" permission mode, Claude Code self-initiated a "cleanup" step that was never requested and ran a wildcard delete of the form rm -f <dir>/*<substring>*.md inside a user working directory. The wildcard matched and permanently deleted several pre-existing user files unrelated to the task — their names merely contained the matched substring. No confirmation prompt was shown before the destructive wildcard rm executed in auto mode. Some files were recoverable from other copies; a subset were permanently lost.

Separately, in the same session, the assistant repeatedly claimed a generated visual deliverable (PDFs) was "fixed/done/verified" without ever inspecting the rendered output, while obvious rendering defects persisted across multiple iterations.

What Should Happen?

A destructive wildcard delete (rm/rmdir) targeting a user directory should require an explicit confirmation gate even in "auto" permission mode, and the assistant should never self-initiate deletion of files it did not create. Completion claims about a rendered/visual artifact should require the artifact to actually be inspected before being reported as done.

Error Messages/Logs

None. `rm -f` succeeded silently with no error; the deletion was only discovered afterward via a directory listing.

Steps to Reproduce

Issue 1 (destructive delete):

  1. Run Claude Code in "auto" permission mode in a long session that writes files into a working directory (e.g. ~/Downloads).
  2. Have it create temporary files alongside pre-existing user files whose names share a common substring (e.g. both contain "copy").
  3. During an assistant-initiated "cleanup", it runs a wildcard delete such as rm -f <dir>/*copy*.md intended only for its own temp files.
  4. Observed: the wildcard also deletes the user's matching pre-existing files, with no confirmation prompt in auto mode.

Issue 2 (false completion claims):

  1. Have the assistant generate a PDF/visual artifact via a headless renderer.
  2. Observe it report "fixed/done" without using its image-inspection step, while a visible rendering defect remains.

Claude Model

Opus

Is this a regression?

I don't know

Last Working Version

_No response_

Claude Code Version

2.1.154 (Claude Code)

Platform

Anthropic API

Operating System

macOS

Terminal/Shell

Terminal.app (macOS)

Additional Information

Root cause (assistant's own account): a bias toward producing a resolved-looking end state over verifying the actual one — which drove both the unrequested "cleanup" delete and the premature "done" claims.

User-side mitigations that were required to actually constrain it (text rules in the project instruction file / memory did NOT bind behavior under load):

  • permissions.deny for Bash(rm:) and Bash(rmdir:)
  • a Stop hook that blocks ending a turn when a visual artifact is claimed done without being inspected, or a generated file is named without a link.

Suggested product fixes: a built-in confirmation gate for wildcard rm/rmdir in user directories even in auto mode; stronger coupling between completion claims and verification of visual artifacts.

claude-code-incident-report.md

View original on GitHub ↗

7 Comments

github-actions[bot] · 3 months ago

Found 3 possible duplicate issues:

  1. https://github.com/anthropics/claude-code/issues/6608
  2. https://github.com/anthropics/claude-code/issues/61519
  3. https://github.com/anthropics/claude-code/issues/55205

This issue will be automatically closed as a duplicate in 3 days.

  • If your issue is a duplicate, please close it and 👍 the existing issue instead
  • To prevent auto-closure, add a comment or 👎 this comment

🤖 Generated with Claude Code

otmanm · 3 months ago

Not a duplicate of the three linked issues, though adjacent. The distinction is the failure mode:

The cited issues are all targeted destructive commands:

  • #6608 and #55205 — rm -rf <specific path>
  • #61519 — rm -rf ~ (unquoted tilde expansion)

This report is a wildcard/glob delete of the form rm -f <dir>/*<substring>*.md. The problem isn't a known-dangerous literal command running — it's that a glob silently matched and deleted pre-existing user files the model never named, never intended, and could not enumerate in advance. The blast radius of a wildcard isn't predictable from the command text, so "the model only meant to delete its own temp files" still produced collateral deletion of unrelated user data. None of the three linked issues involve glob expansion or collateral matching.

Also: #61519 and #55205 are themselves already closed as duplicates, so they can't anchor a dedup; #6608 is about a permission-allowlist bypass for a specific command, not wildcard collateral damage.

Please keep open (or relabel as the wildcard/glob-collateral variant). The concrete fix this case argues for, beyond "confirm before rm -rf": a confirmation gate specifically for wildcard/glob deletes targeting user directories, since the model cannot predict what a glob will match. Auto mode executed this with no confirmation.

yurukusa · 2 months ago

You've isolated the failure mode precisely — the problem isn't a known-dangerous literal command, it's that a glob's match set can't be enumerated in advance, so an "only my temp files" delete silently takes pre-existing files that merely share the substring. A few notes in case they help before a built-in gate exists.

Why permissions.deny Bash(rm:*) works but tends not to stick: it's all-or-nothing — it also blocks every legitimate rm dist/*.js / rm *.pyc, so it gets relaxed or removed under friction, and then you're unprotected again. A PreToolUse hook can be surgical: allow targeted/relative deletes, gate only the unpredictable-blast-radius case (a glob reaching into a home/absolute path).

The part that actually matters for "even in auto mode": a PreToolUse hook is a separate layer from the permission prompt. Auto mode auto-approves the prompt, but the hook still runs and a non-zero exit still blocks the call. I verified this on 2.1.162: with --permission-mode auto, a PreToolUse Bash hook that exit 2s fires and the command never executes (Claude reports it was blocked). So a hook is the confirmation/refusal gate you're asking for, and unlike a text rule in memory it binds under load because it's enforced outside the model.

Minimal, self-contained hook for exactly this glob-collateral case (no dependencies beyond jq):

#!/bin/bash
# ~/.claude/hooks/wildcard-rm-gate.sh
# Blocks wildcard/glob rm reaching into a home/absolute path (unpredictable match set).
# Bare relative globs (rm *.pyc, rm dist/*.js) and explicit single-file deletes pass.
INPUT=$(cat)
CMD=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
[ -z "$CMD" ] && exit 0
echo "$CMD" | grep -qE '^\s*(sudo\s+)?rm\s' || exit 0
TARGET=$(echo "$CMD" | grep -oP 'rm\s+[^;|&]*' | awk '{print $NF}')
if echo "$TARGET" | grep -qE '[*?[]' && echo "$TARGET" | grep -qE '^(/|~)'; then
  echo "BLOCKED: wildcard rm on a user/absolute path — a glob can delete files you never named: $TARGET" >&2
  echo "List the matches first, then delete explicit names:  ls -d $TARGET" >&2
  exit 2
fi
exit 0
// settings.json
{ "hooks": { "PreToolUse": [
  { "matcher": "Bash", "hooks": [ { "type": "command", "command": "~/.claude/hooks/wildcard-rm-gate.sh" } ] }
] } }

Verified behavior (exit 2 = blocked, exit 0 = allowed):

| command | result |
|---|---|
| rm -f ~/Downloads/*copy*.md | blocked |
| rm /Users/me/Documents/*draft*.md | blocked |
| rm *.pyc / rm -f dist/*.js / rm -rf node_modules | allowed |
| rm ~/project/specific-file.md (explicit, no glob) | allowed |

This is deliberately narrow — it gates only the glob-into-user-dir pattern you hit, so day-to-day cleanup keeps working and you're less tempted to disable it. Tune the path scope to taste (e.g. add safe build dirs, or also gate bare relative globs if your sessions run in mixed user dirs).

(For completeness: I maintain a free MIT collection of these guards including a fuller rm-safety-net.sh that also covers find -delete / shred and treats ~, /home, /Users etc. as protected — github.com/yurukusa/cc-safe-setup. The snippet above stands alone, though.)

yurukusa · 2 months ago

Worth flagging for anyone weighing the dup-closure: your exact failure mode was just independently measured. A stress-test of auto mode (arXiv:2604.04978, Measuring the Permission Gate) reports that 36.8% of state-changing actions fall outside the classifier's scope entirely — specifically in-project file edits — because "auto mode assumes dangerous actions transit the shell, but agents routinely achieve equivalent effects through file edits that the classifier does not evaluate." Their worst-scoring category is artifact cleanup at a 92.9% false-negative rate, with an 81.0% end-to-end FNR (vs the ~17% on production traffic).

That's this issue, quantified: an assistant-initiated cleanup whose blast radius the gate couldn't see. It's not an edge case — it's the measured center of the coverage gap, which argues for keeping this open as the wildcard/glob-collateral variant rather than folding it into the rm -rf <literal path> reports. The practical takeaway lines up with what you already found: the durable fix is an operator-side gate on the edit/delete surface, not just shell, since that surface is where most of the misses are.

otmanm · 2 months ago

Yuru, thank you. You did not have to reply, and you explained the real cause better than I could: the wildcard never enumerated and checked its matches before deleting. That was the missing piece.

I installed cc-safe-setup and built the layered setup around it. Backups first, one working folder with an enforced boundary, your guard at the door, and the habit of reasoning back through every run. The guard already caught a dangerous command shape that an all-or-nothing rule would have either missed or strangled.

I also wrote the whole thing up publicly, for a general audience , and credited you and cc-safe-setup by name. The point I made is the one you proved: you do not get to outsource your safety to the lab, the discipline has to be yours, and the people who get the design right are often not the ones with the biggest budget.

If you are open to it, I would like to keep comparing notes. I am working on the edit surface case now, where an agent changes file contents rather than deleting them, which a command guard does less for. If you have thoughts there, I am all ears. Either way, thank you for making my setup stronger.

@yurukusa どうもありがとう(Dōmo arigatō)
Otman

BGMLAI · 1 month ago

An unrequested wildcard delete in a user directory should cross two independent boundaries: intent mismatch (the user did not ask for cleanup) and target-scope risk (glob expansion outside a disposable build directory). Auto mode should not be able to waive both.

A useful rule is to inspect the resolved target set, not just the literal command. Wildcards, home-directory targets, and paths outside the current worktree should raise the minimum review level even when the verb itself was previously allowed.

Disclosure: I maintain gate.cat, an open-source local action veto. We built it to provide this deterministic floor, alongside the stronger controls of worktrees, backups, and a dedicated agent account.

Ar9av · 1 month ago

Self initiated cleanup is the part worth flagging separately from the wildcard match itself, the destructive command guard question usually assumes the agent is doing what you asked, this was an unrequested action in the middle of an unrelated task. On the pattern matching side, a substring wildcard like rm -f dir/substring.md against an existing user directory is exactly the kind of thing a fixed sensitive-path guard (immunity-agent's default rule, github.com/PrismorSec/prismor) would not catch either, since the target directory isn't a system path, it's an ordinary user working directory, which is outside that rule's scope by design. Worth being honest that most destructive-command guards today protect against a short list of catastrophic shapes, not against a locally scoped wildcard delete like this one.