Lesson 2 of 13 · 6 min read
Sandboxing Untrusted Code
In this lesson
TL;DR: A sandbox for untrusted code has two halves. Isolation: a boundary strong enough for hostile code, such as gVisor or a microVM, configured without privileged modes, runtime sockets or host mounts. Environment: nothing worth taking and nowhere to take it: no credentials, only the files the task needs, no network or an allowlisted egress proxy, CPU, memory, disk, process and time limits, and a fresh environment per session, destroyed afterwards. Then treat everything the sandbox returns (files, output, logs) as untrusted input.
Before you read
Pick an answer before you read further. The lesson below explains it, and guessing first, right or wrong, helps the explanation stay with you.
1An analytics agent runs model-written Python inside a Firecracker microVM. The VM mounts the user's whole data bucket read-write using the service's credentials, and has internet access so it can pip-install packages. Which part of this sandbox is doing the work?
Why sandboxes matter more with AI
Code execution is one of the most useful agent capabilities: data analysis, chart generation, running tests, file conversion. The code comes from a model, and the model's choices can be steered by anything in its context. From a security standpoint, an agent's code-execution tool is a remote code execution feature whose input is partly written by strangers. The question is not whether hostile code will run there, but what it can do when it does.
The design
Agent
Writes the code
Code runner
No credentials
Egress proxy
Package mirror only
Also in the picture
Task files
- Copied in, read-only
Results
- Treated as untrusted
Hostile code runs
Assume the code is malicious: an injected instruction made the agent write it. It runs inside a fresh microVM with only the task's files.
Isolation
- Boundary: gVisor or microVMs (Firecracker, Kata) for model-generated or customer code; plain containers only for code you trust. See container isolation.
- Configuration: non-root, read-only root filesystem, no privileged mode, no added capabilities, default or stricter seccomp, no runtime socket, no host network or PID namespace.
Environment
| Concern | Control |
|---|---|
| Credentials | None in the sandbox. If code must call a service, broker the call outside the sandbox with a narrowly scoped, short-lived token |
| Files | Copy in only the task's inputs; mount read-only where possible; write to an ephemeral scratch area |
| Network | Deny by default. If packages are needed, allow only an internal mirror through a proxy; never the open internet, the cloud metadata service or internal ranges |
| Resources | CPU, memory, disk, process count and wall-clock limits, so loops and forks end |
| Lifetime | Fresh sandbox per session or task; destroy afterwards so nothing persists between users |
| Observability | Log executed code, network attempts and resource usage per session |
Outputs
Everything the sandbox returns is untrusted: files may contain malicious content, text output may contain injected instructions for the next model call, and HTML or images must be rendered safely. Cap sizes and types.
Reference designs (checked November 2026)
Two published designs show what the abstract advice looks like in practice.
A hosted code-execution tool. Anthropic documents its code execution tool's sandbox: each container gets 5 GiB of memory, 5 GiB of workspace disk and one CPU; internet access is completely disabled, so only pre-installed libraries can be used; file access is limited to the workspace; containers are scoped to the requesting workspace, checkpointed after about five minutes of inactivity and expire 30 days after creation. Container data is retained for up to 30 days, which is why the tool is outside zero-data-retention arrangements. Every one of those numbers is a design decision with a security consequence: the 30-day lifetime enables multi-session work and also lets one session's tampering affect the next.
A local coding-agent sandbox. Anthropic's write-up on sandboxing Claude Code (October 2025) builds on operating-system primitives, bubblewrap on Linux and Seatbelt on macOS. The agent can read and write in the working directory and modify nothing outside it, and all network traffic goes through a Unix socket to a proxy that runs outside the sandbox, enforces a domain allowlist and asks the user before allowing new domains. The write-up's central claim is that effective sandboxing needs both filesystem and network isolation, and it reports that the sandbox reduced permission prompts by 84% in internal use.
Model writes code
influenced by inputs
MicroVM sandbox
1 CPU, 5 GiB, no network
Results
files and text back
Outside the sandbox, on purpose
Egress proxy
- Allowlist enforced where code cannot reach it
Data broker
- Fetches one API, writes a file in
Return-path checks
- Size caps, sandboxed rendering, labelled as tool output
Isolation
The microVM decides whether code can escape; its resource limits decide how much it can consume.
When the environment, not the boundary, failed
Wiz's 2024 research with Hugging Face is the clearest case of a sandbox that held while its environment did not. A model that executed code when loaded, running in a shared inference pod, could reach the node's instance metadata service, obtain the node's cloud role and read other pods' secrets; separately, build-time code could reach an internal registry that was not properly scoped. Nobody escaped a container. The recommendations were environmental: IMDSv2 with a hop limit, an authenticated registry, and treating models as untrusted code run in a sandbox. Model loading keeps proving the point: a 2025 vLLM vulnerability (CVE-2025-66448) fetched and ran Python from a remote repository through a configuration entry even when the caller had set trust_remote_code=False, so load models inside the sandbox regardless of library flags. OWASP's agentic list names this class unexpected code execution (ASI05).
GPUs and specialised hardware
Some workloads need GPUs inside the sandbox. GPU passthrough to a microVM is possible, but GPU memory and drivers have had isolation weaknesses: the LeftoverLocals vulnerability (CVE-2023-4969, disclosed in January 2024) let one process read another's leftover GPU local memory on several vendors' GPUs. Avoid sharing a GPU between tenants or sessions for untrusted code, clear memory between uses, and prefer dedicated devices or hardware partitioning features with documented isolation guarantees.
Common shortcuts that break sandboxes
- Running code in the application's own container "temporarily".
- Mounting the container runtime socket so code can build images.
- Passing the service's cloud credentials as environment variables.
- Allowing unrestricted outbound internet "for pip".
- Reusing one long-lived sandbox across users.
Worked example: a data-analysis agent
A business-intelligence agent runs model-written pandas code over a user's uploaded CSV.
| Decision | Choice | Reason |
|---|---|---|
| Boundary | A fresh microVM per session | Model-written code is untrusted; containers share the host kernel |
| Resources | 1 vCPU, 5 GiB memory, 5 GiB disk, a wall-clock limit | Bounds runaway or malicious computation |
| Network | None | The data is in the workspace; nothing needs the internet |
| "Get live exchange rates" | A broker tool outside the sandbox fetches one allowlisted API and writes a file into the workspace | Adds one capability without opening egress |
| Reuse | No reuse across sessions; or, if reused, treat the workspace as tainted and never import code from it without review | An injected script in session 1 could leave a modified helper module that session 2 imports |
| Outputs | Files capped (for example 20 MB), HTML rendered only in a sandboxed frame, text returned labelled as tool output | The return path is an exfiltration and injection channel |
Misconceptions interviewers listen for
- "No internet means no exfiltration." Outputs return to the user and the model, and allowlisted mirrors or DNS can carry data.
- "Persistent per-user containers are safe." Persistence enables cross-session tampering and creates a retention obligation.
- "Filesystem isolation is enough for a local agent." Both filesystem and network isolation are needed.
- "trust_remote_code=False means loading a model cannot run code." At least one library path ignored it; load inside the sandbox anyway.
- "gVisor also limits resources." It relies on cgroups for that.
What if · interview follow-ups
Hold your answer as the constraints change
You are designing the sandbox for an AI product feature that runs model-written Python against users' uploaded data. The interviewer adds one product requirement at a time.
Start here
What if the product team wants pip install available at runtime?
Who chooses the package names?
The model does, and models hallucinate package names that attackers then register. Serve installs from an internal mirror behind the egress proxy, with an allowlist and pinned hashes; resolve only pre-approved names and alert on misses, which also catches attempts to use lookups as a covert channel.
Carry it forwardPackage installation is code execution chosen by the model; restrict it to a vetted mirror.
Weak answerOpening PyPI access because the sandbox is isolated.
Constraint changes
What if latency targets push the team to reuse warm sandboxes?
What can one session leave behind for the next?
Modified files, installed packages and background processes. Keep a pool of fresh, never-used sandboxes, or snapshot and restore from a clean image per session; never reuse across users, and if reuse within a user is unavoidable, treat the workspace as tainted and state the retention window.
Carry it forwardSandbox reuse turns isolation in space into exposure over time.
Weak answerReusing containers across sessions for speed without resetting them.
Constraint changes
What if the code needs to query the user's database?
Should credentials ever be inside the sandbox?
No. Put a broker outside the sandbox that queries as the user, read-only and rate-limited, and returns results into the workspace. Results that go back to the model are still untrusted content, because database fields may contain text written by others.
Carry it forwardBring data in through brokers; never bring credentials in.
Weak answerMounting a read-only database credential into the sandbox.
Constraint changes
What if the sandbox has no network but can resolve DNS names?
Who carries a DNS query out?
Your own resolver forwards each query to the authoritative server for the queried domain, so code can encode data into names and send it to an attacker's server. Disable DNS in the sandbox, or allow only names from an allowlist through a resolver you control, and alert on unusual query patterns.
Carry it forwardDNS is egress; count it when you say the sandbox has no network.
Weak answerLeaving the platform default resolver reachable.
Constraint changes
What if the same platform also loads community models for inference?
Does loading count as running untrusted code?
Yes: model files and their configurations have executed code on load, including paths that ignored the library's trust flag. Load them inside the same kind of sandbox, without access to the metadata service, cloud credentials or other tenants' secrets, and prefer formats such as safetensors that contain no code.
Carry it forwardTreat model loading as code execution and isolate it accordingly.
Weak answerLoading models in the shared inference pod because they are 'just weights'.
Now defend it
As the defender, what would you monitor across the sandbox fleet?
Sandbox resource exhaustion and wall-clock kills, egress proxy denials and DNS queries from sandboxes, attempts to reach the metadata address, package-mirror misses, sandbox lifetimes and reuse counts against policy, and output sizes and types returned to users.
How this shows up in interviews
"Design a sandbox for agent-generated code" is a common hard question. Strong answers separate isolation from environment, choose a boundary by trust level, remove credentials and network by default, add limits and ephemerality, and treat outputs as untrusted. They mention GPUs when relevant and resist the shortcuts above. Expect constraint changes: "now it needs internet", "now it needs the user's database".
Say it in 30 seconds: "I assume the code is hostile and make that harmless. Isolation: gVisor or a microVM per session, hardened, no runtime socket or host mounts. Environment: no credentials, only the task's files read-only, no network except an allowlisted package mirror, CPU, memory, disk and time limits, destroyed after use. If it needs a service, I broker the call outside with a scoped token. And whatever comes back is untrusted input."
Check yourself
Say or write your answer first, then reveal. Explaining it from memory, in your own words, is the skill an interviewer is checking.
1The product team wants the code sandbox to query the user's production database directly. How would you allow it with least risk?
2Why destroy the sandbox after each session rather than reusing a warm one for speed?
3Why did Anthropic's local agent sandbox put its network proxy outside the sandbox?
4What did Wiz's 2024 Hugging Face research show about sandboxes?