On July 18, 2026, Anthropic shipped Claude Code v2.1.214. Buried in a changelog full of the usual fixes and additions is a cluster of five entries that all describe the same category of bug: the permission-check layer — the code that decides whether a tool call needs your explicit approval or can run automatically — failed to catch a command it should have flagged. The official changelog lists each one as a separate line item with no additional writeup, which is standard for Anthropic’s release notes but leaves the “so what does this actually mean for me” question unanswered.
That’s what this post is for. Permission checks are the only thing standing between “Claude proposes a command” and “Claude runs a command.” When that layer misjudges a command, the practical effect is the same regardless of whether the trigger was a malicious prompt injection, a compromised MCP tool, or Claude itself generating something unusual in the course of normal work: a command executes without you ever seeing the confirmation dialog. None of the five bugs below required an attacker in the traditional sense — several of them could fire from completely benign, everyday usage. That’s arguably the more useful thing to understand, because it means “I don’t run untrusted prompts” isn’t sufficient reassurance on its own.
We verified every technical detail below against the live changelog rather than paraphrasing from memory — exact wording is quoted where it matters.
1. dir/** Allow Rules Matching Nested Directories Anywhere in the Tree
The changelog entry: “Fixed single-segment dir/** allow rules like Edit(src/**) auto-approving writes to nested dir/ directories anywhere in the tree instead of only <cwd>/dir.”
What failed: When you write a permission rule like Edit(src/**) in your settings, the intent is almost always “let Claude edit files under <cwd>/src/ without asking.” The permission matcher, however, was treating that single path segment as matching any directory named src anywhere in the working tree — not just the one directly under your current working directory. So a rule scoped to ./src/** was silently also covering ./vendor/some-package/src/**, ./node_modules/whatever/src/**, or any other nested src/ directory buried arbitrarily deep in the project.
Why it bites you: This is a classic scope-widening bug, and it’s dangerous specifically because it’s invisible during normal use. You write a narrow allow rule believing it covers one directory, verify it works as expected on files inside that directory, and never notice that it’s also silently approving writes somewhere else in the tree — say, inside a vendored dependency, a build output directory, or a git submodule that happens to share the same leaf directory name. If any of those nested locations feed into your build, CI pipeline, or get committed, an agent (or a prompt-injected instruction) editing files there without a prompt is a real path to unreviewed code landing somewhere it shouldn’t.
There’s a second, related change buried in the “Changed” section of the same release: “single-segment dir/** hook if: conditions” now match only <cwd>/dir — you have to explicitly write **/dir/** to get any-depth matching in hooks. Notably, deny and ask rules keep their old any-depth matching behavior; only the auto-approve (allow) path was tightened. That’s a sensible fail-closed choice: the fix narrows scope on the rules that grant silent access, while leaving the more conservative rules exactly as permissive as before.
If you have any allow rules in .claude/settings.json shaped like Edit(dirname/**), Read(dirname/**), or Write(dirname/**) — no leading **/ — go check what they actually cover in your tree.
2. Windows PowerShell 5.1 Permission-Check Bypass
The changelog entry: “Fixed a permission-check bypass affecting commands run in Windows PowerShell 5.1 sessions.”
Anthropic didn’t publish the specific mechanism for this one — which is fairly common for security-relevant fixes, since a detailed root-cause writeup can double as a working proof of concept for anyone still on an unpatched version. What we can say precisely is what the entry confirms: it’s scoped to Windows PowerShell 5.1 specifically, not PowerShell 7+, and it’s described as a bypass of the permission-check step itself, not a crash or a usability bug.
That distinction matters because PowerShell 5.1 (the version that ships in the box with Windows, as opposed to the cross-platform PowerShell 7.x that most Windows developers install separately) has quoting, escaping, and tokenization behavior that differs from both PowerShell 7 and from Bash. A permission analyzer that classifies commands by parsing their text has to model each shell’s syntax correctly to decide what a command actually does before deciding whether it needs approval — and shell-specific parsing divergences are exactly the kind of thing that produces bypasses, which is the same underlying category as bug #3 below, just on a different shell.
Practical takeaway: if you’re on Windows and still defaulting to the built-in PowerShell 5.1 rather than PowerShell 7+, this is one more reason (on top of the general encoding and reliability fixes elsewhere in this same release — UTF-16LE file writes and hanging child processes are both called out separately for 5.1) to either upgrade your default shell or make sure Claude Code is picking up PowerShell 7 in your environment.
3. zsh Variable Subscripts and Modifiers Treated as Inert Text
The changelog entry: “Fixed Bash permission checks treating zsh variable subscripts and modifiers in [[ ]] comparisons as inert text — these commands now prompt for approval.”
What failed: Claude Code’s Bash permission analyzer parses command strings using Bash syntax rules to figure out what a command will actually do — which binaries it invokes, what arguments it passes, whether it contains redirects or substitutions — so it can match that against your allow/deny/ask rules. But if your default shell is zsh (extremely common on macOS, where it’s been the default since Catalina), the commands Claude proposes and runs are actually interpreted by zsh, which has syntax Bash doesn’t: array/associative-array subscripts like ${arr[$i]}, and parameter expansion modifiers like ${(f)var} or ${(s.:.)PATH}. When these constructs showed up inside a [[ ]] conditional test, the Bash-oriented analyzer didn’t recognize them as active syntax — it treated the whole subscript or modifier expression as if it were inert literal text with no execution semantics.
Why it bites you: The permission analyzer’s job is to determine what a command does before deciding whether that requires your approval. If it can’t parse a chunk of the command at all and just shrugs and treats it as inert, it’s effectively evaluating a simplified, incomplete version of the actual command — meaning its risk assessment can be wrong in either direction, but the dangerous direction is “looks safe, isn’t.” A command whose real behavior is determined by a zsh-specific expansion the analyzer didn’t understand could pattern-match against an allow rule (or simply fail to trip a deny rule) based on the visible, “boring” parts of the command, while the part doing the actual work sailed through unexamined. This is a narrower, shell-specific sibling of bug #5 below — same failure shape, different root cause: the analyzer under-modeling what a command actually evaluates to.
4. help and man Commands Auto-Approved Despite Unsafe Options
The changelog entry: “Fixed Bash permission checks to no longer auto-approve certain help and man commands that could run unsafe options, command substitutions, or backslash paths.”
What failed: help and man are treated by Claude Code’s permission system as low-risk, read-only informational commands — you’re just asking for documentation, so it’s reasonable to let them run without a prompt. The bug was that this “safe by default” classification didn’t account for the fact that both commands accept options and arguments that change their behavior substantially. man in particular supports flags that control which external pager or formatter it shells out to, and both commands’ arguments can legitimately contain shell constructs — command substitution ($(...) or backticks) that gets evaluated before the string is even handed to man/help, or backslash-escaped paths that can resolve outside the directory you’d expect.
Why it bites you: The exploitable shape here is a command that starts with a trusted, always-auto-approved prefix but carries a payload in its arguments. Something like man --pager='some-command; malicious-command' or help "$(cat some_file_with_a_side_effect)" reads, on a surface pattern match, as “just asking for help text” — exactly the category the auto-approve rule was designed to wave through — while the actual execution includes an option or a substitution the classifier never independently evaluated. This is a good illustration of why “the command starts with a known-safe binary name” is a weak basis for an auto-approve decision on its own; the fix moves the classifier to actually look at which options and argument forms are present before deciding a help/man invocation is truly inert.
5. Commands Over 10,000 Characters Auto-Running Instead of Prompting
The changelog entry: “Fixed Bash permission checks misjudging very long commands — commands over 10,000 characters now always prompt instead of running automatically.”
What failed: This is the bluntest of the five, and arguably the most concerning because it doesn’t require any syntax trick at all — just length. Somewhere in the permission-checking pipeline, a command string beyond roughly 10,000 characters was falling through to auto-run instead of triggering a prompt. The changelog doesn’t specify the exact internal cause (a parse timeout, a buffer/complexity limit, a fallback path that defaulted open instead of closed), but the observable behavior is clear: past a length threshold, the safety net stopped applying.
Why it bites you: You don’t need an adversary for this one. A 10,000+ character single-line command shows up in ordinary agentic workflows more often than you’d think — a long chain of piped transformations, a heredoc embedding a sizeable config or script inline, a command Claude assembled programmatically from a large list of file paths or arguments, or a base64-encoded payload passed inline to curl or openssl. Any of those could innocently cross 10,000 characters without anyone trying to evade review. Before this fix, crossing that threshold meant the command executed immediately, with none of your configured deny/ask rules ever getting a chance to evaluate it — a fail-open outcome for exactly the kind of command that’s hardest for a human to eyeball and hardest for an automated matcher to be confident about. The fix flips the default: long commands now fail closed (prompt) rather than fail open (run).
Upgrade Guidance
All five bypasses are fixed as of v2.1.214 (July 18, 2026). If you’re running an older version, none of your allow/deny/ask permission rules are being enforced as reliably as you’d expect for the specific patterns above.
Check your version:
claude --version
If it reports anything earlier than 2.1.214, update:
claude update
Then re-run claude --version to confirm the update landed. If you manage Claude Code through a package manager or a pinned version in CI (Homebrew, npm global install, a Docker base image, etc.), check that pin separately — claude update only affects the binary it’s invoked from, and CI runners in particular are easy to forget since nobody’s watching the update prompt.
If you rely on any single-segment dir/** allow rules for scoping (bug #1), it’s worth an explicit audit after updating rather than assuming the fix silently protects you — the fix narrows what those rules match, so if you were unknowingly depending on the old any-depth behavior for something you actually wanted covered, you’ll need to update the rule to **/dir/** yourself.
FAQ
Q: Do I need to do anything beyond updating to be protected?
For four of the five bugs (PowerShell 5.1, zsh subscripts, help/man, and the 10,000-character threshold), updating to v2.1.214+ is sufficient — the permission analyzer itself was corrected. For the dir/** allow-rule bug, you should also review any single-segment allow rules in your .claude/settings.json to confirm the narrowed (cwd-only) matching still covers what you intended; some setups may have been unintentionally relying on the old broader match.
Q: Were any of these actively exploited, or found through internal testing? Anthropic’s changelog doesn’t disclose the discovery method for any of the five entries, and none of them are described as actively exploited in the wild. Treat this the way you’d treat any responsibly-disclosed permission-check fix: as a defense-in-depth improvement worth applying promptly, not as evidence of a specific incident.
Q: Does this affect me if I never approve risky permissions and always run in a sandboxed or containerized environment? It affects you less, but not zero. Sandboxing limits blast radius if something unexpected executes; it doesn’t restore the permission prompt that should have appeared. If your threat model depends on Claude Code actually asking before running certain commands — for audit logging, for a human review step, or for compliance reasons — these bugs meant that expectation silently didn’t hold for the specific patterns described above, regardless of what runs the process afterward.
Q: Is the 10,000-character threshold something I can configure? The changelog doesn’t mention a configurable override for the threshold itself — it’s presented as a fix to a misjudgment, not a new tunable setting. If your workflows regularly generate very long single-line commands (large heredocs, big argument lists), expect to see more permission prompts after updating, since that’s precisely the fail-closed behavior the fix introduces.
Q: How do I know if I was actually affected by any of these before updating? There’s no retroactive audit log that flags “this command should have prompted and didn’t” — Claude Code’s permission system doesn’t log a counterfactual for checks that passed. The practical answer is to update immediately rather than try to determine after the fact whether any of these five patterns fired in your history.