# Running agent code is becoming a sandboxing problem

AI coding tools spent their first years producing text.

They suggested a line, then a function, then a file. A developer read the suggestion and decided whether it ran. Whatever the model wrote, a person stood between it and the machine.

That person is leaving the loop.

Claude Code, Codex, Cursor, Gemini CLI and GitHub Copilot now run shell commands themselves. They install packages, run tests, rewrite config and push branches, some of them unattended in CI or in a vendor's cloud. Anthropic's engineering team names what happens when a human approves every step: ["approval fatigue"](https://www.anthropic.com/engineering/claude-code-sandboxing).

The inputs did not get safer. An agent reads issues, READMEs, web pages, install scripts and MCP tool output, and any of it can carry instructions the model cannot reliably tell apart from yours. [AI agents need boring engineering](https://blog.prashantkoirala.info.np/ai-agents-need-boring-engineering) covers those basics. This post starts where that one stops.

The model will be tricked sometimes.

That is not a bug to file. It is a design input.

What matters is what the process can touch when it happens: which files, which hosts, which credentials, for how long. That makes running agent code a sandboxing problem, and most of the sandboxes shipping today are off by default or leave the network and credentials partly open.

## The incident record is about reach, not jailbreaks

Since late 2024, and faster since mid-2025, coding agents have produced a steady record of public incidents. Read together, they share a shape.

None depended on a classic jailbreak. In most, attacker text arrived through an ordinary channel, the agent acted on it, and the outcome was decided by what the agent could already reach. In the MCPoison and Codex config rows no model decided anything: the product ran commands a repository declared.

### Ten incidents, one pattern

| Incident | Date | Input vector | What the agent could reach | Fix |
| --- | --- | --- | --- | --- |
| [GitHub MCP "toxic agent flow"](https://invariantlabs.ai/blog/mcp-github-vulnerability) (Invariant Labs) | May 2025 | Malicious issue in a public repo | Private repos via the same token; a public PR as the exit | No code fix; one repo per session, least privilege |
| [Gemini CLI allowlist bypass](https://tracebit.com/blog/code-exec-deception-gemini-ai-cli-hijack) (Tracebit) | July 2025 | Instructions hidden in a [README.md](http://README.md) | Shell via an allowed `grep` prefix, plus network | v0.1.14 |
| [Amazon Q Developer for VS Code](https://aws.amazon.com/security/security-bulletins/AWS-2025-015/), CVE-2025-8217 | July 2025 | Code merged via an over-scoped GitHub token in CodeBuild config | A prompt to wipe local files and cloud resources (press reports); never ran, syntax error | 1.85.0 |
| Cursor CurXecute, CVE-2025-54135 ([GHSA-4cxx-hrm3-49rm](https://github.com/cursor/cursor/security/advisories/GHSA-4cxx-hrm3-49rm)) | August 2025 | Injection in content read through MCP | Writes to `mcp.json`, which launches commands | Cursor 1.3.9 |
| [Cursor MCPoison](https://research.checkpoint.com/2025/cursor-vulnerability-mcpoison/), CVE-2025-54136 | August 2025 | MCP config swapped after approval | Commands on reopen, no re-approval | Cursor 1.3 |
| [Copilot in VS Code](https://embracethered.com/blog/posts/2025/github-copilot-remote-code-execution-via-prompt-injection/), CVE-2025-53773 | August 2025 | Injection in project content | `.vscode/settings.json`, then the shell | August 2025 Patch Tuesday |
| [Nx "s1ngularity"](https://github.com/nrwl/nx/security/advisories/GHSA-cxm3-wv7p-598c) | August 2025 | Malicious postinstall in compromised npm releases | Local AI CLIs, files, the victim's GitHub account | Versions removed by npm 26 to 27 August |
| [Codex CLI project config](https://research.checkpoint.com/2025/openai-codex-cli-command-injection-vulnerability/), CVE-2025-61260 | Fixed Aug 2025, published Dec 2025 | Repo `.env` sets `CODEX_HOME`; project config declares MCP commands | Commands at startup, no approval | 0.23.0 |
| [PromptPwnd](https://www.aikido.dev/blog/promptpwnd-github-actions-ai-agents) (Aikido Security) | December 2025 | Issue and PR text fed to CI agents | Privileged CI tokens | Per project |
| [Clinejection](https://adnanthekhan.com/posts/clinejection/) (Adnan Khan) | February 2026 | Issue title in a triage prompt | Bash in CI, the Actions cache, release tokens | Workflow fixed after disclosure |

The vectors are mundane: an issue, a README, a config file, an install script. So are the exits: a settings file, a shell, a token, an outbound request.

### Every exit was a capability the agent already held

Read the reach column.

[Johann Rehberger's Copilot finding](https://embracethered.com/blog/posts/2025/github-copilot-remote-code-execution-via-prompt-injection/) is the clearest. Injected instructions got Copilot to write `"chat.tools.autoApprove": true` into `.vscode/settings.json`, turning off confirmations, then run shell commands. "The edits are immediately persistent, they are not in-memory as a diff to review." The agent could edit the file that governed its own permissions.

Cursor's August 2025 bugs follow the same line. [MCPoison](https://research.checkpoint.com/2025/cursor-vulnerability-mcpoison/) showed that Cursor approved an MCP server by key name, so a committed `.cursor/rules/mcp.json` could swap the command later. CurXecute, titled "Arbitrary code execution from Cursor Agent through a prompt injection via MCP Special Files", let injected content add an `mcp.json` entry, which [according to Tenable's summary](https://www.tenable.com/blog/faq-cve-2025-54135-cve-2025-54136-vulnerabilities-in-cursor-curxecute-mcpoison) of the Aim Labs research ran without approval.

[Check Point's Codex CLI finding](https://research.checkpoint.com/2025/openai-codex-cli-command-injection-vulnerability/) needed no injection at all. A repository's `.env` set `CODEX_HOME=./.codex`, and the repository's own `config.toml` declared MCP commands that ran at startup with "no interactive approval".

[Tracebit's Gemini CLI finding](https://tracebit.com/blog/code-exec-deception-gemini-ai-cli-hijack) shows why command allowlists are weak. Allowing `grep` allowed anything that began with `grep`, because "Only very cursory validation logic is performed when comparing a shell input to the command whitelist". Whitespace padding hid the rest from the UI, and the CLI ran in its default no-sandbox mode.

[Invariant Labs' GitHub MCP demo](https://invariantlabs.ai/blog/mcp-github-vulnerability) needed no bug. A malicious public issue steered an agent holding a token for private repositories into leaking them through a public pull request. Invariant says "this is not a flaw in the GitHub MCP server code itself" and "even state-of-the-art aligned models are vulnerable". The demo used Claude 4 Opus in Claude Desktop, and Invariant notes that many users switch tool confirmation to "Always Allow". The flaw was one token spanning two trust levels in one session.

### Two rows need a qualifier

Amazon Q was a compromise of the agent product, not an injection into a running agent. Per [AWS-2025-015](https://aws.amazon.com/security/security-bulletins/AWS-2025-015/), the code "was unsuccessful in executing due to a syntax error", which "prevented the malicious code from making changes to any services or customer environments". [The Register reported](https://www.theregister.com/2025/07/24/amazon_q_ai_prompt/) that it was a prompt telling Q to wipe local files and cloud resources. Nothing was wiped.

The [Nx advisory](https://github.com/nrwl/nx/security/advisories/GHSA-cxm3-wv7p-598c) documents a workflow injection that led to npm token theft and malicious releases on 26 August 2025. The postinstall `telemetry.js` told a local agent to act as a "file-search agent" and "write it to /tmp/inventory.txt", and stolen data went to `s1ngularity-repository` repos in victims' own GitHub accounts. The advisory does not name the agents. [Socket](https://socket.dev/blog/nx-packages-compromised) reported that the script looked for the Claude, Gemini and Amazon Q CLIs, and [Snyk](https://snyk.io/blog/weaponizing-ai-coding-agents-for-malware-in-the-nx-malicious-package) lists the flags it passed to switch off their prompts: `--dangerously-skip-permissions`, `--yolo` and `--trust-all-tools`.

Malware no longer needs its own file scanner when the victim has an agent and a flag that removes its prompts. That is the shift described in [AI changed the software supply chain](https://blog.prashantkoirala.info.np/ai-changed-the-software-supply-chain), seen from the attacker's side.

[Anthropic's November 2025 report](https://www.anthropic.com/news/disrupting-AI-espionage) of a state-sponsored group operating Claude Code is a different problem: misuse by the operator, not hijacking. It is out of scope here.

## Prompt injection will not be patched away

If injection were like SQL injection, the answer would be a fix and a regression test.

The people closest to it say it is not.

### Security agencies and researchers agree on the diagnosis

The UK NCSC's post [Prompt injection is not SQL injection (it may be worse)](https://www.ncsc.gov.uk/blog-post/prompt-injection-is-not-sql-injection) (December 2025) says "prompt injection attacks may never be totally mitigated". It calls an LLM an "inherently confusable deputy" and recommends "deterministic (non-LLM) safeguards that constrain the actions of the system", plus logging of tool calls. OWASP's [Top 10 for LLM Applications 2025](https://genai.owasp.org/llm-top-10/) files the same problem under LLM01:2025 Prompt Injection and LLM06:2025 Excessive Agency, and the second is the one engineering can act on.

Simon Willison [explains](https://simonwillison.net/2025/Jun/16/the-lethal-trifecta/) why filters do not close the gap: "LLMs are unable to reliably distinguish the importance of instructions based on where they came from." A guardrail that stops 95% of attacks is "very much a failing grade".

[SABER](https://arxiv.org/abs/2606.01317) judges coding agents by the final state of a project workspace. Its tasks mix embedded injection with ordinary requests where the agent can take an unsafe shortcut or ignore a warning in the workspace, and its abstract reports that the best model tested still had "more than a 54% harmful safety-violation rate". That is a rate on the benchmark's tasks, not on your workload. It does undercut the hope that a well-behaved model keeps a workspace intact on its own.

### The trifecta and the Rule of Two describe the same exposure

Willison's lethal trifecta is "Access to your private data", "Exposure to untrusted content" and "The ability to externally communicate". Remove one leg and the exfiltration path breaks.

Meta's [Agents Rule of Two](https://ai.meta.com/blog/practical-ai-agent-security/) (31 October 2025) makes it a design rule. Until injection can be reliably detected, agents "must satisfy no more than two of the following three properties within a session": "process untrustworthy inputs", "access to sensitive systems or private data", "change state or communicate externally".

![](https://cdn.hashnode.com/uploads/covers/6818fe7787817224e50ba1ae/35c3e759-f978-48b6-aeaf-68d0b20074a5.png align="center")

Meta's third property is wider. It counts any state change, so a CI agent with no internet but permission to push a branch still holds it.

Most rows in the incident table hold all three at once. That is the default shape of a coding agent.

### The control moves to what the process can touch

You cannot reliably stop the model from being confused. You can decide, deterministically, what a confused process is able to do. The rest of this post is about that decision.

## Isolation boundaries differ in what a kernel bug costs

![](https://cdn.hashnode.com/uploads/covers/6818fe7787817224e50ba1ae/c77817cd-e7c3-43b7-affa-c5bb9def0620.jpg align="center")

*Photo by Brett Sayles on* [*Pexels*](https://www.pexels.com/photo/server-racks-on-data-center-5480781/)

"Sandbox" covers mechanisms with very different failure modes. One question sorts them: if code inside finds a kernel bug, what does it get?

### Containers, Landlock and bubblewrap share the host kernel

A container is a process wrapped in namespaces and cgroups. Every system call still lands in the host kernel. Docker's [default seccomp profile](https://docs.docker.com/engine/security/seccomp/) "disables around 44 system calls out of 300+", which narrows the surface but leaves most of the kernel reachable.

[Landlock](https://docs.kernel.org/userspace-api/landlock.html) "empowers any process, including unprivileged ones, to securely restrict themselves". It arrived in Linux 5.13 and grows by ABI version: TCP bind and connect from ABI 4, UDP only from ABI 10. It cannot restrict `stat`, `chmod`, `chown` or `access`, among others.

[Bubblewrap](https://github.com/containers/bubblewrap) builds a sandbox from user namespaces, an empty mount namespace and `PR_SET_NO_NEW_PRIVS`. Flatpak uses it, and so do Claude Code and Codex on Linux. Its README is blunt: "bubblewrap is not a complete, ready-made sandbox with a specific security policy." The policy is whatever the caller passes.

All three share one property: a kernel exploit from inside is a host compromise.

### macOS Seatbelt is deprecated and everywhere

On macOS, Claude Code, Codex, Cursor and Gemini CLI all run commands under Seatbelt via `sandbox-exec`, with generated or built-in profiles. The `sandbox-exec(1)` man page that ships with macOS says "The sandbox-exec command is DEPRECATED" and points developers to the App Sandbox instead.

A whole product category depends on it anyway.

That is a platform risk to track, not a reason to turn the sandbox off.

### gVisor and microVMs move the boundary

[gVisor](https://gvisor.dev/docs/architecture_guide/security/) puts a user-space kernel, the Sentry, in front of the host. "No system call is passed through directly to the host", and the Sentry's own host calls "do not include the creation of new sockets" unless host networking is on. An attacker must compromise the Sentry before touching the host kernel. Since mid-2023 the default [platform](https://gvisor.dev/docs/architecture_guide/platforms/) is systrap; ptrace is "no longer supported". The [cost is stated plainly](https://gvisor.dev/docs/architecture_guide/security/): transitions are "relatively expensive".

[Kata Containers](https://katacontainers.io/) runs each workload in a lightweight VM with a dedicated kernel, on QEMU, Cloud Hypervisor or Firecracker. [Firecracker](https://github.com/firecracker-microvm/firecracker/blob/main/SPECIFICATION.md) is the minimal VMM behind Lambda and Fargate, described in the [NSDI 2020 paper](https://www.usenix.org/conference/nsdi20/presentation/agache), and is "designed to be run securely inside an execution jail, set up by the `jailer` binary" per its [getting-started guide](https://github.com/firecracker-microvm/firecracker/blob/main/docs/getting-started.md).

In a microVM, a guest kernel bug compromises the guest. Reaching the host takes a hypervisor escape as well.

[Wasmtime](https://docs.wasmtime.dev/security.html) has the cleanest capability model: "All interaction with the outside world is done through imports and exports". But an agent running `npm install` and `pytest` needs a POSIX shell and native toolchains. In practice, Wasm suits generated functions, not shell-driving agents.

![](https://cdn.hashnode.com/uploads/covers/6818fe7787817224e50ba1ae/60386a16-a3fc-494e-b18b-2a4aa7d79c00.png align="center")

### The boundaries side by side

| Boundary | A kernel bug inside reaches | Main cost | Used for agents by |
| --- | --- | --- | --- |
| Container + seccomp | The host | Shares the host kernel | Gemini CLI (Docker, Podman), dev containers |
| Landlock | The host | Coverage depends on kernel ABI | Cursor on Linux |
| bubblewrap | The host | Caller writes the policy | Claude Code, Codex on Linux |
| macOS Seatbelt | The host | Deprecated interface | Claude Code, Codex, Cursor, Gemini CLI |
| gVisor | The Sentry first | Slower syscalls | Modal, Gemini CLI `runsc` option |
| microVM (Firecracker, Kata or other) | The guest; the host needs a hypervisor escape | Boot time, memory per VM | E2B, Vercel, Cloudflare; Docker Sandboxes (hypervisor not documented) |
| Wasm | A runtime bug, not a kernel bug | No POSIX shell | Not a fit for shell agents |

If you run agent commands in a container yourself, this is a reasonable floor. It is assembled from documented flags in the [`docker run` reference](https://docs.docker.com/reference/cli/docker/container/run/); the composition is ours.

```bash
# Linux host with Docker Engine and gVisor registered as the runsc runtime.
# One-time setup: sudo /usr/local/bin/runsc install && sudo systemctl reload docker
docker run --rm \
  --network none \
  --read-only --tmpfs /tmp \
  --cap-drop ALL \
  --security-opt no-new-privileges \
  --pids-limit 256 --memory 2g --cpus 2 \
  --user 1000:1000 \
  -v "$PWD":/work -w /work \
  -v "$PWD/.git":/work/.git:ro \
  --runtime=runsc \
  python:3.13-slim python script.py
```

With `--network none`, "only the loopback device is created" ([docs](https://docs.docker.com/engine/network/drivers/none/)). `--runtime=runsc` replaces the shared kernel with gVisor, per the [install page](https://gvisor.dev/docs/user_guide/install/). The workspace mount stays writable on purpose: it is the one thing the agent should change. `.git` is remounted read-only on top, so hooks and git config cannot be planted, and anything the host will run later (`Makefile`, `package.json` scripts, `.vscode/`) needs review before it runs.

## Agent sandboxes ship with gaps their own docs list

Every major coding agent now ships a sandbox or a firewall. The question is what it covers out of the box, and the vendors' docs answer it.

### Claude Code: off by default, and only for shell commands

[Claude Code's sandbox](https://code.claude.com/docs/en/sandboxing) uses Seatbelt on macOS and bubblewrap plus socat on Linux and WSL2. Native Windows commands run unsandboxed. It is off by default; `/sandbox` or `sandbox.enabled: true` turns it on.

It covers Bash, PowerShell and Monitor commands and their children. Built-in file tools, MCP servers, hooks, the statusline and `apiKeyHelper` run outside it. Once enabled, the defaults are:

- writes: working directory and temp, with `.claude` settings, `.bashrc`, `.gitconfig` and `.git` hooks protected
- reads: "Most of the machine, including credential files such as `~/.ssh` and `~/.aws/credentials`"
- network: proxy only, `allowedDomains` starts empty; no UDP, QUIC or ICMP
- environment: "Inherited from Claude Code, including any secrets in its environment"

A failed command can be retried outside the sandbox with `dangerouslyDisableSandbox` unless `allowUnsandboxedCommands` is false.

Anthropic [reports](https://www.anthropic.com/engineering/claude-code-sandboxing) that internally "sandboxing safely reduces permission prompts by 84%". That vendor-reported figure measures friction, not attacks stopped. The [sandbox environments page](https://code.claude.com/docs/en/sandbox-environments) is stricter: "Always run `--dangerously-skip-permissions` sessions inside a container, a VM, or the sandbox runtime."

Keys from the sandboxing docs (November 2026):

```json
{
  "sandbox": {
    "enabled": true,
    "failIfUnavailable": true,
    "allowUnsandboxedCommands": false
  }
}
```

`failIfUnavailable` makes Claude Code exit at startup, instead of quietly running unsandboxed, on an unsupported platform or when bubblewrap or socat is missing. The same docs list `sandbox.filesystem.denyRead`, and `sandbox.credentials.files` and `sandbox.credentials.envVars` with `deny` or `mask` (file masking since v2.1.221), plus `sandbox.network.allowedDomains` with `strictAllowlist` (since v2.1.219; user, managed or CLI settings only). Use them to take `~/.ssh`, `~/.aws` and secret-named variables out of reach before anything else.

### Codex: sandboxed by default, with two quiet gaps

[Codex](https://learn.chatgpt.com/docs/sandboxing) uses Seatbelt on macOS and bubblewrap on Linux, with a bundled fallback if `bwrap` is missing. "Linux uses `bwrap` plus `seccomp` by default", per the [approvals page](https://learn.chatgpt.com/docs/agent-approvals-security.md); write-ups describing a Landlock sandbox are out of date. In Git folders the default is `workspace-write` with `on-request` approvals, and "By default, the agent runs with network access turned off." `.git`, `.agents`, `.codex` and `.aws` stay read-only. The `untrusted` approval policy is retired and `on-failure` deprecated, so older configs may not mean what they say.

Gap one: domain rules in permission profiles are enforced only when `features.network_proxy.enabled = true`, which defaults to false, per the [config reference](https://learn.chatgpt.com/docs/config-file/config-reference.md). Network on without the proxy is direct and unfiltered.

Gap two: `shell_environment_policy.ignore_default_excludes` defaults to `true`, so variables with KEY, SECRET or TOKEN in their names reach the shell.

Adapted from the documented example, with the environment key from the config reference. The first keys pin the Git-folder defaults so they also hold outside Git folders; `ignore_default_excludes = false` is the change that matters.

```toml
# Codex config.toml (keys per learn.chatgpt.com docs, November 2026)
approval_policy   = "on-request"
sandbox_mode      = "workspace-write"
allow_login_shell = false   # do not source shell profiles for agent commands

[sandbox_workspace_write]
network_access = false

[shell_environment_policy]
ignore_default_excludes = false   # drop *KEY*, *SECRET*, *TOKEN* variables
```

Codex cloud splits by phase. The docs, now filed under "Codex Cloud (Legacy)", say "By default, Codex blocks internet access during the agent phase" ([docs](https://learn.chatgpt.com/docs/cloud/internet-access.md)). In that legacy design, setup scripts got internet and secrets, and secrets were removed before the agent ran. In the current cloud environments, programs see a placeholder and a proxy substitutes the real secret for allowed destinations. The legacy presets were None, Common dependencies and All, optionally limited to GET, HEAD and OPTIONS.

### The other agents mix firewalls and opt-in sandboxes

GitHub's coding agent became Copilot cloud agent, and from 28 October 2026 paid plans get the new [Copilot in the cloud](https://docs.github.com/en/copilot/concepts/agents/coding-agent/about-coding-agent) experience. Its [firewall](https://docs.github.com/en/copilot/how-tos/use-copilot-agents/coding-agent/customize-the-agent-firewall) is on by default with a recommended allowlist. GitHub states the limits: "It does not apply directly to Model Context Protocol (MCP) server processes", setup steps are excluded, and "Sophisticated attacks may bypass the firewall, potentially allowing unauthorized network access and data exfiltration." The firewall is also ["not compatible with self-hosted runners"](https://docs.github.com/en/copilot/how-tos/use-copilot-agents/coding-agent/customize-the-agent-environment) or Windows.

[Gemini CLI's sandbox](https://geminicli.com/docs/cli/sandbox/) is opt-in via `-s`, `GEMINI_SANDBOX` or `tools.sandbox`, backed by Seatbelt, Docker, Podman, gVisor or experimental LXC. The default Seatbelt profile, `permissive-open`, allows network access.

[Cursor](https://cursor.com/blog/agent-sandboxing) uses Seatbelt on macOS and Landlock plus seccomp on Linux, with the workspace in an overlay filesystem. It reports, as vendor figures, that sandboxed agents "stop 40% less often" and that about a third of requests on supported platforms run sandboxed.

[Docker Sandboxes](https://docs.docker.com/ai/sandboxes/architecture/) takes the other route and puts the agent in a microVM with a private Docker daemon. "All outbound TCP traffic from the sandbox routes through a proxy on your host", which swaps sentinel credential values for real ones outside the VM.

The [security page](https://docs.docker.com/ai/sandboxes/security/) lists the gaps. The default allowlist includes wildcards such as `*.googleapis.com`. The agent has sudo inside the VM. Local stdio MCP servers run on the host, which makes them part of the threat model covered in [MCP beyond the hype](https://blog.prashantkoirala.info.np/mcp-beyond-the-hype). In direct mode, git hooks and Makefiles the agent writes can run later on the host.

### Defaults at a glance

| Agent | Local mechanism | Sandbox by default | Network by default | Secrets by default |
| --- | --- | --- | --- | --- |
| Claude Code | Seatbelt; bubblewrap on Linux; none on native Windows | Off; shell commands only | Unrestricted until the sandbox is on; then proxy, empty allowlist | Reads `~/.ssh`; inherits env secrets |
| Codex CLI | Seatbelt; bwrap + seccomp | On in Git folders | Off; domain rules need the proxy flag | Keeps KEY, SECRET, TOKEN vars |
| Codex cloud | Hosted | Hosted | Blocked in agent phase | Legacy: setup only; current: proxy placeholder |
| Copilot in the cloud | Cloud sandbox or Actions runner | Hosted | Firewall; excludes MCP and setup | Own session token |
| Gemini CLI | Seatbelt, containers, gVisor | Off; opt-in | Default profile allows network | Not stated in sandbox docs |
| Cursor | Seatbelt; Landlock + seccomp | About a third of requests (vendor, Feb 2026) | Not stated | Not stated |
| Docker Sandboxes | microVM | It is the sandbox | Deny by default, broad built-in allowlist | Sentinel swap at proxy |

A sandbox that is off by default protects the people who were already careful.

## Sandboxes now get their own escape advisories

Three escapes, all fixed:

- **Claude Code** [GHSA-7835-87q9-rgvv](https://github.com/anthropics/claude-code/security/advisories/GHSA-7835-87q9-rgvv) (CVE-2026-55607), June 2026. `.git`\-named worktrees, a symlink and git's `fsmonitor` could overwrite `~/.zshenv` and run code outside the macOS sandbox, after the victim cloned a malicious repo with injection content. Fixed in 2.1.163.
- **Codex** [GHSA-w5fx-fh39-j5rw](https://github.com/openai/codex/security/advisories/GHSA-w5fx-fh39-j5rw) (CVE-2025-59532), September 2025. A model-generated working directory became the writable root, so writes could land outside the session folder. Fixed in CLI 0.39.0 and IDE extension 0.4.12.
- **Cursor**, whose [advisory list](https://github.com/cursor/cursor/security/advisories) includes a 2026 escape via Git hooks (GHSA-8pcm-8jpx-hv8r), a Critical one via failed path canonicalization (GHSA-3v8f-48vw-3mjx) and one via launching privileged containers (GHSA-v4xv-rqh3-w9mc).

Do not read advisory lists as a ranking. Volume reflects disclosure programs and bounties as much as code quality.

### Configuration files are the escape route

Line the advisories up against the incidents and the same files recur. Copilot's `.vscode/settings.json`. Cursor's `mcp.json`. Codex's project `config.toml`. Claude Code's `settings.json` ([GHSA-ff64-7w26-62rf](https://github.com/anthropics/claude-code/security/advisories/GHSA-ff64-7w26-62rf), "Sandbox Escape via Persistent Configuration Injection in settings.json", fixed in 2.1.2). Git hooks and `~/.zshenv`.

Each is read and acted on later, usually outside the sandbox. The products' write-protected lists now cover those files: Claude Code guards `.claude` settings and `.git` hooks, Codex keeps `.codex` and `.git` read-only, and Cursor's example sandbox policy denies writes to `.vscode`, `.cursor` and `.git/hooks`.

Any file a later, unsandboxed process will execute is part of the sandbox boundary.

### Patch the sandbox like a browser

Agent CLIs release often, and the sandbox ships inside them. Pinning an agent version also pins its sandbox bugs. Manage agent tools as fleet software with a minimum version and an owner who reads the advisories, the same discipline [vulnerability response is becoming a 24-hour operational duty](https://blog.prashantkoirala.info.np/vulnerability-response-is-becoming-a-24-hour-operational-duty) argues for on the vendor side.

## Hosted sandbox startup numbers need a definition

Products that run agents for their users rent hosted sandboxes, and three of the best-known platforms use Firecracker microVMs.

### The isolation is mostly Firecracker

E2B: ["Every E2B sandbox runs in its own Firecracker microVM with its own kernel"](https://e2b.dev/security.md). Vercel: a ["secure Firecracker microVM with its own filesystem and network"](https://vercel.com/docs/vercel-sandbox). Cloudflare: ["Each container instance runs in a Firecracker microVM with its own kernel and network"](https://developers.cloudflare.com/containers/platform-details/architecture/). [Modal](https://modal.com/docs/guide/sandbox) runs gVisor or a VM runtime. On Kubernetes, [agent-sandbox](https://github.com/kubernetes-sigs/agent-sandbox) adds a `Sandbox` resource that delegates isolation to a gVisor or Kata RuntimeClass, plus a `SandboxWarmPool`.

A warm pool is also where most fast startup numbers come from.

### Firecracker's number is to guest init

Firecracker's [specification](https://github.com/firecracker-microvm/firecracker/blob/main/SPECIFICATION.md) commits to "&lt;= 125 ms" from the InstanceStart call to the guest's `/sbin/init`, with a minimal kernel, no serial console, on metal instances, enforced by integration tests.

That is a strong result. Guest init is not "sandbox ready".

At `/sbin/init` no language runtime is warm, no repo is cloned and no SDK is connected. Vercel says its sandboxes "start in milliseconds", a vendor claim. Figures in that range generally come from snapshot restore or a warm pool, not a cold boot. Ask of any startup number:

- cold boot, snapshot restore or warm pool
- time to what: guest init, SDK connected, first command
- at what concurrency

### Persistence changes the threat model

[E2B](https://docs.e2b.dev/faq/sandbox-lifetime.md) defaults to a five-minute timeout, up to 24 hours on Pro, and paused sandboxes keep state ["indefinitely"](https://docs.e2b.dev/). Modal allows up to 24 hours plus snapshots. Vercel auto-saves state on stop. Docker Sandboxes persist until `sbx rm`. Whatever an injected instruction planted survives into the next session unless something resets it.

| Platform | Isolation | Startup claim | Egress by default | Lifetime |
| --- | --- | --- | --- | --- |
| E2B | Firecracker microVM | Not published in docs | Internet on | 5 min default, 24 h max (Pro); pause keeps state |
| Modal | gVisor or VM | Not published | Public IPs allowed | 5 min default, 24 h max |
| Vercel Sandbox | Firecracker microVM | "milliseconds" (vendor) | Configurable: open, deny-all, granular | Auto-save; snapshots |
| Cloudflare Containers | Firecracker microVM | Not published | Internet on; [`enableInternet: false`](https://github.com/cloudflare/containers) available | See docs |

Fast startup is what makes a fresh sandbox per task affordable. Use it for that.

## Egress is where the data leaves

The network is the trifecta leg a sandbox can cut most cleanly, and the defaults leave it partly open.

### Default deny needs a proxy, and the proxy needs a policy

Codex starts closed, and so does Claude Code's sandbox once it is enabled. Docker Sandboxes denies by default but ships a broad built-in allowlist. [E2B's internet is on by default](https://docs.e2b.dev/network/internet-access.md), [Modal allows outbound](https://modal.com/docs/guide/sandbox-networking) to public IPs, Gemini CLI's default profile allows the network, and Daytona's [tiers 3 and 4](https://www.daytona.io/docs/en/network-limits/) get full internet.

Opening the network selectively takes a proxy, and the strongest designs give the sandbox no direct route. On Linux, Anthropic's [sandbox runtime](https://github.com/anthropics/sandbox-runtime) runs commands without network access and bind-mounts HTTP and SOCKS5 proxies in over Unix sockets. The proxy resolves hostnames itself and refuses loopback, link-local and cloud metadata ranges.

### Hostname allowlists trust the client

Most allowlists decide on a name the client supplies. Claude Code's docs say so:

> "Allowing broad domains such as `github.com` can create paths for data exfiltration. Because the proxy makes its allow decision from the client-supplied hostname without inspecting TLS, code running inside the sandbox can potentially use domain fronting..."

Modal's beta domain allowlist matches by SNI without decryption and carries the same caveat. E2B matches Host or SNI, filters other ports by CIDR only, and cannot filter QUIC by domain. [Vercel](https://vercel.com/blog/a-sandbox-without-a-network-boundary-is-only-half-a-sandbox) inspects SNI, can terminate TLS selectively, and applies domain rules to DNS.

Claude Code also has an experimental `tlsTerminate` option. Without TLS termination, a domain allowlist checks the name on the envelope.

### Allowlisted domains are exfiltration channels

An honest hostname check still leaves the question of what the allowed host accepts. [PromptArmor's May 2026 analysis](https://www.promptarmor.com/resources/what-domains-should-i-add-to-my-allowlist) covers the usual entries:

- npm: publish a package an attacker can read
- `github.com`: push to a public repo or gist, or use GitHub as command and control
- `docs.google.com`: submit a prefilled Form with one GET request
- `*.amazonaws.com`: matches `attacker.s3.amazonaws.com`

The legacy Codex cloud docs show a hidden instruction in a GitHub issue piping the last commit message to an external server, and suggest allowing only GET, HEAD and OPTIONS. That helps against uploads. The Forms case shows a GET can still carry data in its URL.

E2B has a sharper trap: adding a domain to `allowOut` automatically allows 8.8.8.8 for all traffic. "Deny everything except one API" also opens one public resolver on every port.

### DNS is a network

[Zenity](https://labs.zenity.io/post/it-s-always-dns-in-claude-s-sandbox-from-data-exfiltration-to-a-bidirectional-dns-shell) found HTTP blocked but DNS resolvable in Claude's code-execution sandbox, enough for exfiltration and a shell over TXT records. Reported in November 2025 and fixed, it returned in May 2026: "A newer deployment had brought the problem back to life." Zenity calls it "Claude's sandbox" and does not say whether Claude Code is affected. [Mindgard](https://mindgard.ai/disclosures/cline-bot-ai-coding-agent-data-exfiltration-via-prompt-injection-and-dns) showed Cline leaking keys through `ping`, which needed no approval. Modal states that "DNS traffic is allowed without restriction except when all outbound traffic is blocked".

The Zenity regression is the lesson. Egress rules are configuration, configuration drifts, and a fixed leak can return in a later deployment. Test egress from inside the sandbox on a schedule.

## Credentials should never enter the sandbox

Egress controls limit where stolen data goes. The better move is to keep credentials out of reach entirely.

### Inherited environments are the default leak

The defaults run the wrong way. Claude Code's sandbox inherits secrets and can read `~/.aws/credentials`. Codex passes secret-named variables. An agent that can read a credential can be told to send it somewhere.

### Inject secrets at the proxy

Two patterns recur across vendors. Either the sandbox holds a placeholder or nothing and a proxy outside the boundary attaches the real credential on the way out, or the secret exists only in a phase the agent never runs in.

![](https://cdn.hashnode.com/uploads/covers/6818fe7787817224e50ba1ae/c76515c4-b608-4c02-b3d0-e4b6d2b177f3.png align="center")

- **Claude Code cloud**: a [custom proxy](https://www.anthropic.com/engineering/claude-code-sandboxing) validates a scoped credential and the push target branch, then attaches the real git token
- **Claude Code local**: `credentials` rules that `mask` files and variables
- **Docker Sandboxes**: sentinel value swapped at the host proxy
- **Vercel Sandbox**: host-side header transform
- **E2B**: `Secret.fill` for HTTPS requests to a chosen host
- **Codex cloud**: a proxy-substituted placeholder today; setup-phase-only secrets in the legacy design

This excerpt from [Vercel's August 2026 post](https://vercel.com/blog/a-sandbox-without-a-network-boundary-is-only-half-a-sandbox) (Vercel Sandbox SDK) injects a bearer token for one host, then cuts the network:

```ts
const sandbox = await Sandbox.create({
  networkPolicy: { allow: { "ai-gateway.vercel.sh": [{ transform: [{ headers: {
    "Authorization": `Bearer ${process.env.AI_GATEWAY_TOKEN}` } }] }] } }
});
await sandbox.update({ networkPolicy: 'deny-all' });
```

The token lives in the orchestrator, not the microVM. Code inside can call the gateway but cannot read the header. Header transforms are on Vercel's Pro and Enterprise plans, and `deny-all` also blocks DNS resolution.

Injection is not scoping. E2B documents two caveats for `Secret.fill`: every process sending HTTPS to that host gets the secret, and a missing secret fails open. Injected code can still use a credential through the proxy. That is why Anthropic's proxy also checks the branch.

### Make the tokens that do enter short-lived

GitHub Actions [OIDC tokens](https://docs.github.com/en/actions/concepts/security/openid-connect) are "only valid for a single job", with no stored secret. Vercel recommends its OIDC token for Sandbox auth. The same move for package publishing is covered in [software publishing is moving beyond long-lived tokens](https://blog.prashantkoirala.info.np/software-publishing-is-moving-beyond-long-lived-tokens); Nx and Cline both lost long-lived npm tokens.

## CI puts outsider text and release credentials in one job

![](https://cdn.hashnode.com/uploads/covers/6818fe7787817224e50ba1ae/fb1bdf47-2baa-4a7d-b073-47c5e3ffa833.jpg align="center")

*Photo by Miha Meglic on* [*Unsplash*](https://unsplash.com/photos/a-control-room-with-a-desk-and-two-chairs-p7Bfwn_VKRQ)

A CI job triggered by issues or pull requests can hold all three Rule of Two properties at once: it reads outsider text, holds secrets, and can push, publish and write caches. Add an agent with a shell and the rule is broken before the first prompt.

### Clinejection, step by step

[Adnan Khan's write-up](https://adnanthekhan.com/posts/clinejection/) (9 February 2026) is the most complete public chain, from an issue title to release tokens.

1. Cline's issue triage workflow, live from 21 December 2025, ran `claude-code-action` with `--allowedTools "Bash,Read,Write,Edit,Glob,Grep,WebFetch,WebSearch"`.
2. `allowed_non_write_users: "*"` let anyone who could open an issue trigger it.
3. The issue title was interpolated into the prompt.
4. An injected title could induce `npm install` of attacker code in the triage job.
5. Actions cache poisoning could reach the nightly release workflow, exposing `VSCE_PAT`, `OVSX_PAT` and `NPM_RELEASE_TOKEN`.

![](https://cdn.hashnode.com/uploads/covers/6818fe7787817224e50ba1ae/07a51255-5fc6-45ff-ba3a-39c6215acee9.png align="center")

Khan reported it on 1 January 2026 and, by his account, got no substantive response until public disclosure, after which a fix landed within roughly 30 minutes. On 17 February a different actor used an npm token that had not been properly revoked to publish `cline@2.3.0` with a postinstall running `npm install -g openclaw@latest`. Cline's statement, as quoted by Khan, said "The only modification was an added postinstall script"; 2.4.0 fixed it ([GHSA-9ppg-jx86-fqw7](https://github.com/cline/cline/security/advisories/GHSA-9ppg-jx86-fqw7)).

An agent with Bash, an input anyone could write, a shared cache and a long-lived token were enough.

[Aikido's PromptPwnd](https://www.aikido.dev/blog/promptpwnd-github-actions-ai-agents) (December 2025) shows it is a pattern: issue and PR text reaching agents with privileged tokens via the Gemini CLI action, Claude Code Actions, OpenAI Codex Actions and GitHub AI Inference. Google patched gemini-cli within four days. Aikido's "At least 5 Fortune 500 companies are impacted" is vendor-reported, with no names.

### Hardening an agent job in GitHub Actions

A skeleton for an issue-triage agent. The agent script is a placeholder; the workflow syntax is standard, and the checkout pin is actions/checkout v7.0.1.

```yaml
name: issue-triage

on:
  issues:
    types: [opened]

permissions: {}

jobs:
  triage:
    runs-on: ubuntu-24.04
    timeout-minutes: 15
    permissions:
      contents: read
      issues: write
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
        with:
          persist-credentials: false

      - name: Run the triage agent (placeholder script)
        env:
          ISSUE_TITLE: ${{ github.event.issue.title }}
          ISSUE_BODY: ${{ github.event.issue.body }}
          GH_TOKEN: ${{ github.token }}
          MODEL_API_KEY: ${{ secrets.MODEL_API_KEY }}
        run: ./scripts/triage-agent.sh
```

- `permissions: {}`, then only what the job needs; this token can label and comment, not push
- `persist-credentials: false`, so no token is left in `.git/config`
- issue text through `env`, never `${{ }}` inside `run:`; this stops shell injection, not prompt injection
- no Bash, Write or WebFetch for triage
- no release or cloud secrets in workflows outsiders can trigger
- the model API key is the only secret in the job: scoped to this workflow, with a spend limit, granting nothing else
- release jobs that do not restore caches untrusted jobs can write
- release credentials behind a protected `environment` with reviewers, and OIDC publishing instead of a stored token
- for Copilot sessions on Actions runners, ephemeral single-use runners, as GitHub recommends

A triage bot should be able to label an issue. That is all.

## Sandbox policy should follow input trust and reach

No single boundary fits every agent. Each step up in untrusted input should come with a step down in reach.

| Tier | Typical input | Minimum boundary | Network | Credentials |
| --- | --- | --- | --- | --- |
| Local interactive | Your code, docs you chose | Built-in sandbox on, approvals on | Proxy, short allowlist | Deny reads of `~/.ssh`, `~/.aws`; strip secret vars |
| Local autonomous | Same, plus whatever it fetches | Container, VM, Docker Sandboxes or sandbox runtime | Default deny | None inside; proxy injection |
| CI | Issues, PRs, third-party text | Ephemeral runner, minimal job token | Only what the job needs | OIDC; no release secrets on untrusted triggers |
| Hosted multi-tenant | Arbitrary user input | microVM or gVisor per session | Deny, changed per phase | Proxy injection only |

### Step one: inventory and turn on what ships

- every agent CLI and extension, with versions
- every workflow that invokes an agent, and its trigger
- every MCP server, and whether it runs inside the sandbox
- Claude Code: `sandbox.enabled`, `failIfUnavailable`, `allowUnsandboxedCommands: false`
- Codex: `ignore_default_excludes = false`; the proxy flag before any domain rules
- Gemini CLI: a sandbox enabled, with a profile chosen deliberately rather than the network-open default

### Step two: close the network, then open it narrowly

- default deny for the agent phase
- registries open during setup only, then deny
- no general-purpose hosts like `github.com` or `*.amazonaws.com` unless the task needs them
- DNS resolved by the proxy, or blocked with the rest of egress
- egress tested from inside, on a schedule

### Step three: take credentials out and treat config as code

- no long-lived tokens the agent can read
- proxy-side injection, scoped to one host, branch or job
- OIDC for cloud and registry access from CI
- agent settings, MCP config, editor settings, git hooks and shell startup files write-protected and reviewed like workflow changes
- persistent sandboxes reset between untrusted tasks

---

AI coding tools used to stop at a suggestion. A person read it and decided whether it ran.

Now the agent runs it, often before anyone reads it, and the text that shaped it may have come from an issue title or an install script. Prompt injection will keep working some of the time. The sandbox decides what that costs: which kernel the code shares, which hosts it reaches, which secrets it reads, which files it leaves for the next unsandboxed process.

The tools are namespaces, microVMs, proxies and short-lived tokens, applied to a new kind of process. Treat the agent as untrusted code with a helpful interface, and most of its incidents become boring.

## References

**Incident reports and advisories**

1. Invariant Labs, GitHub MCP toxic agent flow disclosure, 26 May 2025: <https://invariantlabs.ai/blog/mcp-github-vulnerability>
2. Tracebit, Gemini CLI allowlist bypass and README injection, 28 July 2025: <https://tracebit.com/blog/code-exec-deception-gemini-ai-cli-hijack>
3. AWS Security Bulletin AWS-2025-015, CVE-2025-8217 (Amazon Q Developer for VS Code), 23 July 2025: <https://aws.amazon.com/security/security-bulletins/AWS-2025-015/>
4. The Register, Amazon Q prompt report, 24 July 2025: <https://www.theregister.com/2025/07/24/amazon_q_ai_prompt/>
5. Cursor security advisories (GHSA-4cxx-hrm3-49rm, GHSA-8pcm-8jpx-hv8r, GHSA-3v8f-48vw-3mjx, GHSA-v4xv-rqh3-w9mc and others): <https://github.com/cursor/cursor/security/advisories>
6. Tenable, FAQ on CurXecute and MCPoison: <https://www.tenable.com/blog/faq-cve-2025-54135-cve-2025-54136-vulnerabilities-in-cursor-curxecute-mcpoison>
7. Check Point Research, Cursor MCPoison (CVE-2025-54136), 5 August 2025: <https://research.checkpoint.com/2025/cursor-vulnerability-mcpoison/>
8. Johann Rehberger, GitHub Copilot RCE via prompt injection (CVE-2025-53773), 12 August 2025: <https://embracethered.com/blog/posts/2025/github-copilot-remote-code-execution-via-prompt-injection/>
9. Nx, GHSA-cxm3-wv7p-598c, 27 August 2025: <https://github.com/nrwl/nx/security/advisories/GHSA-cxm3-wv7p-598c>
10. Socket, Nx package compromise analysis: <https://socket.dev/blog/nx-packages-compromised>
11. Snyk, weaponizing AI coding agents in the Nx malicious package: <https://snyk.io/blog/weaponizing-ai-coding-agents-for-malware-in-the-nx-malicious-package>
12. Check Point Research, Codex CLI command injection (CVE-2025-61260), 1 December 2025: <https://research.checkpoint.com/2025/openai-codex-cli-command-injection-vulnerability/>
13. OpenAI Codex, GHSA-w5fx-fh39-j5rw (CVE-2025-59532), 19 September 2025: <https://github.com/openai/codex/security/advisories/GHSA-w5fx-fh39-j5rw>
14. Anthropic Claude Code, GHSA-ff64-7w26-62rf (CVE-2026-25725): <https://github.com/anthropics/claude-code/security/advisories/GHSA-ff64-7w26-62rf>
15. Anthropic Claude Code, GHSA-7835-87q9-rgvv (CVE-2026-55607), 25 June 2026: <https://github.com/anthropics/claude-code/security/advisories/GHSA-7835-87q9-rgvv>
16. Aikido Security, PromptPwnd, 4 December 2025: <https://www.aikido.dev/blog/promptpwnd-github-actions-ai-agents>
17. Adnan Khan, Clinejection, 9 February 2026: <https://adnanthekhan.com/posts/clinejection/>
18. Cline, GHSA-9ppg-jx86-fqw7: <https://github.com/cline/cline/security/advisories/GHSA-9ppg-jx86-fqw7>
19. Anthropic, report on an AI-orchestrated espionage campaign, 13 November 2025: <https://www.anthropic.com/news/disrupting-AI-espionage>
20. Zenity Labs, DNS exfiltration in Claude's sandbox, 27 August 2026: <https://labs.zenity.io/post/it-s-always-dns-in-claude-s-sandbox-from-data-exfiltration-to-a-bidirectional-dns-shell>
21. Mindgard, Cline data exfiltration via prompt injection and DNS, 18 November 2025: <https://mindgard.ai/disclosures/cline-bot-ai-coding-agent-data-exfiltration-via-prompt-injection-and-dns>
22. PromptArmor, allowlist domain analysis, 13 May 2026: <https://www.promptarmor.com/resources/what-domains-should-i-add-to-my-allowlist>

**Guidance, frameworks and research**

23. UK NCSC, Prompt injection is not SQL injection (it may be worse), 8 December 2025: <https://www.ncsc.gov.uk/blog-post/prompt-injection-is-not-sql-injection>
24. Simon Willison, the lethal trifecta, 16 June 2025: <https://simonwillison.net/2025/Jun/16/the-lethal-trifecta/>
25. Meta AI, Agents Rule of Two, 31 October 2025: <https://ai.meta.com/blog/practical-ai-agent-security/>
26. OWASP, Top 10 for LLM Applications 2025: <https://genai.owasp.org/llm-top-10/>
27. SABER, arXiv:2606.01317: <https://arxiv.org/abs/2606.01317>
28. Anthropic Engineering, Beyond permission prompts: making Claude Code more secure and autonomous, 20 October 2025: <https://www.anthropic.com/engineering/claude-code-sandboxing>

**Agent product documentation**

29. Claude Code, Sandboxing: <https://code.claude.com/docs/en/sandboxing>
30. Claude Code, Sandbox environments: <https://code.claude.com/docs/en/sandbox-environments>
31. Anthropic sandbox runtime: <https://github.com/anthropics/sandbox-runtime>
32. OpenAI Codex, Sandboxing: <https://learn.chatgpt.com/docs/sandboxing>
33. OpenAI Codex, Agent approvals and security: <https://learn.chatgpt.com/docs/agent-approvals-security.md>
34. OpenAI Codex, Configuration reference: <https://learn.chatgpt.com/docs/config-file/config-reference.md>
35. OpenAI Codex cloud, Internet access: <https://learn.chatgpt.com/docs/cloud/internet-access.md>
36. GitHub Docs, Copilot in the cloud (coding agent) overview: <https://docs.github.com/en/copilot/concepts/agents/coding-agent/about-coding-agent>
37. GitHub Docs, Customize the agent firewall: <https://docs.github.com/en/copilot/how-tos/use-copilot-agents/coding-agent/customize-the-agent-firewall>
38. GitHub Docs, Customize the agent environment: <https://docs.github.com/en/copilot/how-tos/use-copilot-agents/coding-agent/customize-the-agent-environment>
39. Gemini CLI, Sandboxing: <https://geminicli.com/docs/cli/sandbox/>
40. Cursor, agent sandboxing post, 18 February 2026: <https://cursor.com/blog/agent-sandboxing>
41. Docker, Sandboxes architecture: <https://docs.docker.com/ai/sandboxes/architecture/>
42. Docker, Sandboxes security: <https://docs.docker.com/ai/sandboxes/security/>

**Isolation technology**

43. Docker, Seccomp security profiles: <https://docs.docker.com/engine/security/seccomp/>
44. Docker, None network driver: <https://docs.docker.com/engine/network/drivers/none/>
45. Docker, `docker container run` reference: <https://docs.docker.com/reference/cli/docker/container/run/>
46. Linux kernel documentation, Landlock: <https://docs.kernel.org/userspace-api/landlock.html>
47. Bubblewrap: <https://github.com/containers/bubblewrap>
48. Apple, `sandbox-exec(1)` man page, shipped with macOS (`man sandbox-exec`)
49. gVisor, Security model: <https://gvisor.dev/docs/architecture_guide/security/>
50. gVisor, Platforms: <https://gvisor.dev/docs/architecture_guide/platforms/>
51. gVisor, Installation: <https://gvisor.dev/docs/user_guide/install/>
52. Kata Containers: <https://katacontainers.io/>
53. Firecracker, [SPECIFICATION.md](http://SPECIFICATION.md): <https://github.com/firecracker-microvm/firecracker/blob/main/SPECIFICATION.md>
54. Firecracker, Getting started: <https://github.com/firecracker-microvm/firecracker/blob/main/docs/getting-started.md>
55. Agache et al., Firecracker: Lightweight Virtualization for Serverless Applications, NSDI 2020: <https://www.usenix.org/conference/nsdi20/presentation/agache>
56. Wasmtime, Security: <https://docs.wasmtime.dev/security.html>

**Hosted sandbox platforms and credentials**

57. E2B, Security: <https://e2b.dev/security.md>
58. E2B, Internet access: <https://docs.e2b.dev/network/internet-access.md>
59. E2B, Sandbox lifetime: <https://docs.e2b.dev/faq/sandbox-lifetime.md>
60. Modal, Sandboxes: <https://modal.com/docs/guide/sandbox>
61. Modal, Sandbox networking: <https://modal.com/docs/guide/sandbox-networking>
62. Vercel, Vercel Sandbox: <https://vercel.com/docs/vercel-sandbox>
63. Vercel, network boundary post, 11 August 2026: <https://vercel.com/blog/a-sandbox-without-a-network-boundary-is-only-half-a-sandbox>
64. Cloudflare, Containers architecture: <https://developers.cloudflare.com/containers/platform-details/architecture/>
65. Cloudflare, containers package README (`enableInternet`): <https://github.com/cloudflare/containers>
66. Daytona, Network limits: <https://www.daytona.io/docs/en/network-limits/>
67. kubernetes-sigs/agent-sandbox: <https://github.com/kubernetes-sigs/agent-sandbox>
68. GitHub Docs, OpenID Connect: <https://docs.github.com/en/actions/concepts/security/openid-connect>
