[BUG] Permission deny patterns not working for Read/Write tools in settings.local.json
Environment
- Platform (select one):
- [x] Anthropic API
- [ ] AWS Bedrock
- [ ] Google Vertex AI
- [ ] Other: <!-- specify -->
- Claude CLI version: 1.0.61 (Claude Code)
- Operating System: Windows 11
- Terminal: Windows Terminal
Bug Description
I discovered that permission deny patterns in settings.local.json are not working for Read and Write tools, despite working correctly for Bash commands. Initially, I tried to protect .env files containing sensitive data, but Claude Code could still read them despite deny patterns. As an alternative security measure, I created a dedicated secrets/ directory structure to isolate sensitive information (API keys, database passwords, etc.) and attempted to protect it using deny patterns, but Claude Code can still access all files within this directory as well. This creates a critical security vulnerability where sensitive files cannot be protected from Claude Code access regardless of the approach used.
Steps to Reproduce
- First, attempt to protect
.envfiles by creating asettings.local.jsonfile with deny patterns:
``json``
{
"permissions": {
"deny": [
"Read(*.env)",
"Read(.env*)"
]
}
}
- Test
.envfile protection:
- Create
.envfile with sensitive environment variables - Try
Read(.env)tool → FAILS: File is still readable despite deny pattern
- As an alternative security approach, create a dedicated
secrets/directory structure:
````
secrets/
├── keys/
│ ├── api_key.txt
│ └── database_password.txt
└── certificates/
- Update
settings.local.jsonto include secrets directory protection:
``json``
{
"permissions": {
"deny": [
"Read(*.env)",
"Read(.env*)",
"Read(secrets/**)",
"Read(**/secrets/**)",
"Read(id_rsa)",
"Write(.env*)",
"Write(**/secrets/**)"
]
}
}
- Populate the secrets directory with sensitive data:
secrets/keys/api_key.txtwith secret API keysecrets/keys/database_password.txtwith database credentials- Also create
id_rsaSSH private key file for additional testing
- Test access using Claude Code tools to verify the deny patterns work:
- Try
Read(.env)tool - Try
Read(secrets/keys/api_key.txt)tool - Try
Read(id_rsa)tool - Try
Write(.env.test)tool
- Compare with Bash command restrictions (which work correctly):
- Try
Bash(rm test.txt)(should be denied) - Try
Bash(sudo echo test)(should be denied)
Expected Behavior
Both the .env files and the secrets/ directory should be inaccessible to Claude Code tools, returning permission denied errors similar to Bash commands. This would allow developers to either protect individual sensitive files or organize sensitive information in protected directories.
Actual Behavior
- ✅ Bash command restrictions work correctly:
Bash(sudo:*)→ "Permission to use Bash with command sudo has been denied."Bash(rm:*)→ "Permission to use Bash with command rm has been denied."
- ❌ Read/Write tool restrictions are completely ignored:
Read(.env)→ Successfully reads file content (despite deny pattern)Read(secrets/keys/api_key.txt)→ Successfully reads secret API key (despite deny pattern)Read(secrets/keys/database_password.txt)→ Successfully reads database credentials (despite deny pattern)Read(id_rsa)→ Successfully reads SSH private key (despite deny pattern)Write(.env.test)→ Successfully creates file (despite deny pattern)
Both the original .env files AND all files in the newly created secrets/ directory remain fully accessible to Claude Code, making both protection approaches completely ineffective.
Additional Context
Security Impact: This completely undermines attempts to create secure project structures. Developers cannot safely organize sensitive information (API keys, database credentials, SSH keys) in dedicated directories, as Claude Code ignores all file-based deny patterns. This forces developers to either avoid using Claude Code on projects with sensitive data or resort to external workarounds.
Tested Patterns (All Failed):
We tested multiple pattern variations, none worked for Read/Write tools:
"Read(*.env)",
"Read(.env*)",
"Read(secrets/**)",
"Read(**/secrets/**)",
"Read(secrets/keys/*)",
"Write(.env*)",
"Write(**/secrets/**)"
Current Workarounds:
- Store secrets outside the project directory
- Use system environment variables exclusively
- Encrypt sensitive files
- Use external secret management tools
Pattern Matching Inconsistency: The permission system works perfectly for Bash commands but completely fails for file operation tools (Read, Write, LS), suggesting an implementation issue in the pattern matching logic for these specific tools.
This appears to be a critical security bug that should be prioritized for fixing, as it undermines the entire purpose of the permission system for file access control.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗