BuildNot yet confirmed elsewhere1 publisher2 min readPublished
Claude Code's deny rules missed an env-prefixed rm -rf in every 2.1.288 run
A dev.to tester ran TZ="$HOME" rm -rf build past a Claude Code 2.1.288 Bash(rm *) deny rule in 6 of 6 sandboxed runs. Version 2.1.289 stopped all six, but bash -c 'rm -rf build' still ran in 6 of 6 sandboxed runs on that build.
The Engineer · Build desk

On Claude Code 2.1.289, FOO="$(pwd)" rm -rf build ran in 7 of 7 runs where Bash was otherwise allowed or permissions were bypassed; bash -c 'rm -rf build' ran in 6 of 6 sandboxed runs; TZ="$HOME" rm -rf build was stopped in 6 of 6 runs.
| Figure | Value | Note | Claim |
|---|---|---|---|
| FOO="$(pwd)" rm -rf build, Bash allowed or bypassed | 7 of 7 runs ran | 7 | |
| bash -c rm -rf, sandboxed | 6 of 6 runs ran | 6 | |
| TZ="$HOME" rm -rf stopped | 6 of 6 runs | 5 |
What happened
- The 2.1.289 changelog lists two fixes: a rule missing a command behind an env prefix with an expanded value, and a rule skipped when a bare variable assignment came first.
- Both bugs need the Bash sandbox on, its auto-allow mode at the default, and a deny or ask rule the user expected to hold.
- The tester ran 39 headless sessions on 2026-10-05 on macOS under Seatbelt, comparing the installed 2.1.289 build with the 2.1.288 npm package.
- The permissions page's own table lists bash -c 'rm -rf build/' and /bin/rm -rf build/ as forms that Bash(rm *) does not stop.
Why it matters
- contradiction The sandboxing page says explicit deny rules are always respected under auto-allow, yet 2.1.288 deleted the folder in all six TZ runs.
- exposure The permission modes page lists this setup as a common one, so the 2.1.288 gap reached ordinary configurations.
- decision Teams that put destructive commands in deny need to check which spellings their agents can reach, since a bash -c wrapper passed in 6 of 6 sandboxed runs.
A Bash rule is a text match. The permissions page says it "matches the command text Claude writes, after Claude Code splits compound commands and strips wrappers" [15]. The stripped wrappers are timeout, time, nice, nohup, stdbuf, command, builtin and noglob. env is not on the list [2].
Leading variable assignments get two treatments. For allow rules, Claude Code strips known-safe variables, so Bash(npm test *) matches NODE_ENV=test npm test, and an allow rule will not match past any other variable [3]. TZ sits on the known-safe list that a 2.1.98 changelog entry describes [1]. The dev.to write-up flags that list as relevant to the 2.1.288 bug [1]. The changelog fix is worded around an expanded value, "$HOME", behind the prefix [8].
The bash -c result confirms documented behavior. The 6 of 6 sandboxed runs on 2.1.289 are what the docs table predicts for that form [6].
On the FOO="$(pwd)" case, the 2.1.289 changelog says a rule skipped after a bare variable assignment was fixed [8]. The docs say deny rules match past any leading assignment [13]. The write-up's opening summary describes the seven runs by configuration, Bash "otherwise allowed or permissions were bypassed", and does not say a deny rule was loaded in each one [7]. Together with bash -c, that is 13 of 13 runs executing on 2.1.289, under two different configurations [17].
For the numbers to transfer, a deployment needs the setup the two bugs share, plus the tester's platform and model: macOS under Seatbelt, claude-opus-5-5 [11]. The deletion target was a throwaway build/ folder, recreated before every attempt [12].
In our view, a deny rule is a guard on Claude's usual phrasing, which is how the docs scope it: the rule "covers the invocation Claude usually produces and isn't a security boundary around the program" [15]. A limit that has to hold against rephrasing belongs below the command text, in what the sandbox and the filesystem allow.
What to watch
- A per-run breakdown of the seven FOO="$(pwd)" runs, showing whether a deny rule was loaded under sandbox auto-allow in each.
- Whether a later Claude Code changelog entry extends rule matching to bash -c or other shell wrappers, or the docs keep listing it as uncaught.
- Reruns of the same test on a non-macOS sandbox or another model, to see if the 6 of 6 results hold.
Clarity's read
What the record supports and how the coverage leans. The claims behind it follow.
Reality
- Evidence68
- Adoption
- Insufficient
- Hype gap−10
- Incentives
- Insufficient
- Confidence65
Claim ledger
Ranked by verification strength, evidence, and original report placement.
- [1]
A 2.1.98 changelog entry says read-only commands with env-var prefixes did not prompt unless the var is known-safe (LANG, TZ, NO_COLOR, etc.); the write-up says TZ and LANG are on a known-safe list and that this matters to the bug.
ReportedSupportedSource: Claude Code changelog and dev.to write-up2 sources— create a free account to open themView cited source - [2]
The wrappers Claude Code strips before matching are timeout, time, nice, nohup and stdbuf, plus the shell builtins command and builtin, and zsh's noglob; env is not on the list.
ReportedSupportedSource: Claude Code permissions page, as quoted by the dev.to write-up2 sources— create a free account to open themView cited source - [3]
Claude Code strips a leading assignment of certain known-safe environment variables, so Bash(npm test *) matches NODE_ENV=test npm test; an allow rule won't match past an assignment of any other variable.
ReportedSupportedSource: Claude Code permissions page, as quoted by the dev.to write-up2 sources— create a free account to open themView cited source - [4]
With the Bash sandbox in auto-allow mode and Bash(rm *) in deny or ask, Claude Code 2.1.288 ran TZ="$HOME" rm -rf build in 6 of 6 runs and the folder was gone every time.
- [5]
Claude Code 2.1.289 stopped TZ="$HOME" rm -rf build in 6 of 6 runs under the same setup.
- [6]
On 2.1.289, bash -c 'rm -rf build' still ran in 6 of 6 sandboxed runs.
- [7]
On 2.1.289, FOO="$(pwd)" rm -rf build ran in 7 of 7 runs where Bash was otherwise allowed or permissions were bypassed.
- [8]
The 2.1.289 changelog has two lines: one fixes Bash deny and ask rules missing a command behind an environment variable prefix with an expanded value (e.g. TZ="$HOME" rm -rf build) when the sandbox auto-allows commands; the other fixes a deny or ask rule being skipped under sandbox auto-allow when a bare variable assignment came before the command.
- [9]
Both bugs need the same setup: the Bash sandbox switched on, its auto-allow mode left at the default, and a deny or ask rule the user expected to hold.
- [10]
The permission modes page lists "Manual mode plus the Bash sandbox in auto-allow mode" as one of its common setups and says of it: "Deny rules still apply, and ask rules that name a command, such as Bash(git push *), still prompt."
ReportedSupportedSource: Claude Code permission modes page, as quoted by the dev.to write-upView cited source - [11]
The tests ran on 2026-10-05 between 16:25 and 16:43 UTC on macOS, where the sandbox uses Seatbelt, using Claude Code 2.1.289 (installed build) and 2.1.288 (npm package run from the lab directory), both driving claude-opus-5-5 through --model opus, for 39 headless claude -p sessions.
- [12]
The only thing any command could delete was a throwaway build/ folder inside the lab, recreated before every attempt.
- [13]
The permissions page says a deny or ask rule matches past any leading assignment, so Bash(rm *) in deny still matches FOO=bar rm -rf tmp/.
ReportedSupportedSource: Claude Code permissions page, as quoted by the dev.to write-upView cited source - [14]
The permissions page has a table of forms a rule does not catch; for Bash(rm *), the "Doesn't stop" column lists /bin/rm -rf build/ and bash -c 'rm -rf build/'.
ReportedSupportedSource: Claude Code permissions page, as quoted by the dev.to write-upView cited source - [15]
The permissions page says: "A Bash rule matches the command text Claude writes, after Claude Code splits compound commands and strips wrappers. It doesn't match the same program invoked in a different form, so a deny or ask rule covers the invocation Claude usually produces and isn't a security boundary around the program."
ReportedSupportedSource: Claude Code permissions page, as quoted by the dev.to write-upView cited source - [16]
The sandboxing page lists what still applies "Even in auto-allow mode", including "Explicit deny rules are always respected".
ReportedSupportedSource: Claude Code sandboxing page, as quoted by the dev.to write-upView cited source - [17]
Across the two variants that still ran on 2.1.289, 13 of 13 runs executed (6 bash -c runs plus 7 FOO="$(pwd)" runs), under different configurations.
Sources
1 independent publisher whose own reporting we read for this story.
Topics and entities
Follow any of these and your For You feed starts watching them — no settings page required.
Topics
- Agent permissions and sandboxingFollow
- AI Coding Agent SecurityFollow