sandbox.network.allowedDomains does not work for Node.js processes (DNS resolution blocked)
Summary
When Node.js CLI tools (e.g., tools using fetch() or dns.lookup()) are executed via the Bash tool, they fail with ENOTFOUND DNS errors even when the target domain is listed in sandbox.network.allowedDomains and the command is in excludedCommands. The same domain resolves and connects successfully with curl under the identical sandbox configuration.
Root Cause Analysis
The sandbox network allowlist operates at the system call level, intercepting DNS resolution from the C library resolver (used by curl and similar tools). However, Node.js uses libuv's getaddrinfo implementation via a thread pool, which the sandbox intercepts and blocks at a different layer. This means the allowlist is effectively bypassed for any process that does not use the standard C library DNS resolution path.
Reproduction
Settings
{
"sandbox": {
"excludedCommands": ["ncli"],
"network": {
"allowedDomains": ["mcp.notion.com", "api.notion.com"]
}
}
}
Test Results
All tests were performed in clean subagent and worktree sessions to eliminate caching or session-state effects.
| Test | Sandbox | Tool | Result |
|------|---------|------|--------|
| Fetch mcp.notion.com | ON | curl | HTTP 401 (connected OK) |
| Fetch mcp.notion.com | ON | Node.js fetch() | ENOTFOUND (blocked) |
| Fetch mcp.notion.com | OFF | Node.js fetch() | HTTP 404 (connected OK) |
| dns.lookup('mcp.notion.com') | ON | Node.js dns module | ENOTFOUND |
| dns.lookup('mcp.notion.com') | OFF | Node.js dns module | 208.103.161.2 (resolved OK) |
Steps to Reproduce
- Add a domain to
sandbox.network.allowedDomainsin project or user settings. - Run a Node.js CLI tool that makes a
fetch()request to that domain via the Bash tool:
``bash``
node -e "fetch('https://mcp.notion.com').then(r => console.log(r.status)).catch(e => console.log(e.cause?.code))"
- Observe
ENOTFOUNDerror. - Run
curlto the same domain via the Bash tool:
``bash``
curl -s -o /dev/null -w "%{http_code}" https://mcp.notion.com/mcp
- Observe successful connection (HTTP 401).
Expected Behavior
allowedDomainsshould work consistently for all processes spawned by the Bash tool, including Node.js and other runtimes that use non-standard DNS resolution.- Alternatively,
excludedCommandsshould fully bypass all sandbox restrictions (including DNS filtering) for the matched command and its entire child process tree.
Actual Behavior
allowedDomainsonly permits DNS resolution for processes using the C library resolver (e.g.,curl).excludedCommandsdoes not prevent DNS blocking for Node.js processes, even when the command name matches.- The only working workaround is setting
dangerouslyDisableSandbox: trueon every Bash invocation that runs a Node.js-based tool.
Suggestions
- Fix
allowedDomainsfor all resolver implementations: Ensure the sandbox DNS filter allows resolution for domains inallowedDomainsregardless of whether the process uses the C library resolver or libuv's resolver (or any other implementation). - Fix
excludedCommandspropagation: When a command matchesexcludedCommands, lift all sandbox restrictions — including DNS — for that entire process tree, not just the top-level command. - Add
excludedProcessesor similar config: Allow specifying process binary names (e.g.,node) that should bypass sandbox DNS restrictions, covering cases where tools are invoked through shims or wrappers (e.g.,mise,nvm). - Document the limitation: At minimum, document that
allowedDomainsmay not work for Node.js-based CLI tools and thatdangerouslyDisableSandbox: trueis currently required as a workaround.
Environment
- OS: macOS (Darwin 25.3.0, arm64)
- Node.js: Managed via mise shim
- Claude Code: Latest version with sandbox network restrictions enabled
Workaround
Use dangerouslyDisableSandbox: true when invoking Node.js-based CLI tools via the Bash tool, or create a PreToolUse hook that automatically injects updatedInput with dangerouslyDisableSandbox: true for known commands.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗