Before adding complexity, establish the foundation.
A sandbox is an isolated execution environment for running untrusted code. The goal is to contain the code's consequences — not just the code itself.
| Question | Answered By |
|---|---|
| What can this code access on this machine? | Compute isolation (microVM, container) |
| What can this code access through the network? | Network isolation (egress control) |
Key insight: Most people think of sandboxing as only the first question. This is the central mistake the article addresses.
Untrusted code inside microVM
│
│ (VM boundary intact, no escape needed)
│
▼
Outbound network connection ──────► External attacker's server
│
├── Exfiltrate private files
├── Send environment variables / credentials
├── Scan internal network services
└── Call authenticated APIs with stolen tokens
An AI agent reads a code repository. A prompt injection hidden in a comment instructs it to upload all files to
evil.com. The microVM boundary is never crossed. The code simply opens a network connection and sends the data.
The VM worked perfectly. Containment still failed.
These are real attack vectors — not theoretical. Learn to identify each one.
A network bypass is a sandbox escape — even if the VM boundary never breaks.
Complete network disconnection is safe but often impractical. A real workload may need to:
The solution is selective connectivity — grant only what the workload actually needs.
FULLY OPEN ◄────────────────────────────► FULLY ISOLATED
│ │
│ USEFUL SANDBOX LIVES HERE │
│ │
Dangerous Often impractical
| Need | Policy |
|---|---|
| Use one AI provider | Allow ai-provider.com, deny everything else |
| Write to storage | Allow one specific S3 bucket, not entire AWS |
| Access internal service | Allow one private IP, block rest of private range |
| Install packages safely | Allow registry during setup, remove access before untrusted code runs |
Domain rules — for modern cloud services
allow: "api.openai.com"CIDR rules — for infrastructure
10.0.0.0/8, 192.168.0.0/16)Use both together. They solve different problems.
Network permissions should change throughout a workload's lifecycle, not remain static.
Phase 1: SETUP
├── Allow: package registry (npm, pip)
├── Allow: git repository
└── Allow: AI model API
▼ [Untrusted code is about to run]
Phase 2: EXECUTION ← tighten here
├── Deny: package registry
├── Deny: git repository
└── Allow: AI model API only
▼ [Code finished, need to save results]
Phase 3: OUTPUT
├── Allow: one specific storage bucket
└── Deny: everything else
▼ [Done]
Phase 4: COMPLETE
└── Deny: all outbound traffic
This is achievable without restarting the workload — policies update at runtime.
This is an architectural requirement, not just a preference.
[microVM]
└── Firewall running inside VM
└── Untrusted code can modify or disable it
[Host machine]
└── Firewall running on host ← untrusted code cannot reach this
└── [microVM]
└── Untrusted code
The firewall must run outside the sandbox it governs.
SNI explained simply: When your browser starts a TLS connection, it announces the hostname it wants in plaintext before encryption begins. This is so servers hosting multiple domains know which certificate to use. The firewall reads only this announcement — it does not decrypt your traffic.
This solves a specific and important problem.
Environment variable: API_KEY=sk-abc123
│
├── Legitimate code reads it ✓
├── Malicious code reads it ✓
└── Malicious code sends it to attacker's server ✓
└── Attacker now has your API key forever
even after sandbox is destroyed
[Host Firewall]
├── Holds the real credential (never enters VM)
├── Creates a unique certificate authority for this sandbox
└── When sandbox connects to allowed destination:
1. Terminates TLS from sandbox
2. Reads the request
3. Injects Authorization header with real credential
4. Opens new TLS connection to real destination
5── Credential traveled only on the host, never inside VM
| Property | Result |
|---|---|
| Credential never in VM | Cannot be read by any code inside sandbox |
| Credential never leaves host unencrypted | Cannot be intercepted |
| CA is unique per sandbox | Disposed when sandbox stops |
| Credential only sent to configured destination | Uploading files elsewhere doesn't transfer the credential |
| Can restrict by path/method | Code can POST results but not GET other resources |
Static allowlists cannot cover every requirement. Sometimes you need custom logic in the security path.
Selected HTTPS requests are forwarded to your own proxy before reaching their destination.
Your proxy receives:
Incoming request from sandbox
│
▼
[Your Proxy — outside sandbox]
│
├── Redact sensitive fields from request body
├── Check per-user authorization rules
├── Scan packages for supply chain attacks
├── Log request for compliance audit trail
├── Exchange sandbox identity for scoped credential
└── Reject operations that violate business rules
│
▼
Destination service (if approved)
Key point: Your policy logic and secrets stay outside the sandbox, even when the code inside has full root access within its microVM.
Now the implementation makes sense:
import { Sandbox } from '@vercel/sandbox';
const sandbox = await Sandbox.create({
networkPolicy: {
allow: {
// Only this domain is permitted
"ai-gateway.vercel.sh": [{
transform: [{
headers: {
// Credential injected at network boundary
// Never stored inside the sandbox
"Authorization": `Bearer ${process.env.AI_GATEWAY_TOKEN}`
}
}]
}]
}
// All other destinations: denied by default
}
});
What this policy enforces:
ai-gateway.vercel.shUpdating policy at runtime (dynamic lifecycle):
// When untrusted phase begins, lock it down completely
await sandbox.update({ networkPolicy: 'deny-all' });
Bring everything together into one coherent picture:
COMPLETE SANDBOX = Compute Isolation + Network Isolation
Compute Isolation answers:
"What can code access on this machine?"
Network Isolation answers:
"What can code access through the network?"
"What credentials can it use?"
"Which requests need additional policy?"
"When does all communication stop?"
A sandbox is not defined only by where its code runs. It is defined by what that code can reach, what authority it receives, and which boundaries hold when the code itself is hostile.
| Concept | One-Line Summary |
|---|---|
| Compute isolation | Prevents access to the host machine |
| Network isolation | Prevents access through the network |
| Egress control | Allowlist outbound destinations; deny by default |
| Domain policies | Control connections by hostname |
| CIDR policies | Control connections by IP range |
| Dynamic policies | Change permissions during workload lifecycle |
| Firewall placement | Must be on host, outside the VM |
| SNI inspection | Read hostname from unencrypted TLS handshake |
| Credential injection | Add auth headers at network boundary, not inside VM |
| Request forwarding | Route selected requests through your own policy proxy |
| Network bypass = escape | A bypass achieves the same harm as a VM escape |