[Bug] filesystem.denyRead not merged with Read(...) deny permissions in sandbox
Resolved 💬 4 comments Opened May 1, 2026 by devpikachu Closed Jun 1, 2026
Bug Description
Sandbox documentation claim that filesystem.denyRead gets merged with Read(...) deny permission rules. In practice, the harness is able to Bash(cat) paths that are in the permission Read(...) deny list, but not in the sandbox filesystem.denyRead.
The documentation states:
filesystem.denyReadPaths where sandboxed commands cannot read. Arrays are merged across all settings scopes. Also merged with paths fromRead(...)deny permission rules.
In practice, Claude Code:
What the docs and system prompt claim:
- code.claude.com/docs/en/settings#sandbox-settings: Read(...) deny rules merge into sandbox.filesystem.denyRead, enforced at OS-level boundary, "applies to all subprocess commands".
- The Bash tool description in this very session shows Filesystem: {"read":{"denyOnly":["./.idea","./.bruno"], …}} — confirming the merge happened at session start.
What actually happens:
┌───────────────────────────────────────┬────────────────┬─────────────────────────┐
│ Path access │ Tool │ Result │
├───────────────────────────────────────┼────────────────┼─────────────────────────┤
│ .bruno/workspace.yml │ Read tool │ denied ✓ │
├───────────────────────────────────────┼────────────────┼─────────────────────────┤
│ .bruno/workspace.yml │ cat (relative) │ succeeds ✗ │
├───────────────────────────────────────┼────────────────┼─────────────────────────┤
│ /home/.../.bruno/workspace.yml │ cat (absolute) │ succeeds ✗ │
├───────────────────────────────────────┼────────────────┼─────────────────────────┤
│ .bruno/ │ ls │ succeeds ✗ │
├───────────────────────────────────────┼────────────────┼─────────────────────────┤
│ recursive grep into .bruno │ grep -r │ succeeds ✗ │
├───────────────────────────────────────┼────────────────┼─────────────────────────┤
│ cat .../workspace.yml │ sh -c │ succeeds ✗ │
├───────────────────────────────────────┼────────────────┼─────────────────────────┤
│ /home/.../sandbox-test-$$ │ echo > │ denied (read-only FS) ✓ │
└───────────────────────────────────────┴────────────────┴─────────────────────────┘
So:
- The write half of filesystem enforcement is real (you can't write outside the sandbox-permitted paths — bubblewrap remounts ro).
- The read half (denyRead from Read(...) merge) is not enforced for Bash subprocess reads in this Claude Code build, despite the docs saying it should be.
Environment Info
- Platform: linux
- Terminal: WarpTerminal
- Version: 2.1.126
- Feedback ID: 95fa306f-8e37-42c2-aa54-29474a62bbe5
Errors
[{"error":"Error: Tool ListMcpResourcesTool not found\n at g27 (/$bunfs/root/src/entrypoints/cli.js:3117:36246)\n at sA (/$bunfs/root/src/entrypoints/cli.js:497:21130)\n at a3 (/$bunfs/root/src/entrypoints/cli.js:497:39896)\n at l9 (/$bunfs/root/src/entrypoints/cli.js:497:50705)\n at cU (/$bunfs/root/src/entrypoints/cli.js:497:87759)\n at qJ8 (/$bunfs/root/src/entrypoints/cli.js:497:86717)\n at I4H (/$bunfs/root/src/entrypoints/cli.js:497:86538)\n at E4H (/$bunfs/root/src/entrypoints/cli.js:497:82863)\n at sH (/$bunfs/root/src/entrypoints/cli.js:497:6551)\n at kH (/$bunfs/root/src/entrypoints/cli.js:497:5005)\n at async (/$bunfs/root/src/entrypoints/cli.js:18826:2595)","timestamp":"2026-05-01T10:14:52.953Z"},{"error":"Error: Tool ListMcpResourcesTool not found\n at g27 (/$bunfs/root/src/entrypoints/cli.js:3117:36246)\n at sA (/$bunfs/root/src/entrypoints/cli.js:497:21130)\n at a3 (/$bunfs/root/src/entrypoints/cli.js:497:39896)\n at l9 (/$bunfs/root/src/entrypoints/cli.js:497:50705)\n at cU (/$bunfs/root/src/entrypoints/cli.js:497:87759)\n at qJ8 (/$bunfs/root/src/entrypoints/cli.js:497:86717)\n at I4H (/$bunfs/root/src/entrypoints/cli.js:497:86538)\n at E4H (/$bunfs/root/src/entrypoints/cli.js:497:82863)\n at sH (/$bunfs/root/src/entrypoints/cli.js:497:6551)\n at kH (/$bunfs/root/src/entrypoints/cli.js:497:5005)","timestamp":"2026-05-01T10:14:53.049Z"},{"error":"Error: Tool ListMcpResourcesTool not found\n at g27 (/$bunfs/root/src/entrypoints/cli.js:3117:36246)\n at sA (/$bunfs/root/src/entrypoints/cli.js:497:21130)\n at a3 (/$bunfs/root/src/entrypoints/cli.js:497:39896)\n at l9 (/$bunfs/root/src/entrypoints/cli.js:497:50705)\n at cU (/$bunfs/root/src/entrypoints/cli.js:497:87759)\n at qJ8 (/$bunfs/root/src/entrypoints/cli.js:497:86717)\n at I4H (/$bunfs/root/src/entrypoints/cli.js:497:86538)\n at E4H (/$bunfs/root/src/entrypoints/cli.js:497:82863)\n at sH (/$bunfs/root/src/entrypoints/cli.js:497:6551)\n at kH (/$bunfs/root/src/entrypoints/cli.js:497:5005)","timestamp":"2026-05-01T10:14:53.152Z"},{"error":"Error: Tool mcp__linear-server__get_issue not found\n at g27 (/$bunfs/root/src/entrypoints/cli.js:3117:36246)\n at sA (/$bunfs/root/src/entrypoints/cli.js:497:21130)\n at a3 (/$bunfs/root/src/entrypoints/cli.js:497:39896)\n at l9 (/$bunfs/root/src/entrypoints/cli.js:497:50705)\n at cU (/$bunfs/root/src/entrypoints/cli.js:497:87759)\n at qJ8 (/$bunfs/root/src/entrypoints/cli.js:497:86717)\n at I4H (/$bunfs/root/src/entrypoints/cli.js:497:86538)\n at E4H (/$bunfs/root/src/entrypoints/cli.js:497:82863)\n at sH (/$bunfs/root/src/entrypoints/cli.js:497:6551)\n at kH (/$bunfs/root/src/entrypoints/cli.js:497:5005)","timestamp":"2026-05-01T10:14:53.184Z"},{"error":"Error: File does not exist. Note: your current working directory is /home/andrei/Repositories/omnilium/cloud.\n at call (/$bunfs/root/src/entrypoints/cli.js:4868:11399)\n at processTicksAndRejections (native:7:39)","timestamp":"2026-05-01T10:15:09.119Z"},{"error":"McpToolCallError: failed to create pull request: POST https://api.github.com/repos/[REDACTED]/cloud/pulls: 403 Resource protected by organization SAML enforcement. You must grant your Personal Access token access to an organization within this enterprise. []\n at Ng_ (/$bunfs/root/src/entrypoints/cli.js:4894:63)\n at eF7 (/$bunfs/root/src/entrypoints/cli.js:4894:1409)\n at processTicksAndRejections (native:7:39)","timestamp":"2026-05-01T10:15:30.584Z"}]
settings.json
{
"$schema": "https://json.schemastore.org/claude-code-settings.json",
"permissions": {
"allow": [
"Read",
"Glob",
"Grep",
"Edit",
"Write",
"WebSearch",
"Bash(cat:*)",
"Bash(gh:*)",
"Bash(git:*)",
"Bash(go-task:*)",
"Bash(head:*)",
"Bash(jq:*)",
"Bash(task:*)",
[...]
],
"deny": [
"Read(./.idea/**)",
"Edit(./.idea/**)",
"Write(./.idea/**)",
"Glob(./.idea/**)",
"Grep(./.idea/**)",
"Read(./.bruno/**)",
"Edit(./.bruno/**)",
"Write(./.bruno/**)",
"Glob(./.bruno/**)",
"Grep(./.bruno/**)",
[...]
]
},
"sandbox": {
"enabled": true,
"failIfUnavailable": true,
"autoAllowBashIfSandboxed": true,
"allowUnsandboxedCommands": false,
"network": {
"allowAllUnixSockets": true,
"allowedDomains": [
[...]
]
}
},
"enabledPlugins": {
"security-guidance@claude-plugins-official": true,
"claude-md-management@claude-plugins-official": true
},
"hooks": {
"PreToolUse": [
[...]
],
"PostToolUse": [
[...]
]
}
}This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗