[Bug] Tool-scoped deny rules override sandbox network isolation at network level
Bug Description
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report
- [ ] I am using the latest version of Claude Code
What's Wrong?
Adding a tool-scoped deny rule like WebFetch(domain:github.com) to permissions.deny in .claude/settings.json causes the sandbox to block all network access to that domain — not just WebFetch tool calls. This breaks git push, git pull, git fetch, and any other operation that makes HTTPS connections to the denied domain.
The deny rule syntax WebFetch(domain:X) implies tool-level scoping (only the WebFetch tool should be affected), but at runtime it appears to be enforced as a network-level block in the sandbox proxy, effectively adding the domain to a denied hosts list. This overrides sandbox.network.allowedDomains even when the domain is explicitly listed there.
What Should Happen?
WebFetch(domain:github.com) in permissions.deny should only prevent the WebFetch tool from accessing github.com. Other tools and network operations — git over HTTPS, gh CLI calls, Bash commands — should remain unaffected.
When a domain appears in both sandbox.network.allowedDomains and a WebFetch(domain:...) deny rule, the sandbox should allow network traffic to that domain while blocking only the WebFetch tool from using it.
Error Messages/Logs
$ git push -u origin sandbox-beads
fatal: unable to access 'https://github.com/snowpackdata/tessellate.git/': CONNECT tunnel failed, response 403
The 403 from CONNECT tunnel failed indicates the sandbox HTTPS proxy is rejecting the connection at the network level, not that GitHub is returning a 403.
Steps to Reproduce
- Start with a working sandbox configuration where
github.comis inallowedDomainsand git push works:
{
"sandbox": {
"enabled": true,
"autoAllowBashIfSandboxed": true,
"enableWeakerNetworkIsolation": true,
"network": {
"allowedDomains": [
"github.com",
"api.github.com",
"uploads.github.com"
]
}
},
"permissions": {
"deny": []
}
}
- Confirm
git pushworks in a Claude Code session (Bash tool) - Add a WebFetch-scoped deny rule:
{
"permissions": {
"deny": [
"WebFetch(domain:github.com)"
]
}
}
- Start a new Claude Code session (settings reload on session start)
- Ask Claude to run
git pushvia the Bash tool - Observe:
CONNECT tunnel failed, response 403 - Remove the
WebFetch(domain:github.com)deny rule, restart session git pushworks again immediately — no other changes needed
Runtime sandbox state
When the deny rule was active, the runtime sandbox config injected into the session showed:
Network: {
"allowedHosts": ["github.com", "api.github.com", ...],
"deniedHosts": ["github.com", "raw.githubusercontent.com"]
}
The deniedHosts array does not exist in our settings.json — it was synthesized at runtime from the WebFetch(domain:...) deny rules. Since denied hosts override allowed hosts, all network access to github.com was blocked.
Is this a regression?
I don't know
Claude Code Version
2.1.81 (Claude Code)
Platform
Anthropic API
Operating System
macOS 15.5 (Build 24F74), Apple Silicon (arm64)
Terminal/Shell
Ghostty
Additional Information
Full sandbox configuration (working state, after removing the deny rule)
{
"sandbox": {
"enabled": true,
"autoAllowBashIfSandboxed": true,
"allowUnsandboxedCommands": false,
"enableWeakerNetworkIsolation": true,
"excludedCommands": ["docker", "docker-compose", "subl"],
"filesystem": {
"allowRead": ["~/.docker/config.json", "~/.ssh/known_hosts", ".env"],
"allowWrite": ["~/bin", "/tmp", "/private/tmp", "~/.bun", "~/.tessellate", "~/Snowpack Data Downloads"]
},
"network": {
"allowedDomains": [
"github.com", "api.github.com", "uploads.github.com",
"registry.npmjs.org", "api.anthropic.com", "code.claude.com",
"docs.anthropic.com", "snowpack-data.com", "localhost"
],
"allowUnixSockets": ["~/.docker/run/docker.sock"],
"allowLocalBinding": true
}
}
}
Use case
We use Claude Code with a comprehensive sandbox configuration for a multi-agent development workflow. We added WebFetch(domain:github.com) to `permissi…
Note: Content was truncated.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗