Do Claude Code Deny Rules Actually Work?
Running Claude Code? See, control, and price every tool call it makes — allow/ask/deny policy, a per-session bill at API rates, and a full audit log, in one command:
curl -sf https://agenticcontrolplane.com/install.sh | bash
Full Claude Code guide → · see your first governed call → · free up to 5 agents
Last updated: August 31, 2026.
Write a deny rule in .claude/settings.json, watch Claude Code respect it, and you’ll reasonably conclude the file is protected. The issue tracker disagrees often enough that it’s worth a careful look. This post is a catalog of the documented ways deny rules fail — filed by users, mostly still open — followed by what does hold.
The short version: deny rules are worth writing and worth keeping. They are also best-effort filtering inside the agent’s own process, and the record shows at least five distinct classes of route around them. If a deny rule is the only thing between your agent and something you can’t afford, the record says that’s one layer short.
The bug record
Class 1: rules not enforced at all. The bluntest reports. #26334 is titled with the finding — “The deny rules simply don’t deny anything” — and #25621 reports Bash deny rules not enforced. #27040 documents denied config files read and edited with no prompt and no error; #27547 shows deny-matching commands falling through to an ask prompt instead of a block. #8961 has the detail that should worry you most: the same denied file was readable in one session and correctly blocked in another. Intermittent enforcement is worse than none, because your test proves the rule works and the failure happens later.
Class 2: Bash patterns are prefixes, not parsers. A rule like Bash(git push*) matches a command string. cd repo && git push, a wrapper script, an alias, a compound command assembled mid-session — none of these look like the prefix. #28008 documents denied file content reached through recursive grep and glob from an un-denied path. This isn’t a bug so much as the nature of string-matching a shell: the docs themselves note that wrapper commands can’t be reliably matched by prefix rules. Every regex-based hook guide ends with the same caveat, because it’s true.
Class 3: delegation routes around the list. #43142: a subagent bypasses git deny rules set in settings.local.json. The parent session has the policy; the delegated agent’s calls don’t consistently inherit the check. The same shape shows up outside Claude Code — opencode’s hook layer has an open issue noting any agent can bypass restrictions by delegating to a subagent — which suggests this is what happens whenever policy lives in the session rather than under it.
Class 4: alternate read paths. #6828 and #61148: the @-file attachment syntax reads files that Read-tool deny rules block, including via @../ traversal. CVE-2025-59829 covered symlink-mediated access. And the Knostic write-up on .env handling adds the sequencing detail that deny rules may only bind after a file has been accessed once.
Class 5: the settings file is itself writable state. CVE-2026-33068 documented a repository able to silently set bypassPermissions. Separately, security researchers demonstrated malicious hooks rewriting the permissions file to allow-list curl. The deny list lives in a file the session can touch; anything that can edit the file can edit the policy.
What this adds up to
None of these classes is exotic, and naming them isn’t a dunk on Claude Code — its permission system is still the most complete native one we’ve ranked, and several of the issues above will get fixed. The structural point outlives the individual bugs: a deny list enforced inside the agent’s own process, written in a file the process can reach, matched against strings the model composes, is best-effort by construction. It filters the common case. It is not a boundary, and the failure mode when it slips is silent — the command just runs, and nothing in the transcript looks unusual.
That silence is the part we’d fix first. In one widely-read incident report (#10077 — an rm -rf that took out a home directory), the session log didn’t record the command that ran. The user’s complaint wasn’t only that the deny layer failed; it was that afterwards it was impossible to see exactly what was executed. A control that can fail and can’t tell you it failed is two problems wearing one trenchcoat.
What actually holds
Layered, in order of what it buys you:
- Keep the deny rules. They’re free, they catch the everyday case, and most sessions never test the bypass classes. Just calibrate what they are: a tripwire, not a wall.
- Sandbox for containment. A devcontainer, Docker sandbox, or the native OS sandboxing bounds where damage can land regardless of what the model composes. This is the right answer for untrusted input, and it’s the one the community converged on. Its limit is the mirror image of its strength: a wrong-but-in-bounds action — a destructive command against a database you legitimately have credentials for — sails through.
- Policy and audit on the tool-call path. A PreToolUse hook fires before every native tool call and MCP call, in every mode except
--dangerously-skip-permissions. Backed by a rules engine rather than a regex, it’s the seam where a deny can be decided outside the agent’s process — and, as important, where every call and every decision gets recorded by something the agent doesn’t author. Then a deny that fails to deny is at least a visible event instead of a quiet one.
We build the third layer — the hook plus server-side policy and an audit trail is what ACP’s Claude Code integration is — so weigh that disclosure as you read this. But the ranking above doesn’t depend on the product: rules where they’re cheap, a sandbox where input is untrusted, and enforcement-plus-record on the call path is just what the bug record recommends once you take it seriously.
Frequently asked questions
Why are my Claude Code deny rules not working?
Three common causes, all documented in the issue tracker. Pattern mismatch: Bash deny rules match command prefixes, so wrappers, compound commands, and flag variations walk past them. Path variance: reads can reach a denied file through @-attachments, recursive grep from a parent directory, or a symlink. And enforcement bugs: multiple issues report deny rules in settings.json intermittently not applied at all — allowed in one session, blocked in the next.
Do deny rules apply to subagents in Claude Code?
Not reliably. Issue #43142 documents a subagent bypassing git deny rules set in settings.local.json. When a task is delegated, the delegated agent’s tool calls have not consistently been checked against the parent session’s deny list. If your deny list is load-bearing, assume delegation can route around it.
Are Claude Code deny rules a security boundary?
No — and on this point the documented behavior is clear. They’re best-effort client-side filtering: useful against accidents, not against a determined bypass, and subject to the failure classes cataloged here. Anthropic’s own managed-settings guidance frames pattern matching on command strings as guardrails, not an OS-level boundary. For a boundary you need enforcement the agent process can’t route around: an OS sandbox for containment, or a gateway on the tool-call path for policy.
How do I actually stop Claude Code reading my .env file?
Layer it. A deny rule on the path is the first layer and worth having, but reads have documented routes around it (@-attachments, recursive search, symlinks, and the note that deny may only apply after a first access). The reliable layers are keeping the secret out of the agent’s reach entirely — a credential broker that injects at execution time, or an environment where the file simply isn’t present — plus an audit trail so you can see whether it was read.
What should I use instead of deny rules?
Not instead — in addition. Keep the deny rules; they catch the common case cheaply. Add an OS-level sandbox if the agent handles untrusted input, and put policy plus audit on the tool-call path itself, where the decision is made outside the agent’s process and every allow and deny is recorded whether or not the client honored it.
Where to read more
- The Best Ways to Control What Claude Code Can Do, Ranked — the six options, honestly compared
- Which Coding Agents Can You Actually Control, Ranked — the same question across fourteen agents
- Sandboxes and control planes — why containment and policy compose instead of compete
- Your rules file is not a policy — the same failure one level up, in prose rules