Two ways out. One lets a patch write anywhere on the disk with no prompt. The other gets unsandboxed command execution out of read-only, the strictest mode Codex has.
We found two ways out of the OpenAI Codex sandbox and reported both to OpenAI on August 12, 2026. Both were fixed inside of eight days.
The first is in the open-source Codex CLI. One extra line in a patch hands the patch tool write access to the whole disk, in the normal agent mode, with no approval prompt. We call it Overpatch.
The second is in the JavaScript tool that Codex Desktop installs. The sandbox worked, but the secret that told trusted code from untrusted code was sitting in memory the untrusted code could read. We call it Heapjack. It runs at read-only, where the agent supposedly can’t write anything, and it ends with unsandboxed command execution.
Overpatch #
In workspace-write mode, the agent is only allowed to write inside the workspace folder. A shell command that tries $HOME is refused.
We got apply_patch to write there anyways.
apply_patch is a tool the Codex harness gives the agent for editing files. The bug is that it grants write access to the parent folder of each path in the patch. Name /tmp and it grants write access to /.
The patch that lands the write has two changes. One appends a line to .zshrc through a symlink into $HOME. The other names /tmp and does nothing. That change fails, because you cannot write a file over a directory. It is in the patch only to widen the grant. Take that line out and the write is refused.
Write .zshrc and the next terminal you open runs it unsandboxed.
Heapjack #
Codex Desktop writes an [mcp_servers.node_repl] block into the global ~/.codex/config.toml at install. No opt-in, no setting to turn it off. That is why the plain codex CLI gets the tool too.
The tool is a native Rust program, unsandboxed. It opens applications, connects to unix sockets, writes the config file. It starts a Node process through the Codex sandbox, so all JavaScript runs sandboxed. Inside that one Node process there are two V8 vm contexts. The trusted one holds OpenAI’s own code and gets the secret token. The untrusted one runs the agent’s code and does not.
The trusted context proves it is the trusted context with a random token, generated fresh on every run.
One Node process, one V8 isolate, one heap shared by both contexts. The token is a string in that heap.
Untrusted code takes a snapshot of the heap (v8.getHeapSnapshot()) and tries every string shaped like a UUID. A wrong token comes back not authorized. A right token with a bad argument comes back with a real validation error.
The sandboxed Node process sends its requests to that Rust parent as JSON lines written to fd 1, its stdout. One pipe, shared by both contexts. The untrusted code writes its own request onto that pipe, in the same format. The native parent reads it, checks the token, and does the work. Knowing that one string is the whole boundary.
The parent runs the system open command, so launchd starts the application unsandboxed, outside Codex’s process tree. That is what our proof of concept did. The same token allows connecting to any unix socket, a Docker daemon socket being the obvious one. It also exposes a tool for editing the global ~/.codex/config.toml.
All of it ran at read-only, the strictest sandbox mode, where the agent supposedly can’t write anything. Codex never asked to approve anything.
Open a cloned repo in Codex in read-only sandbox mode, ask it a question, and whoever wrote that repository gets unsandboxed command execution, with no prompt and nothing on screen. The same access they would have if you turned the sandbox off and ran their script yourself.
How we built Accomplish #
Both bugs have the same shape. The thing doing the enforcement was sitting inside the thing being enforced. apply_patch worked out its own permissions from input it was handed. node_repl kept the secret that separates trusted from untrusted in the same memory as the untrusted code. Both sandboxes were told, from the inside, to let something through.
That is why Accomplish runs the whole agent inside a VM. The model, bash, git, and every process any of them start. Real credentials never enter the guest at all; the agent works with placeholders, and outbound traffic goes through a proxy on the host that the agent cannot reach or reconfigure. We treat everything in the guest as untrusted, root included, because nothing that decides what is allowed is running in there with it.
Run Overpatch under Accomplish and the patch writes anywhere it likes, inside the VM. Run Heapjack and the forged request launches an application, inside the VM. Neither one reaches your laptop.