[BUG] Read tool ignores active permissions.deny rules (verified via /permissions), while Bash correctly enforces the equivalent sandbox restriction on the same paths

Status Open
Reported on v2.1.223
Maintainer reply None cached
Activity 1 comment · opened Aug 6, 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?

Title: Read tool ignores active permissions.deny rules (verified via /permissions), while Bash correctly enforces the equivalent sandbox restriction on the same paths

Summary

The Read tool successfully reads files matched by rules that are confirmed
active and present in the Deny list (verified via the /permissions
UI), for two independent rules, across two directories, at multiple path
depths. The Bash tool, by contrast, correctly enforces its own filesystem
sandbox restrictions on the very same paths — so this is not a general
policy failure, it's specific to how Read (and likely Write/Edit)
checks permissions before opening a file.

Environment

  • Claude Code version: 2.1.223 (claude --version confirmed)
  • OS: Linux, 7.1.5-201.fc44.x86_64 (Fedora-based), x86_64
  • Project: local directory with a project-level .claude/settings.json, no git repo
  • Session run as CLI (CLAUDE_CODE_ENTRYPOINT=cli)
  • sandbox.enabled: true with allowedPaths restricted to the project dir

Relevant config

<project>/.claude/settings.json:

{
  "permissions": {
    "deny": [
      "Read(/home/**)",
      "Read(/etc/**)"
    ],
    "allow": [
      "Read(~/claude/**)",
      "Edit(~/claude/**)"
    ]
  },
  "sandbox": {
    "enabled": true,
    "allowedPaths": ["~/claude"],
    "allowedDomains": ["api.github.com", "registry.npmjs.org"]
  }
}

Confirmed via /permissions → Deny tab → search etc:

1. Edit(/etc/**)
2. Edit(/etc/wireguard/**)
3. Read(/etc/**)
4. Read(/etc/**)          <- appears twice (two settings sources), both active

Header text: "Claude Code will always reject requests to use denied tools."

Steps to reproduce

  1. In a project with .claude/settings.json containing "deny": ["Read(/etc/**)", "Read(/home/**)"] (and a narrower allow carving back only the project directory), start a session.
  2. Confirm via /permissions that the rules appear as active/duplicated in the Deny tab.
  3. Call the Read tool on a path matched by the deny rule, e.g.:
  • Read(/etc/passwd)
  • Read(/etc/hosts)
  • Read(/etc/bluetooth/main.conf) (nested, 2 levels deep)
  • Read(/etc/unbound/root.key) (nested)
  • Read(/home/leon/.bashrc) (outside the allowed project carve-out)

Expected behavior

Every call above should be hard-blocked by the Deny rule — per the
/permissions UI's own description, Claude Code should "always reject
requests to use denied tools," with no prompt and no read.

Actual behavior

All calls above succeeded and returned full file contents. No denial
message, no prompt, no error — behaviorally identical to there being no
deny rule at all.

The only case that failed was Read(/etc/ssh/sshd_config), and that
failure was EACCES — i.e. blocked by real Unix file permissions (root-only
file), not by Claude Code's permission engine. This rules out "the deny
rule works but I got a false positive from a different block" — the deny
engine visibly never engaged.

Why this looks like a Read-specific bypass, not a broader policy failure

The Bash tool's own sandbox did correctly restrict the same filesystem
region: ls -la /home/leon/.bashrc via Bash returned "No such file or
directory" (the mount-level sandbox hides paths outside sandbox.allowedPaths
entirely). But Read(/home/leon/.bashrc) — same absolute path — succeeded
and returned the file's contents.

This indicates Read/Write/Edit are not subject to the same OS/mount-
level sandbox that constrains Bash, and their only access control is the
permissions.allow/deny rule engine — which, per the above, is not being
consulted (or is silently no-op'ing) before the file open for these rules.

Impact

Any project relying on permissions.deny with Read(...) glob rules to
keep Claude out of specific paths (secrets, other users' files, system
config, sibling projects, etc.) gets no actual protection from that rule for
the Read tool — only real OS file permissions and the (separately
configured, and much more limited) sandbox allowedPaths list provide any
enforcement. This is easy to miss because the /permissions UI displays the
rule as present and active, and explicitly states denied tools are "always"
rejected.

Suggested fix direction

Have the Read/Write/Edit tool handlers run the same
permissions.deny glob match that gates the interactive approval prompt
before performing the filesystem operation, independent of whether the
call would otherwise auto-approve/auto-deny via the sandbox. Alternatively,
if Read/Write/Edit are intentionally exempted from mount-level
sandboxing for legitimate reasons, that tradeoff should at minimum be
reflected honestly in the /permissions UI (e.g. "Deny rules apply to Bash
only" or similar), rather than claiming a universal guarantee.

What Should Happen?

Expected behavior

Every call above should be hard-blocked by the Deny rule — per the
/permissions UI's own description, Claude Code should "always reject
requests to use denied tools," with no prompt and no read.

Error Messages/Logs

Steps to Reproduce

Steps to reproduce

  1. In a project with .claude/settings.json containing "deny": ["Read(/etc/**)", "Read(/home/**)"] (and a narrower allow carving back only the project directory), start a session.
  2. Confirm via /permissions that the rules appear as active/duplicated in the Deny tab.
  3. Call the Read tool on a path matched by the deny rule, e.g.:
  • Read(/etc/passwd)
  • Read(/etc/hosts)
  • Read(/etc/bluetooth/main.conf) (nested, 2 levels deep)
  • Read(/etc/unbound/root.key) (nested)
  • Read(/home/leon/.bashrc) (outside the allowed project carve-out)

Claude Model

Sonnet (default)

Is this a regression?

Yes, this worked in a previous version

Last Working Version

_No response_

Claude Code Version

2.1.223

Platform

Anthropic API

Operating System

Other Linux

Terminal/Shell

Other

Additional Information

_No response_

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗