Heidelberg AICurriculum
Track 12 · Advanced
12.9

Running untrusted AI code safely

Your agent writes code. Do not run it here.

7 lessons 2026-08-13 AI-generated

1Overview

An isolated, disposable environment where code of unknown provenance can execute without reaching anything that matters.

The Docker chapters in this track teach containers as packaging — a way to ship a service. This chapter teaches the same technology as a boundary, pointed the other way: keeping code your agent just wrote away from your laptop, your credentials and your production host. → Firecracker microVMs, what a container does and does not isolate, and the small set of rules that make an execute-this-code loop survivable. → It also carries a live lesson in dependency risk: one of the best-known sandboxes in this space moved its core development into a private codebase mid-2026.

An agent that writes code eventually wants to run it. This is where — a microVM with no credentials, a narrow filesystem, no network unless asked, and a hard time limit. Containers as a security boundary rather than a packaging format, and the difference between the two.

1.2After this chapter you can
Say precisely what a container isolates and what it does not
Run agent-generated code in a microVM instead of on your host
Give a sandbox the narrowest filesystem and network it can still work in
Return results from a sandbox without handing it your credentials
Set a wall-clock and resource ceiling so a loop cannot run all night
Judge whether an open-source sandbox is still open before you depend on it
1.3When to reach for it

Any loop where a model writes code and something runs it: data analysis agents, self-testing agents, anything with an execute tool.

1.4Key parts

A microVM or hardened container, a scrubbed environment, an explicit network policy, a resource and time ceiling, and a result channel that carries data rather than access.

1.5Free vs paid

The sandbox runtime is open source and self-hostable; the hosted version is metered per second of execution. Self-hosting trades money for an operational burden.

1.6Watch out

A container shares the host kernel. That is fine for packaging and thin for adversarial code — which is what "code my agent wrote from a web page it read" is.

2Lessons 7

2.1 Explain the isolation differences between containers and microVMs

A microVM runs its own kernel instance, while a container shares the host kernel, giving microVMs a stronger security boundary for untrusted code.

Show how kernel isolation differs between containers and microVMs

  1. Launch a docker run container that executes uname -r
  2. Observe the kernel version printed inside the container
  3. Start a firecracker microVM that runs uname -r
  4. Observe the independent kernel version printed by the microVM
  • You'll see The Docker container displays the host's kernel version, whereas each Firecracker microVM shows its own separate kernel version
  • Takeaway Containers share the host kernel and cannot protect against kernel exploits; microVMs provide a true isolation boundary with their own kernels
  • Check How does the difference in displayed kernel versions illustrate the security advantage of microVMs over containers when running untrusted code?
  • Cost Free to reason about — this lesson is the vocabulary the rest of the chapter builds on.

2.2 Run agent‑generated code inside a microVM

The Sandbox.create() method starts a Firecracker microVM that can run commands via sandbox.commands.run(), returning structured results.

Execute agent‑generated code inside an isolated Firecracker microVM using the E2B SDK

  1. Open the e2b.dev site and register for an account
  2. Generate an API key, export it as E2B_API_KEY in your shell
  3. Install the Python SDK with pip install e2b
  4. Write a script that calls Sandbox.create() then runs sandbox.commands.run('python3 -c "print(2+2)"')
  5. Run the script and view the result object containing stdout, stderr and exit code
  6. Terminate the microVM with sandbox.kill()
  • You'll see The commands.run() call returns a JSON‑like result showing stdout = 4, empty stderr and a zero exit code
  • Takeaway Each execution runs in its own microVM so no state or credentials persist between runs
  • Check In what way does creating a sandbox with Sandbox.create() and invoking commands.run() keep agent‑generated code from affecting the host system?
  • Cost Self-hosted: your own compute, Terraform-managed. Hosted: metered per second of sandbox runtime, billed to the E2B account behind your API key.

2.3 Configure a sandbox with deny‑by‑default network and scoped filesystem

A deny‑by‑default network blocks all outbound traffic unless hosts are explicitly added to the network.allow_out list.

Configure an E2B sandbox so that only whitelisted hosts can be contacted and no other files exist inside it

  1. Call Sandbox.create without extra mounts to get a fresh environment
  2. Pass allowInternetAccess=False to Sandbox.create to disable all outbound traffic by default
  3. Add required destinations to the network.allow_out allow‑list in the sandbox configuration
  4. Attempt a request to an unlisted address and verify it fails immediately
  • You'll see The outbound request to the non‑whitelisted host is rejected instantly, while a request to a whitelisted host succeeds
  • Takeaway Start with a blanket deny and then add narrow allowances because allow rules override the default block
  • Check What happens to network connections when you disable internet access globally and then explicitly whitelist hosts in an E2B sandbox?
  • Cost No extra infrastructure — this is a parameter on the same Sandbox.create() call, free either way.

2.4 Run untrusted code and retrieve its output safely

Keeping credentials out of the sandbox and only returning plain JSON output ensures privileged actions stay in trusted code.

Separate secret handling from sandboxed execution so that only safe results are returned

  1. Create a sandbox with Sandbox.create using an empty envs dictionary
  2. Run the agent‑written script inside the sandbox via sandbox.commands.run and capture its stdout/stderr
  3. Read any output files produced by the sandbox with sandbox.read_file after execution
  4. Pass the captured JSON result to trusted code that holds credentials and performs privileged operations
  • You'll see The sandbox returns a plain JSON payload while its environment contains no secret values
  • Takeaway Never expose real credentials inside the sandbox; let trusted code handle any privileged work
  • Check Why must credentials stay outside the sandbox and only JSON results be passed back after running agent‑generated code?
  • Cost No added cost — this is how you call the same SDK, not a different one.

2.5 Configure a sandbox timeout that stops runaway loops

The timeoutMs parameter sets a hard execution limit, aborting the microVM when the time expires.

Enforce a maximum runtime so runaway loops are terminated automatically

  1. Create a sandbox with Sandbox.create({ timeoutMs: 60000 }) to set a one‑minute limit
  2. Optionally call sandbox.setTimeout(120000) if a longer run is needed for a specific task
  3. Run a script that contains a loop exceeding the configured limit
  4. Observe the sandbox aborting the process when the timeout is reached
  • You'll see The sandbox stops mid‑loop as soon as its timeout expires, and subsequent commands fail immediately
  • Takeaway Match the sandbox timeout to the actual work required rather than relying on an unlimited ceiling
  • Check How does a sandbox’s timeout protect against runaway code, and what observable behaviour occurs when the limit is hit?
  • Cost None beyond the sandbox time you would have used anyway — a tight timeout only ever removes wasted runtime, never useful runtime.

2.6 Verify a sandbox project's maintenance status yourself

The maintenance status paragraph in a repository’s README indicates whether the project is actively maintained or deprecated.

Verify that a sandbox‑related library you depend on is still actively maintained

  1. Navigate to the project's page on GitHub in your browser
  2. Click the README tab to view its contents
  3. Scroll until you find the maintenance status paragraph
  4. Record any statements about ongoing development, deprecation or migration to a private codebase
  • You'll see The README displays a clear statement about the project’s current maintenance status
  • Takeaway Always check the project's own README for maintenance information before trusting it in your sandbox workflow
  • Check Which part of a GitHub repository should you examine to determine if its sandbox tool is still actively maintained?
  • Cost A few minutes to read a README before you build on it — far cheaper than discovering a security fix will never land.

2.7 Run agent‑written code safely

MicroVM isolation, credential‑free environments, deny‑by‑default networking, resource caps and verified open‑source tools together form a defence‑in‑depth sandbox for untrusted scripts.

Run an agent‑generated script only after it passes every safety gate you control

  1. Select Firecracker microVM as the execution target to guarantee kernel isolation
  2. Inspect the sandbox’s envs list and ensure no real credentials are present, moving them to trusted code outside the sandbox
  3. Set the default network policy to deny all outbound traffic using the network rules control, then add explicit allow entries for required hosts only
  4. Configure a matching execution timeout, and limit CPU and memory with the respective resource caps controls
  5. Re‑open the sandbox tool’s README to confirm it remains open‑source before each deployment
  • You'll see The script is accepted only when it meets all five checks: microVM isolation, credential‑free envs, deny‑all network with allow‑list, tight resource limits and a verified open‑source sandbox
  • Takeaway Combining multiple independent safeguards blocks different attack vectors, making the overall execution safe
  • Check How do the combined checks of microVM isolation, credential‑free environments, deny‑by‑default networking, resource caps and README verification together ensure safe execution of agent‑written scripts?
  • Cost A few minutes of deliberate configuration per sandbox, reused across every task that shares the same template.

3You’ll know it worked 11 checkable outcomes in this chapter

  • The code runs inside the Fly machine and cannot access files or environment variables on your local laptop
  • The container exits with a clean exit code and no changes appear on the host filesystem or network interfaces
  • The function returns `(-1, '', 'Timeout')` for infinite loops and proper output for safe scripts
  • All new tests pass before the commit is merged, and CI reports zero failures
  • The agent refuses to generate code that violates rules defined in the nearest context file
  • Attempts by the code to perform privileged syscalls are blocked with permission errors inside the sandbox
  • Check the Worker logs for successful execution and confirm no external credentials were accessed
  • The payload can read /etc/passwd inside the VM but cannot see the host's password file or affect host processes

11 outcomes in all — one per recipe below.

4FAQ, Tips & How-to 11

one problem, one solution, one action
How-to Everyone

Run untrusted user code safely

Fly.io machines let you launch an OCI‑compatible image inside a lightweight virtual machine (Firecracker microVM). By packaging the execution environment in a Docker image and deploying it as a Fly machine, you isolate user‑provided code from your host, credentials, and production services.

Fly․io ↗ Lesson → AI-generated
How-to Everyone

I need to run random Python snippets safely

A Docker container can act as a digital prison that runs arbitrary Python code while preventing access to the host filesystem, network, and excessive resources. By pulling a minimal python:3.11-slim image and launching it with restrictive flags, any malicious behavior is confined and the container self‑destructs after execution.

codingdidi ↗ Lesson → AI-generated
How-to Everyone

Running untrusted code with automatic timeout

Python's `subprocess.run` can launch the Docker command from a script, automatically applying a timeout and capturing stdout/stderr. This lets you programmatically evaluate untrusted code and return a uniform result tuple (exit_code, stdout, stderr) without manual CLI interaction.

codingdidi ↗ Lesson → AI-generated
How-to Everyone

Need to run untrusted Python code

Specific Docker run options (`--network none`, `-m <mem>`, `--cpus <cpu>`) provide a lightweight security boundary that stops most attacks: no outbound connections, limited RAM to prevent OOM, and CPU caps to kill infinite loops quickly.

codingdidi ↗ Lesson → AI-generated
How-to Everyone

Want to keep AI‑written changes safe

Breaking each AI‑written change into tiny, test‑driven commits limits the blast radius of bugs or malicious behavior and makes reviews trivial. With a single focused test per commit you can instantly verify intent and catch regressions.

Google Cloud Tech ↗ Lesson → AI-generated
How-to Everyone

Need to keep AI agents from using unsafe patterns

Placing a lightweight Gemini‑style context file in each project directory gives the agent a scoped style guide and security policy, preventing it from using unsafe patterns or accessing irrelevant resources.

Google Cloud Tech ↗ Lesson → AI-generated
How-to Everyone

Need to run AI‑generated scripts without risking the host

Unlike plain Docker containers, gVisor intercepts most system calls and runs them in user space, providing a stronger barrier that prevents the agent from escaping to the host kernel.

Google Cloud Tech ↗ Lesson → AI-generated
How-to Everyone

Code might hide bugs or logic attacks

Static analysis (SAST/SCA) catches known patterns of vulnerability, while an adversarial AI agent can simulate attacks on business logic that static tools miss, giving a more complete security picture.

Google Cloud Tech ↗ Lesson → AI-generated
How-to Everyone

Need to test AI‑generated script safely

Cloudflare Workers (now called 'mode worker') provide a secure, browser‑enabled sandbox that isolates execution from your local machine and credentials. By deploying the agent’s code to this environment you get OS‑level separation, limited network access, and built‑in monitoring, making it safe to run code generated by the AI.

Adebayo Ajibade ↗ Lesson → AI-generated
How-to Everyone

Running untrusted user code

A Firecracker microVM runs a minimal Linux kernel in its own virtualized environment, providing hardware‑enforced isolation separate from the host OS. This prevents malicious payloads from accessing host files, credentials, or network resources while still booting in milliseconds.

Kishore Newton ↗ Lesson → AI-generated
How-to Everyone

Need a fresh sandbox for each job

Automating the creation, execution, and destruction of Firecracker microVMs ensures each user request gets a fresh isolated environment, eliminating state leakage and simplifying resource cleanup.

Kishore Newton ↗ Lesson → AI-generated

The same set on /recipes, filtered by tool and role.

5See also

💬 Discuss this chapter

Ask, share, or report — over on the Heidelberg AI community forum.