sandbox.network.allowedDomains does not work for Node.js processes (DNS resolution blocked)

Resolved 💬 3 comments Opened Mar 23, 2026 by shrek-kurata Closed Apr 21, 2026

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

  1. Add a domain to sandbox.network.allowedDomains in project or user settings.
  2. 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))"
``

  1. Observe ENOTFOUND error.
  2. Run curl to the same domain via the Bash tool:

``bash
curl -s -o /dev/null -w "%{http_code}" https://mcp.notion.com/mcp
``

  1. Observe successful connection (HTTP 401).

Expected Behavior

  • allowedDomains should work consistently for all processes spawned by the Bash tool, including Node.js and other runtimes that use non-standard DNS resolution.
  • Alternatively, excludedCommands should fully bypass all sandbox restrictions (including DNS filtering) for the matched command and its entire child process tree.

Actual Behavior

  • allowedDomains only permits DNS resolution for processes using the C library resolver (e.g., curl).
  • excludedCommands does not prevent DNS blocking for Node.js processes, even when the command name matches.
  • The only working workaround is setting dangerouslyDisableSandbox: true on every Bash invocation that runs a Node.js-based tool.

Suggestions

  1. Fix allowedDomains for all resolver implementations: Ensure the sandbox DNS filter allows resolution for domains in allowedDomains regardless of whether the process uses the C library resolver or libuv's resolver (or any other implementation).
  2. Fix excludedCommands propagation: When a command matches excludedCommands, lift all sandbox restrictions — including DNS — for that entire process tree, not just the top-level command.
  3. Add excludedProcesses or 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).
  4. Document the limitation: At minimum, document that allowedDomains may not work for Node.js-based CLI tools and that dangerouslyDisableSandbox: true is 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.

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗