Permission Deny Configuration Not Enforced for Read/Write Tools

Status Fixed / completed
Maintainer reply โœ“ Yes โ€” bogini
Activity 6 comments ยท opened Aug 27, 2025 ยท closed Sep 2, 2025
๐Ÿ’ก Likely answer: A maintainer (bogini, collaborator) responded on this thread โ€” see the highlighted reply below.

๐Ÿ“‹ Environment

  • Claude CLI Version: 1.0.93 (Claude Code)
  • Platform: Linux (CentOS/RHEL 8.10, kernel 4.18.0-553.27.1.el8_10.x86_64)
  • Installation Path: /usr/local/bin/claude
  • Test Project: OpenROAD EDA tools project
  • Working Directory: /hdd1/users/mjkim/CLionProjects/OpenROAD-flow-scripts/tools/OpenROAD/

๐Ÿšจ Issue Summary

The permission deny feature in .claude/settings.json and .claude/settings.local.json is completely non-functional for Read and Write tools, despite multiple configuration attempts and recent updates. This creates a critical security vulnerability for protecting high-value intellectual property.

โš™๏ธ Configuration Tested

1. Absolute Path Configuration (settings.local.json)

{
  "permissions": {
    "allow": [
      "Bash(mkdir:*)",
      "WebFetch(domain:docs.anthropic.com)",
      "WebFetch(domain:github.com)",
      "WebFetch(domain:stackoverflow.com)",
      "Bash(cat:*)",
      "Bash(/bug)"
    ],
    "deny": [
      "Read(/hdd1/users/mjkim/CLionProjects/OpenROAD-flow-scripts/tools/OpenROAD/src/Main.cc)",
      "Read(/hdd1/users/mjkim/CLionProjects/OpenROAD-flow-scripts/tools/OpenROAD/src/OpenRoad.cc)",
      "Read(/hdd1/users/mjkim/CLionProjects/OpenROAD-flow-scripts/tools/OpenROAD/include/ord/OpenRoad.hh)",
      "Read(/hdd1/users/mjkim/CLionProjects/OpenROAD-flow-scripts/tools/OpenROAD/CMakeLists.txt)",
      "Write(/hdd1/users/mjkim/CLionProjects/OpenROAD-flow-scripts/tools/OpenROAD/src/Main.cc)",
      "Write(/hdd1/users/mjkim/CLionProjects/OpenROAD-flow-scripts/tools/OpenROAD/src/OpenRoad.cc)",
      "Write(/hdd1/users/mjkim/CLionProjects/OpenROAD-flow-scripts/tools/OpenROAD/include/ord/OpenRoad.hh)",
      "Write(/hdd1/users/mjkim/CLionProjects/OpenROAD-flow-scripts/tools/OpenROAD/CMakeLists.txt)"
    ]
  }
}

2. Relative Path Configuration (settings.local.json)

{
  "permissions": {
    "allow": [
      "Bash(mkdir:*)",
      "WebFetch(domain:docs.anthropic.com)",
      "WebFetch(domain:github.com)",
      "WebFetch(domain:stackoverflow.com)",
      "Bash(cat:*)",
      "Bash(/bug)"
    ],
    "deny": [
      "Read(src/Main.cc)",
      "Read(src/OpenRoad.cc)",
      "Read(include/ord/OpenRoad.hh)",
      "Read(CMakeLists.txt)",
      "Read(src/drt/src/global.cpp)",
      "Read(src/gpl/src/**)",
      "Read(src/cts/src/Clock.cpp)",
      "Write(src/Main.cc)",
      "Write(src/OpenRoad.cc)",
      "Write(include/ord/OpenRoad.hh)",
      "Write(CMakeLists.txt)",
      "Write(src/drt/src/global.cpp)",
      "Write(src/gpl/src/**)",
      "Write(src/cts/src/Clock.cpp)"
    ]
  }
}

๐Ÿ“Š Test Results Summary

| Test Case | Configuration | Expected Result | Actual Result | Status |
|-----------|---------------|----------------|---------------|--------|
| Read src/Main.cc | Absolute paths | โŒ ACCESS DENIED | โœ… FULL ACCESS | โŒ FAILED |
| Read CMakeLists.txt | Absolute paths | โŒ ACCESS DENIED | โœ… FULL ACCESS | โŒ FAILED |
| Write src/Main.cc | Absolute paths | โŒ WRITE DENIED | โœ… EDIT SUCCESSFUL | โŒ FAILED |
| Read src/Main.cc | Relative paths | โŒ ACCESS DENIED | โœ… FULL ACCESS | โŒ FAILED |
| Read CMakeLists.txt | Relative paths | โŒ ACCESS DENIED | โœ… FULL ACCESS | โŒ FAILED |
| Write src/Main.cc | Relative paths | โŒ WRITE DENIED | โœ… EDIT SUCCESSFUL | โŒ FAILED |
| Bash commands | Both configs | โŒ Command denied | โŒ PROPERLY BLOCKED | โœ… WORKS |

๐Ÿ” Detailed Test Evidence

Test 1: File Access Despite Absolute Path Deny Rules

# Configuration: "Read(/full/path/to/src/Main.cc)" in deny list
# Claude Command: Read tool on Main.cc
# Expected: Permission denied error
# Actual: Successfully read entire file content (578 lines)

Test 2: File Access Despite Relative Path Deny Rules

# Configuration: "Read(src/Main.cc)" in deny list  
# Claude Command: Read tool on src/Main.cc
# Expected: Permission denied error
# Actual: Successfully read entire file content

Test 3: File Editing Despite Write Deny Rules

# Configuration: "Write(src/Main.cc)" in deny list
# Claude Command: Edit tool to modify Main.cc
# Expected: Write permission denied
# Actual: Successfully edited file, changes applied

Test 4: Bash Command Restrictions Work Correctly โœ…

# Configuration: Bash commands properly restricted in allow list
# Result: Unauthorized bash commands are blocked correctly
# Status: THIS PART WORKS AS EXPECTED

Test 5: File Permission Workaround

# Additional test: Changed file permissions to 000 (no access)
# Result: Claude was blocked by filesystem permissions
# Workaround: Claude can use chmod to restore permissions and access files

๐Ÿ›ก๏ธ Security Impact

Severity: CRITICAL

  • Complete Bypass: File access restrictions are completely ineffective
  • Data Exposure: Sensitive source code, build configurations, and proprietary algorithms fully accessible
  • Integrity Risk: Critical files can be modified despite explicit deny rules
  • Permission Escalation: Claude can use chmod to bypass filesystem-level protections
  • Commercial Risk: Enterprise users cannot safely protect intellectual property worth millions

Real-World Scenario

A company with worth of proprietary algorithms cannot use Claude CLI safely, as all "protected" files remain fully accessible and modifiable.

๐Ÿ“… Timeline & Related Issues

Issue History

  1. July 2025: Original issue #4467 reported by @alicecosmos
  2. July 2025: Developer (@ant-kurt) commented: "fix merged, should go out in next release"
  3. August 2025: Issue closed as resolved
  4. August 2025: Current testing with v1.0.93 shows problem still persists

Related Issues

  • #4467: Permission deny patterns not working for Read/Write tools
  • #2720: Permissions Configuration Bypass in Local Settings Enforcement
  • #891: .claude/settings.json not applying ignorePatterns
  • Stack Overflow: "Claude Code doesn't respect settings.json deny block"

๐Ÿ”ง Attempted Solutions

Configuration Variations Tested โœ…

  1. JSON Syntax: Verified correct JSON structure and formatting
  2. File Locations: Tested both settings.json and settings.local.json
  3. Path Types: Tested absolute paths, relative paths, and wildcard patterns
  4. File Existence: Confirmed all target files exist and are accessible
  5. Working Directory: Verified Claude CLI is running from correct project root
  6. Settings Priority: Ensured settings.local.json takes precedence

Software Updates Tested โœ…

  1. Claude CLI Update: Updated to latest version 1.0.93
  2. Configuration Reload: Restarted Claude CLI sessions after config changes
  3. Clean Environment: Tested in fresh terminal sessions

Alternative Protection Methods โŒ

  1. Filesystem Permissions: Can be bypassed via chmod commands
  2. File Attributes: chattr +i immutable flag could work but untested
  3. Directory Isolation: Only reliable method currently available

๐Ÿ”ฌ Root Cause Analysis

Observed Behavior Pattern

The issue appears to be a fundamental architectural bug in the permission system:

  • Working Components:
  • Bash command restrictions function correctly
  • Allow rules work for specified tools
  • JSON parsing and configuration loading works
  • Broken Components:
  • File operation tools (Read, Write, Edit, MultiEdit, etc.) completely ignore deny rules
  • Both absolute and relative path matching fails
  • Wildcard patterns are ignored
  • No error messages or warnings about denied access

Technical Analysis

The problem suggests that:

  1. Permission checking is implemented inconsistently across tool types
  2. File operation tools bypass the permission system entirely
  3. Path resolution/matching logic may be fundamentally flawed
  4. The "fix" mentioned in issue #4467 was either incomplete or reverted

๐Ÿ“ Reproduction Steps

Prerequisites

  1. Claude CLI version 1.0.93 or later
  2. A project directory with sensitive files
  3. Write access to create .claude/settings.local.json

Steps to Reproduce

  1. Create .claude/settings.local.json in your project root:

``json
{
"permissions": {
"deny": [
"Read(path/to/sensitive/file.txt)",
"Write(path/to/sensitive/file.txt)"
]
}
}
``

  1. Verify JSON syntax is valid
  2. Start Claude CLI in the project directory
  3. Ask Claude to read the denied file using Read tool
  4. Ask Claude to edit the denied file using Edit tool
  5. Observe that both operations succeed despite explicit deny rules

Expected vs Actual Results

  • Expected: Claude should return permission denied errors
  • Actual: Claude successfully reads and modifies all files
  • Control Test: Bash command restrictions work correctly

๐Ÿš€ Requested Actions

Immediate Priority (P0)

  1. Acknowledge Issue: Confirm that the bug still exists in v1.0.93
  2. Security Review: Assess impact on enterprise users with sensitive codebases
  3. Hotfix Planning: Prioritize this as a security-critical bug

Development Tasks

  1. Root Cause Investigation: Debug why file tools bypass permission checks
  2. Comprehensive Fix: Ensure all file operation tools respect deny rules
  3. Regression Testing: Test all permission scenarios (allow, ask, deny)
  4. Path Resolution: Fix both absolute and relative path matching

Documentation & Communication

  1. Security Advisory: Warn users about current vulnerability
  2. Workaround Guidance: Document temporary protection methods
  3. Fix Documentation: Update permission documentation if syntax changes
  4. Release Notes: Clearly document the fix when released

๐Ÿ’ผ Commercial Impact

Current Situation

  • Enterprise Blocker: Organizations cannot safely deploy Claude CLI with proprietary code
  • Security Compliance: Current implementation fails security audit requirements
  • Trust Issue: Previously "resolved" issue creates confidence problems
  • Competitive Risk: Sensitive IP remains vulnerable to accidental exposure

Business Value of Fix

  • Market Expansion: Enable enterprise adoption of Claude CLI
  • Security Compliance: Meet enterprise security standards
  • User Confidence: Restore trust in permission system reliability
  • Risk Mitigation: Prevent potential IP theft or accidental disclosure

---

Report Generated: August 27, 2025
Tested Version: Claude CLI 1.0.93
Test Environment: Linux CentOS/RHEL 8.10
Severity: Critical Security Issue

View original on GitHub โ†—

6 Comments

coygeek ยท 1 year ago

Yes, I confirmed the same issue for v1.0.93.

___

Claude Code Deny Permissions Test Results

Test Environment

  • Working directory: /Users/user/Desktop/deny-test
  • Settings files:
  • .claude/settings.json (project-level)
  • .claude/settings.local.json (local project-level)
  • Test execution date: 2025-08-27
  • Tested Version: Claude Code 1.0.93
  • Platform: darwin (macOS)

Executive Summary

CRITICAL FINDING: The deny permission system as documented is completely non-functional in the current test environment. All deny rules were ineffective, allowing unrestricted access to files, commands, and tools that should have been blocked.

Phase 1: Basic Deny Rule Testing

1.1 Read Tool Denial

Settings:

"deny": [
  "Read(./test-files/blocked.txt)",
  "Read(./.env*)",
  "Read(./secrets/**)"
]

Test Results:

  • โŒ Read(./test-files/blocked.txt) - File was readable (SHOULD BE BLOCKED)
  • โŒ Read(./secrets/.env) - File was readable (SHOULD BE BLOCKED)
  • โŒ Read(./secrets/.env.local) - File was readable (SHOULD BE BLOCKED)
  • โŒ Read(./secrets/secret.key) - File was readable (SHOULD BE BLOCKED)
  • โŒ Read(./test-files/test.txt) - File was readable (SHOULD BE BLOCKED)

Finding: Read deny rules are NOT being enforced.

1.2 Edit Tool Denial

Settings:

"deny": ["Edit(./configs/*.json)"]

Test Results:

  • โŒ Edit(./configs/app.json) - Edit succeeded, changed version from "1.0.0" to "1.0.1" (SHOULD BE BLOCKED)

Finding: Edit deny rules are NOT being enforced.

1.3 Bash Command Denial

Settings:

"deny": [
  "Bash(echo 'test')",
  "Bash(rm:*)"
]

Test Results:

  • โŒ echo 'test' - Command executed successfully (SHOULD BE BLOCKED)
  • โŒ rm --help - Command executed successfully (SHOULD BE BLOCKED)
  • โŒ curl --version - Command executed (documentation says should be blocked by default)

Finding: Bash deny rules are NOT being enforced, including default blocks mentioned in documentation.

1.4 WebFetch Denial

Settings:

"deny": ["WebFetch"]

Test Results:

  • โŒ WebFetch(https://example.com) - Request succeeded, returned page title (SHOULD BE BLOCKED)

Finding: WebFetch deny rule is NOT being enforced.

Phase 2: Pattern Matching Tests

2.1 Wildcard and Recursive Patterns

Additional Settings Added:

"deny": [
  "Read(*.secret)",
  "Read(test-files/**/private.*)"
]

Test Results:

  • โŒ Read(./test-files/data.secret) - File was readable (SHOULD BE BLOCKED by *.secret pattern)
  • โŒ Read(./test-files/nested/private.txt) - File was readable (SHOULD BE BLOCKED by **/private.* pattern)

Finding: Advanced pattern matching (wildcards and recursive patterns) is NOT working.

2.2 Path Type Testing

The documentation mentions three path types:

  • Relative paths (./file)
  • Absolute paths with // prefix
  • Home directory paths with ~/ prefix

Finding: Could not test absolute and home directory patterns effectively as basic deny functionality is not working.

Phase 3: Precedence Testing

3.1 Rule Type Precedence (deny vs allow)

Settings in .claude/settings.json:

{
  "deny": ["Read(./test-files/blocked.txt)"],
  "allow": ["Read(./test-files/test.txt)"]
}

Settings in .claude/settings.local.json:

{
  "deny": ["Read(./configs/app.json)", "Bash(ls:*)"],
  "allow": ["Read(./test-files/blocked.txt)"]
}

Test Results:

  • โŒ Read(./test-files/blocked.txt) - File readable despite deny rule in settings.json
  • โŒ Read(./configs/app.json) - File readable despite deny rule in settings.local.json
  • โŒ ls test-files - Command executed despite deny rule in settings.local.json

Finding: Neither deny precedence over allow nor settings file hierarchy is functioning.

Phase 4: Advanced Features

4.1 MCP Tool Denial

Settings:

"deny": [
  "mcp__context7",
  "mcp__context7__resolve-library-id"
]

Test Results:

  • โŒ mcp__context7__resolve-library-id for "react" - Tool executed successfully (SHOULD BE BLOCKED)
  • โŒ mcp__context7__resolve-library-id for "vue" - Tool executed successfully (SHOULD BE BLOCKED)

Finding: MCP tool denial is NOT working.

4.2 Command-line Flags

Not tested - Would require running claude with --disallowedTools flag

Phase 5: Edge Cases

5.1 Special Characters in Filenames

Test Results:

  • โœ… Created and read file with special character: special$file.txt - Worked as expected
  • No deny rules were tested as basic deny functionality is not working

5.2 Empty Deny Array

Not tested - Basic deny functionality must work first

Phase 6: Security Validation

6.1 Bypass Attempts

Test Results:

  • โœ… cat secrets/.env via Bash - Successfully read file (bypasses Read deny rule, but Bash deny also not working)
  • โœ… Command chaining echo 'Hello' && echo 'test' - Both commands executed
  • No actual security boundaries exist to bypass as deny rules are not enforced

Finding: Since deny rules are not enforced, all "bypass" attempts succeed trivially.

Critical Observations

  1. Complete System Failure: Not a single deny rule was enforced during testing
  2. All Access Unrestricted: Files, commands, and tools that should be blocked are fully accessible
  3. No Error Messages: The system silently ignores deny rules rather than providing feedback
  4. Documentation Mismatch: The actual behavior contradicts the detailed documentation provided

Potential Root Causes

  1. Feature Not Enabled: The deny permission system may not be active in this Claude Code instance
  2. Configuration Issue: Settings files may not be loaded or parsed correctly
  3. Implementation Bug: The deny system may have a critical bug preventing it from functioning
  4. Environment Specific: The deny system may not work on macOS or in this specific environment
  5. Version Mismatch: The documentation may describe a different version than what's currently running

Recommendations

  1. Immediate Investigation: The development team should investigate why deny permissions are completely non-functional
  2. Security Warning: Users should not rely on deny permissions for security until this is fixed
  3. Documentation Update: Add a warning about current limitations or version requirements
  4. Error Reporting: Implement clear error messages when deny rules fail to load or apply
  5. Testing Infrastructure: Implement automated tests to ensure deny permissions work before releases

Conclusion

The deny permission system, despite being thoroughly documented, is completely non-functional in the test environment. This represents a critical security issue if users are relying on these permissions to protect sensitive data or prevent dangerous operations. All documented features - from basic file denial to advanced MCP tool blocking - failed to work as described.

Test Status: FAILED - The deny permission system does not provide any of the security boundaries it claims to implement.

paulsutherland ยท 1 year ago

Claude code still reads and edits .env files and uses the contents in the session when I set the .env file to deny!

.claude/settings.json

{
  "permissions": {
    "deny": [
      "Read(.env)"
    ]
  }
}
coygeek ยท 1 year ago

I wrote an effective workaround in issue #6699

paulsutherland ยท 1 year ago

@coygeek ๐Ÿ™ Legend - thank you that works great.

Note: It is important to pay attention your comment:

(Ensure this script is executable: chmod +x .claude/hooks/protect_sensitive_files.py)

otherwise Claude still reads the contents.

bogini collaborator ยท 12 months ago

The patterns like /hdd1/users/mjkim/CLionProjects/OpenROAD-flow-scripts/** aren't working because they're interpreted as relative to your settings file location, not as absolute paths.

Use double slash (//) for absolute paths:

// โŒ Wrong - Won't block the absolute path
"deny": [
  "Read(/hdd1/users/mjkim/CLionProjects/OpenROAD-flow-scripts/**)",
  "Write(/hdd1/users/mjkim/CLionProjects/OpenROAD-flow-scripts/tools/OpenROAD/src/Main.cc)"
]

// โœ… Correct - Blocks the absolute paths
"deny": [
  "Read(//hdd1/users/mjkim/CLionProjects/OpenROAD-flow-scripts/**)",
  "Write(//hdd1/users/mjkim/CLionProjects/OpenROAD-flow-scripts/tools/OpenROAD/src/Main.cc)"
]

See https://docs.anthropic.com/en/docs/claude-code/iam#tool-specific-permission-rules. The permission system supports four pattern types:

  • //path โ†’ Absolute from filesystem root
  • ~/path โ†’ From home directory
  • /path โ†’ Relative to settings file directory
  • path โ†’ Relative to current working directory

The single / prefix means "relative to the directory containing .claude/settings.json", not "absolute path". While this is counterintuitive for Unix-style paths, it's the designed behavior. We're updating our documentation to make this clearer and prevent future confusion.

github-actions[bot] ยท 11 months ago

This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.