Published
- 5 min read
By Allan D - Editor, AI Security Wire
The Pyodide Sandbox Escape That Hit Cohere, Hugging Face, and n8n
Researchers from Cyera Research Labs used a DEF CON 34 talk this month to walk through a sandbox escape that turned out not to be a single product bug at all. It was an architectural flaw in Pyodide, the WebAssembly build of CPython that lets Python execute inside JavaScript runtimes, and once they knew what to look for, they found it reproduced across seven separate products. Four CVEs came out of the work, ranging from 8.3 to 9.9 on the CVSS scale. Two of the affected tools, Cohere’s Terrarium and Hugging Face’s smolagents, are sandboxes built specifically to let AI agents write and run Python on the fly, which is exactly the kind of infrastructure more agentic AI deployments are starting to lean on.
The blocklist problem
Every affected product made the same design choice: block the Python functions that look dangerous, then trust the rest of the interpreter. Teams disabled os.system, patched the JavaScript bridge, and locked down obvious paths to the Object constructor. What they left alone was ctypes, Python’s foreign function interface for calling native code directly, and _pyodide._base.eval_code, an internal code-evaluation entry point that runs outside the context where the patches had been applied.
Ctypes is the more interesting of the two, because it doesn’t need to touch anything a blocklist would think to flag. An attacker can resolve libc.system() through the FFI and call it straight from Python, skipping the wrapped, patched version of os.system entirely. Cyera’s researchers, Vladimir Tokarev and Ofek Itach for the n8n disclosure, and Saar Pearl on the broader Pyodide work, showed this bypass working in as few as two lines of code. The second path, invoking eval_code directly, sidesteps the problem differently: it runs in an execution context that never received the security patches in the first place, so restrictions that hold everywhere else in the sandbox simply don’t apply there.
The deeper issue is that WebAssembly’s memory isolation was never the thing keeping these sandboxes safe. WASM protects its own linear memory just fine. It does nothing to stop code from reaching capabilities that the embedding JavaScript environment chooses to expose, and Emscripten, the toolchain Pyodide is built with, exposes quite a lot. A blocklist enumerated at the Python layer has no way to account for what’s reachable one layer down.
What it looked like in production: n8n
The clearest illustration is CVE-2025-68668, the n8n flaw carrying the highest CVSS score in the set at 9.9. n8n’s Code node let low-privilege users with workflow-creation permissions run Python through Pyodide inside the same Node.js process as the rest of the application. Using either bypass, an attacker could execute arbitrary system commands, reach the SQLite database mounted inside the container, and rewrite their own user record to escalate from standard user to administrator. From there, every credential and API token n8n had stored for connected integrations was exposed, along with a pivot point into whatever SaaS platforms and infrastructure those integrations touched.
n8n was notified on October 27, 2025, published mitigation guidance a month later, and shipped a patch on December 24, 2025, by moving Python execution out of the deprecated in-process Pyodide mode and into an external runner with genuine process isolation. That last detail is the throughline for the whole disclosure: patching the specific bypass doesn’t fix the architecture, moving execution outside the process does.
The other six products
Grist, an open-source spreadsheet and database platform that supports Python formulas, picked up CVE-2026-24002 at CVSS 9.1, with the same sandbox-escape mechanics giving an attacker a path to sensitive data behind the spreadsheet layer. Cohere’s Terrarium, marketed as a sandboxed environment specifically for executing AI-generated code, was assigned CVE-2026-61522 at CVSS 9.3. Hugging Face’s smolagents, a framework for building agents that can call tools and execute code, drew CVE-2026-10613 at CVSS 8.3, the lowest of the four scored CVEs but still a working escape out of an AI agent’s execution boundary. Researchers also reproduced the same underlying bypass in langchain-sandbox, the Streamlit-adjacent app framework stlite, and the Python wheel-building tool cibuildwheel, bringing the total to seven confirmed products, though not all seven received individual CVE identifiers.
Maintainer responses varied. Some rebuilt around process-level isolation the way n8n did. Others archived the affected component outright rather than maintain it. A few pushed the isolation responsibility down to deployment configuration, telling operators to run the sandbox in its own container or VM rather than fixing the library-level assumption. That inconsistency is itself a signal: there is no single upstream fix for “Pyodide plus a blocklist,” because the flaw lives in the pattern, not in any one codebase.
Defensive guidance
For any team running Pyodide, whether directly or vendored inside a workflow tool, AI agent framework, or code-execution feature, blocklisting individual Python modules should not be treated as a security boundary on its own. Restrict or remove ctypes where it isn’t needed, and audit for other alternate execution paths like eval_code that sit outside your primary patch surface.
The more durable fix is architectural: treat Pyodide-based code execution the way you’d treat any untrusted-code problem, with process or container-level isolation, least-privilege service accounts, restricted outbound network access, and short-lived, workload-scoped credentials rather than long-lived tokens sitting in the same process as the interpreter. Ephemeral, single-use containers for untrusted execution close off the credential-harvesting and lateral-movement path that made the n8n case so severe.
Security teams evaluating AI agent platforms should ask a specific question that this research makes newly relevant: when the agent executes model-generated code, what actually isolates that execution, and is the answer a blocklist or a boundary? Given how many products reused the same blocklist assumption without realizing it, the honest answer for a lot of deployed AI tooling right now is probably the former.
References
Frequently Asked Questions
- What is Pyodide and why do AI tools use it?
- Pyodide is a build of CPython compiled to WebAssembly, which lets Python code run inside JavaScript environments like browsers, Node.js, and Deno. AI platforms use it to let a model or a user submit Python code and get it executed without spinning up a full server-side interpreter, which is why it shows up inside AI code-execution sandboxes like Cohere's Terrarium and Hugging Face's smolagents.
- What was the actual vulnerability?
- Products built blocklist-based sandboxes around Pyodide that blocked functions like os.system but left Python's ctypes module and the pyodide._base.eval_code function reachable. Ctypes gives Python a foreign function interface into native code, so an attacker could call libc.system() directly in as little as two lines of code, bypassing every patched entry point.
- Which products and CVEs came out of this research?
- Four CVEs were assigned: CVE-2025-68668 in n8n (CVSS 9.9), CVE-2026-24002 in the Grist spreadsheet platform (CVSS 9.1), CVE-2026-61522 in Cohere's Terrarium AI code sandbox (CVSS 9.3), and CVE-2026-10613 in Hugging Face's smolagents agent framework (CVSS 8.3). Researchers also reproduced the escape in langchain-sandbox, stlite, and cibuildwheel, seven affected products in total.