cloudflare / cloudflare/sandbox-sdk
allowedHosts fails closed for approved hosts and setAllowedHosts does not enforce
- Dominant language
- TypeScript
- Stars
- 1.1k
- Forks
- 114
- Avg merge
- 22h 42m
- Merged PRs (30d)
- 14
Description
**Describe the bug**
On a production Workers Paid deployment using `@cloudflare/sandbox@0.12.4`, outbound host allowlisting does not behave as documented:
1. A class-level `allowedHosts` allowlist combined with `enableInternet = false` blocks both allowed and disallowed HTTPS destinations by timeout.
2. A runtime `setAllowedHosts()` call reports success when internet is enabled, but does not deny a destination outside the allowlist.
This prevents a fail-closed sandbox from reaching only explicitly approved hosts.
**To Reproduce**
Deploy a fresh RPC-transport Sandbox Worker with a `standard-2` container and export `ContainerProxy`:
```ts
import {
ContainerProxy,
Sandbox as CloudflareSandbox,
} from "@cloudflare/sandbox";
export { ContainerProxy };
export class Sandbox extends CloudflareSandbox {
enableInternet = false;
allowedHosts = ["github.com"];
}
```
From a newly created sandbox instance, run:
```sh
curl --max-time 15 -I https://github.com
curl --max-time 15 -I https://example.com
```
Observed: both commands time out. The allowed destination never reaches an outbound handler or the public destination.
A second test on a fresh instance with internet enabled:
```ts
const sandbox = getSandbox(env.Sandbox, freshId);
await sandbox.setAllowedHosts(["github.com"]);
```
Then run the same two curl commands. Observed: both destinations remain reachable, including `example.com`.
I also tested a class-level `outbound` handler and `outboundByHost`, always exporting `ContainerProxy`. With `enableInternet = false`, connection/DNS setup times out before the allowed request reaches the handler. With internet enabled, unlisted destinations remain reachable.
All tests used fresh sandbox IDs to avoid live-instance policy carryover.
**Expected behavior**
As documented:
- `enableInternet = false` should deny public internet by default.
- A host in `allowedHosts` should override that default and be reachable.
- A host outside the allowlist should be denied.
- `setAllowedHosts(["github.com"])` should produce the same deny-by-default behavior for the live instance.
Expected test result: `github.com` succeeds and `example.com` is denied.
**Environment**
- `@cloudflare/sandbox`: 0.12.4
- Container image: `cloudflare/sandbox:0.12.4` (matching release image)
- Wrangler: 4.114.0
- Transport: RPC
- Container instance type: `standard-2`
- Deployment: production `workers.dev` Worker on Workers Paid
- Tests performed July 24, 2026
**Additional context**
The outbound-traffic documentation states that `allowedHosts` overrides `enableInternet = false` and that exporting `ContainerProxy` enables interception. We need this boundary for an engineering-agent sandbox and cannot safely fall back to unrestricted internet access.
This differs from #791: that issue concerns customizing the response for a host that is successfully blocked. Here, an explicitly allowed host cannot egress in the fail-closed configuration, while runtime allowlisting does not block unlisted hosts in the internet-enabled configuration.
Contributor guide
Research direction
Trace class-level allowedHosts and enableInternet handling through ContainerProxy, outbound, and outboundByHost, then compare it with setAllowedHosts on a fresh instance. Reproduce the two curl checks: github.com should succeed while example.com is denied, both for the class-level fail-closed setup and the runtime allowlist.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- backend, networking, security
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100