Read() deny rules in settings.json do not block the built-in Read tool
Summary
deny rules using the Read(...) permission pattern in settings.json do not block Claude Code's built-in Read tool from accessing the specified paths. The rules appear to have no effect on the built-in tool, making it impossible to restrict file access via settings.
Steps to Reproduce
- Add deny rules to
~/.claude/settings.json:
{
"permissions": {
"allow": [
"Read(/Users/me/safe-folder/**)"
],
"deny": [
"Read(/Users/me/.ssh/*)",
"Read(/Users/me/.ssh/**)"
]
}
}
- Restart Claude Code
- Ask Claude to read a file inside
~/.ssh/using the built-in Read tool
Expected Behavior
The Read tool should be blocked from accessing paths matched by deny rules. Claude should either refuse silently or prompt the user for confirmation.
Actual Behavior
The built-in Read tool successfully reads files in ~/.ssh/ without prompting, completely bypassing the deny rules.
Additional Observations
- The deny rules seem to have no effect regardless of whether
*or**globs are used - Even when the broad allow rule was removed (so no allow rule matched
~/.ssh/**), the Read tool still accessed the files without prompting — suggesting the built-in Read tool bypasses the permission system entirely Bash(...)deny rules may work correctly (not tested), but theRead(...)deny rules do not
Impact
Users cannot use settings.json to protect sensitive files (SSH keys, credentials, etc.) from being read by Claude Code's built-in Read tool. This is a security concern, as users may reasonably expect that deny rules provide actual access control.
Environment
- macOS Darwin 25.3.0
- Claude Code (claude-sonnet-4-6)
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗