← all articles

Sandboxing the code your AI assistant writes: what actually stops it

The moment that changes how you think about this

The first time an agent I was running suggested pip install for a package I’d never heard of, then imported it and ran a script that reached out to a URL I didn’t recognize, I stopped and actually read what it wrote instead of hitting approve out of habit. Nothing malicious happened that time. But it’s the kind of near-miss that makes you realize “let the model run code” and “let the model run code safely” are two very different engineering problems, and most people building on top of coding assistants are quietly skipping the second one.

This isn’t a hypothetical risk model. Every mainstream coding assistant now has a code execution path, whether that’s Claude Code running shell commands, Cursor’s agent mode calling terminal tools, GitHub Copilot’s workspace features, or the code interpreter built into ChatGPT. The model doesn’t know your infrastructure and doesn’t have a sense of consequence. It writes code the way it writes prose: fluently, and without inherent limits. If it hallucinates a destructive command or gets prompt-injected by something it read off the web, whatever it’s running on takes the hit.

What a sandbox actually needs to stop

It helps to be specific about the failure modes instead of waving at “security” in general. There are really three categories:

Accidental damage. The model writes rm -rf against the wrong path, or a script that fills disk, or an infinite loop that pins a CPU core. No malice involved, just a bug the model didn’t catch, same as a bug a junior engineer might write.

Injected instructions. The model is asked to summarize a webpage, a PDF, or a GitHub issue, and that content contains text aimed at the model rather than at you, telling it to exfiltrate environment variables or install a backdoor. This is now a well documented attack surface for anything that lets an LLM read untrusted content and then act on tools.

Supply chain risk in the generated code itself. The model imports a package, and that package (or a transitive dependency of it) does something it shouldn’t. This is the same risk as any developer running npm install, just at higher volume because agents install things fast and without a human pausing to check the registry.

A sandbox worth the name has to contain all three, not just the first one. A lot of “sandboxed” setups I’ve seen only really solve for accidental damage.

The actual mechanisms, and what each one buys you

Plain subprocess with resource limits. The cheapest option: run the generated code as a subprocess with a timeout, a memory cap via ulimit or cgroups, and maybe a restricted user account. This stops runaway loops and disk fills. It does nothing against network exfiltration or a package that reads your SSH keys, because the process still shares the host’s filesystem and network namespace unless you explicitly strip that away.

Linux namespaces and cgroups (what Docker is built from). A container gives you real filesystem isolation, a separate process tree, and network namespace control, so you can flat out disable network access or force it through an allowlisted proxy. This is the baseline most people mean when they say “we sandbox it.” The catch is that containers share the host kernel. A kernel exploit or a container escape bug, and the isolation is gone. That’s not a theoretical concern, it’s why gVisor and Firecracker exist at all.

gVisor. Google’s user-space kernel intercepts syscalls from the container and re-implements them rather than passing them straight to the host kernel. It shrinks the attack surface a lot, at the cost of syscall-heavy workloads running slower because every syscall now goes through an extra layer of interception. For code interpreter workloads, that overhead is usually acceptable since the workloads are typically short-lived scripts, not long-running syscall-bound services.

Firecracker and other microVMs. This is what AWS Lambda and several of the AI code-execution vendors run on. Each execution gets its own tiny virtual machine with its own kernel, so a full container escape doesn’t get you onto the host, it gets you onto a throwaway VM. This is a meaningfully stronger boundary than namespaces alone, and it’s the approach services like E2B and Modal’s sandboxes are built around. The tradeoff is operational complexity: you’re now managing VM images, not just container images, and cold starts matter more if you’re spinning one up per request.

WebAssembly sandboxes. Running generated code inside a WASM runtime like Wasmtime gives you very tight, capability-based isolation, the code literally can’t touch the filesystem or network unless you explicitly grant that capability. It’s a good fit if the generated code is genuinely just computation, but it’s a bad fit the moment the assistant needs to install arbitrary Python packages with native extensions, which is most real coding-assistant workloads.

None of these are “the answer.” They’re different points on a spectrum of isolation strength versus operational cost, and the right one depends on what the assistant is actually doing.

How this shows up in the tools you’re already using

Worth being honest about what’s actually happening under the hood of the assistants people use daily, based on how they’re documented to behave rather than any inside knowledge of their infrastructure.

Claude Code and Cursor’s agent mode, by default, run shell commands on your own machine or your own CI environment. That’s not a criticism, it’s the whole point, since a lot of the value is editing your actual repo and running your actual tests. But it means the sandbox is whatever you set up yourself: a devcontainer, a disposable VM, a CI runner with scoped credentials. The tool gives you an approval prompt before commands run, which is a human-in-the-loop control, not a sandbox. Approving a command doesn’t make it safe, it just means a person glanced at it.

Hosted code-execution features, like ChatGPT’s code interpreter, run in an isolated container the vendor controls, typically with no outbound network access at all, which closes off the exfiltration path by removing the exit door entirely. That’s a genuinely strong default for the “analyze this CSV” use case, but it also means you can’t install arbitrary packages from the internet, so it’s not a fit for agent workflows that need to hit real APIs.

Agent platforms built for autonomous coding, Replit’s Agent and similar tools, run generated code inside their own managed containers rather than your machine, which protects you but shifts the trust boundary onto the vendor’s isolation. You’re trading “I control the sandbox” for “I trust their sandbox,” which is a fine trade if you’ve actually looked at what they claim it does, and a bad one if you’re just assuming it’s handled.

What I actually run

For agent workloads that touch real credentials or write to production-adjacent systems, I don’t let the assistant run anything directly on my machine. Generated code goes into a container that’s rebuilt from a pinned base image, mounted read-only against everything except a scratch directory, and network access goes through an explicit allowlist of the two or three domains the task actually needs, not an open connection to the internet. For anything that’s just “help me write and test a script,” a devcontainer with no credentials mounted and a short-lived token scoped to a single repo is enough, since the downside of a mistake there is annoying, not expensive.

The one habit that’s paid off more than any specific tool choice: never let the sandbox and the credential scope be the same decision. A tightly isolated container that still has your full AWS admin key baked into an environment variable isn’t a sandbox, it’s a leak with extra steps.

The tradeoff people size up wrong

Most people think about sandboxing as a filesystem problem, keep the model from writing where it shouldn’t. The bigger and more commonly missed hole is network egress. A model that can read your filesystem but can’t reach the internet is mostly contained. A model that can reach the internet but can’t touch your filesystem can still exfiltrate anything you handed it in the prompt or the environment. If you’re only locking down one of the two, lock down the network first.

If you’re building or choosing a workflow around this, the questions worth asking a vendor or your own setup are concrete: does generated code get its own kernel or does it share yours, is network access default-open or default-closed, and what credentials does the sandbox actually have access to versus what it needs. Anything less specific than that is marketing, not an answer.

For more breakdowns like this on the tools and infrastructure behind AI coding assistants, head back to the AI Tool Gazette homepage.

for builders
Running agents or scrapers at scale?

AI pipelines that crawl, research, or automate the web hit rate limits and geo-blocks fast. Singapore Mobile Proxy runs real 4G/5G mobile IPs that carriers still trust.

see plans →
read on
More from the Gazette

Tool reviews, model and pricing news, and build guides for people shipping real things with AI.

browse all articles →