sh -c/bash -c Dangerous-RM Bypass Is Fixed, Plus 30+ Permission, Plugin, and Resume Hardening Changes Across the Same Two-Day WindowOriginally published: 2026-10-04 12:13 UTC / 14:13 Berlin / 08:13 EDT Last verified: 2026-10-04 12:13 UTC (GitHub anthropics/claude-code releases.atom fetch + GitHub issue #96300 verification) No corrections at this time.
Two Claude Code stable releases landed within 17 hours, and the second one closes a real bypassPermissions-mode permission bypass that a third-party reporter had publicly disclosed and that Anthropic had already partially addressed in v2.1.288.
rm (such as one on / or the home directory) inside a bash -c or sh -c script running without a prompt in bypassPermissions mode or under a shell allow rule (#96300)."agent.spawn for teammates and tightens nine additional permission, plugin, and shell-rule holes. Most relevant for anyone running unattended agents: a fix that prevents a user-installed plugin from rewriting the descriptions of an organization-managed MCP server's sign-in tools.The underlying bug report — [anthropics/claude-code#96300](https://github.com/anthropics/claude-code/issues/96300), titled "Dangerous-rm check does not look inside sh -c / bash -c" — was filed before the v2.1.288 cut and is the canonical primary source for the gap. Anthropic's release notes cite the issue number directly, so we are not relying on a single unverified social-media post. The bug reporter's reproduction script and outputs are in the public issue body, and Anthropic's changelog acknowledges the gap and credits the issue by ID.
The headline fix is the sh -c / bash -c unwrap that closes the dangerous-rm bypass. The pre-2.1.5 logic looked at the top-level Bash command, unwrapped subshells, brace groups, and command substitutions, then asked "is the program rm or rmdir?" The sh -c "rm -rf X" and bash -c 'rm -rf X' forms put sh/bash in the program slot and hide rm inside a string argument, so the check never saw it. The fix treats the string passed to -c as a new command to inspect, so the inner rm lands back in the path the dangerous-rm check actually understands.
The reproduction table from issue #96300 (each row ran 3–5 times; result reproduced every time):
| Command | Before v2.1.288 |
|---|---|
rm -rf <cwd> | stopped — "Dangerous rm operation detected" |
(rm -rf <cwd>) | stopped |
{ rm -rf <cwd>; } | stopped |
echo $(rm -rf <cwd>) | stopped |
sh -c "rm -rf <cwd>" | runs, files deleted |
bash -c 'rm -rf <cwd>' | runs, files deleted |
After v2.1.288, the last two rows go from "runs, files deleted" to the same dangerous-rm prompt as the first four. After v2.1.289, additional wrappers that were also slipping past are tightened:
TZ="$HOME" rm -rf build) — fixed under sandbox auto-allow.idle_prompt notification hooks firing while background agents are still running (#93672) — fixed (PreToolUse/PermissionRequest hooks being skipped on JSON serialization failure is also tightened: the call is now blocked, not silently allowed).permissions.blockReadsOutsideWorkingDirectories is on — fixed.rm inside a bash -c or sh -c script running without a prompt in bypassPermissions mode or under a shell allow rule (#96300) — fixed.v2.1.289 also adds one forward-looking capability: agent.spawn for teammates, a single agent id across plugin hook events, and idle and waiting states in $.agent.list(). That is a real surface-area addition — anything built on top of Claude Code's mod/plugin system that coordinates multiple agents will need to be aware of the new hook events and the agent.spawn semantics.
v2.1.288's MCP work is also worth flagging on its own: the "Added a re-authenticate prompt when an MCP server asks for more OAuth scope during a tool call" line means MCP servers can now request additional scopes mid-session instead of erroring out or silently auto-granting. For anyone wiring MCP into production agent harnesses, that's a contract change worth pinning in your test plan.
If you run Claude Code in bypassPermissions mode — unattended CI loops, scheduled-agent harnesses, in-repo autonomous refactors, or subagent chains that pass work through claude -p — the v2.1.288 fix is the kind of patch you want applied before the next unattended run, not after. The bug is small in code (a regex/shell-parse check that didn't recurse into -c arguments) but its blast radius is large because of where the check sits: it is the only prompt before a rm -rf of the working directory, the home directory, or a system directory in bypass mode. One sh -c wrapper and that prompt disappears.
Three concrete reasons the gap matters for autonomous deployments:
1. CI / sandbox-bypass use cases. The reporter's environment was Windows 10 + Git Bash + Haiku, but the underlying function is shared, and the reporter explicitly notes: "We did not run it, because Haiku and Sonnet refuse to issue that command" — meaning they did not empirically confirm the home-directory or / case on their own machine because the model refused. In other words, the model's self-restraint was the only line of defense. Anyone running a less-aligned model, or a model whose training cut lets it follow a prompt-injected instruction to wrap a destructive command in sh -c, was relying on a check that wasn't there. 2. Prompt-injection surface. Anyone who has run an agent on untrusted input — repository text, fetched web pages, third-party MCP server responses — knows that the model can be coaxed into wrapping destructive commands in bash -c. The fix narrows the gap from "any prompt-injection can wrap" to "must use a wrapper the check doesn't yet know about." 3. Shell allow rules aren't a substitute. The fix notes "or under a shell allow rule" explicitly. A Bash(bash:<em>) or Bash(sh:</em>) allow rule in permissions.allow was enough for the dangerous-rm check to be skipped. If your harness uses broad shell allow rules (a common pattern for build/test agents), v2.1.288 should be a hard pin.
The wider hardening arc matters, too. Looking at the issue body, the dangerous-rm check has been incrementally hardened across 2.1.208 ($(...) and backticks), 2.1.261 (double-quoted sh -c for variable targets), 2.1.273 (subshells in bypass mode), and now 2.1.288/289 (sh -c / bash -c and env-var-prefix overrides). That is a deliberate tightening sequence from the maintainers, not a one-off patch. If your autonomous-agent harness sits on an older Claude Code pin, the cumulative gap between your pin and head has been growing on this surface for several releases.
Two independent primary sources confirm the change, plus one bug report:
anthropics/claude-code#96300](https://github.com/anthropics/claude-code/issues/96300): the public disclosure with the reproduction script, the exact table above, environment details (Claude Code 2.1.280, Windows 10 Git Bash, Haiku), and an explicit impact statement: "In bypass mode this check is the only prompt before a removal of the working directory, the home directory or a system directory. One sh -c wrapper removes that prompt."v2.1.288](https://github.com/anthropics/claude-code/releases/tag/v2.1.288) (2026-10-02 20:19 UTC): verbatim "Fixed a dangerous rm (such as one on / or the home directory) inside a bash -c or sh -c script running without a prompt in bypassPermissions mode or under a shell allow rule (#96300)" — Anthropic's own acknowledgment of the gap with the issue number cited.v2.1.289](https://github.com/anthropics/claude-code/releases/tag/v2.1.289) (2026-10-03 23:07 UTC): nine additional permission/plugin fixes, the user-installed-plugin-vs-organization-MCP-signin fix, and the agent.spawn addition for teammates.The full anthropics/claude-code releases atom feed shows v2.1.289 as the current stable top entry, v2.1.288 immediately before it, and v2.1.287 as the prior pillar we already covered. We did not run the reproduction script ourselves in this session — the reporter's environment (Windows 10 Git Bash, Haiku) was not available to us, and reproducing destructive rm -rf behavior on a CI machine is not something we will do without a disposable sandbox. The claim "v2.1.288 closes the v2.1.280 bypass" rests on the release notes' explicit citation of issue #96300 and the verbatim phrasing match against the issue's reproduction table, not on our own re-test.
The fix has real scope limits, and the maintainers are honest about them in the issue thread.
What the fix does not cover. The reporter explicitly notes: "No check on the command text can cover every wrapper. A script file or python -c will always get past it. This report is only about sh -c and bash -c, which models write on their own in ordinary work." So a sufficiently motivated attacker — or a model that has been prompt-injected — can still reach rm -rf by writing a script to /tmp/x.sh and executing it, or by invoking python -c 'import shutil; shutil.rmtree(...)', or by piping into xargs. The dangerous-rm check is a defense-in-depth control, not a complete one. Treat it as one wall among several.
Empirical test scope. The reporter tested only the working directory on Windows 10 Git Bash with the Haiku session model. The home-directory and / cases are expected to behave the same because the same function path is shared, but were not run by the reporter (the model refused on its own). Anthropic's release notes phrase the fix in terms of "/ or the home directory" — the broader framing — but we have not independently confirmed the fix in those paths.
Default vs. bypass mode. The fix only changes behavior in --permission-mode bypassPermissions or under explicit shell allow rules. If your harness runs Claude Code in default mode with a tight allow list, the dangerous-rm check was already blocking the bare rm -rf <cwd> form; the new fix is irrelevant to you beyond a tighter wall. If you run in --permission-mode acceptEdits or plan, the same applies.
Versioning. v2.1.289 is the latest stable as of 2026-10-04 12:08 UTC. Both fixes require bumping from any pre-2.1.288 pin. If your package.json or Dockerfile pins to a specific version, expect to rebuild or shift one agent. npm auto-updater fix in v2.1.288 means platforms relying on the npm auto-update path should now get a real binary instead of a placeholder claude stub, but if you pinned to a specific npm version, that change doesn't help.
Plugin trust model. v2.1.289's fix "user-installed plugin being able to rewrite the descriptions of an organization-managed MCP server's sign-in tools" closes a confusion-of-deputy path where a user-side plugin could shadow the trust signals a managed MCP server was supposed to present to the model. If you ship a managed Claude Code environment with organization-managed MCP servers, the fix protects the integrity of the OAuth sign-in flow your users see. There is no migration required, but you should re-test any user-side plugin your mod that previously had write access to MCP server tool descriptions.
This is a well-handled hardening cycle, not a panic moment. A third-party reporter found a real bypass in the dangerous-rm check, filed it publicly, and Anthropic shipped the fix and credited the issue in the public changelog within the same release train. The v2.1.289 follow-up closes nine adjacent gaps in the same surface area, which is what you want to see from a maintainer who has actually internalized the threat model.
The pattern matters more than any single patch: the dangerous-rm check has been incrementally hardened at 2.1.208, 2.1.261, 2.1.273, 2.1.288, and now 2.1.289 — five separate rounds in five months. If your harness pins to anything older than 2.1.288 and runs in bypassPermissions, the cumulative gap is the real concern, not the headline sh -c line.
For founders and engineering leads who delegate Claude Code to a CI or autonomous loop: bump to 2.1.289 today, audit any bash or sh allow rule in your settings.json for breadth, and re-run your end-to-end permission tests against the v2.1.289 build before you trust the next unattended run.
1. Pin to ≥2.1.289 today. If you use the npm auto-update path, verify the auto-update fix in v2.1.288 actually replaced the placeholder stub (run claude --version and confirm it reports 2.1.289 or newer). If you pin to a specific version, change the pin now. 2. Audit shell allow rules. Any Bash(bash:<em>) or Bash(sh:</em>) rule in settings.json should be narrowed or removed unless you have a strong reason for it. Broad shell allow rules in bypassPermissions mode were the explicit second attack surface the v2.1.288 fix calls out. 4. Re-test unattended runs end-to-end. Your existing CI or scheduled-agent harness should be run against 2.1.289 in a disposable sandbox to confirm no behavior change breaks your flow, before you promote the pin to production. 5. For high-risk tasks, prefer default mode with explicit allow rules. bypassPermissions is convenient for autonomous loops but it is also the mode this entire family of fixes targets. If a particular agent task can run in default mode with a tight allow list, do that. 7. If you ship a managed Claude Code environment: re-test any user-side plugin your mod that touches MCP server tool descriptions. The v2.1.289 fix protects the integrity of the OAuth sign-in flow but you should confirm no legitimate plugin relies on the old behavior. 8. Watch for the next patch. Given the hardening cadence (2.1.288 → 2.1.289 in 27 hours), expect another patch or a stable v2.1.290 within a few days. If you run unattended agents, plan for one more bump before the next unattended job.
sh -c / bash -c" (public disclosure with reproduction script and table)](https://github.com/anthropics/claude-code/issues/96300)