Cellbreak: Grist’s Pyodide Sandbox Escape and the Data-at-Risk Blast Radius

Key Findings
- Cyera Research Labs uncovered a Pyodide sandbox escape that leads to RCE in Grist‑Core.
- Grist‑Core is a programmable alternative to Excel/Sheets and is widely used across government, education, and industry workflows.
- Cyera Research Labs worked with the Grist‑Core security team, and the issue was promptly patched.
Motivation
One malicious formula can turn a spreadsheet into a Remote Code Execution (RCE) beachhead. This sandbox escape lets a formula author execute OS commands or run host‑runtime JavaScript, collapsing the boundary between “cell logic” and host execution.
Grist‑Core is a modern relational spreadsheet and programmable alternative to Excel and Google Sheets. Teams use it to model business data, build lightweight apps, and automate workflows with Python formulas across tables and integrations. Grist is available as both a managed service and a self‑hosted deployment, which means this execution surface can exist in SaaS and on‑prem environments. In practice, that makes Grist a data hub: it sits next to customer records, operational metrics, and SaaS integrations, and it can be configured to access the credentials that power those workflows.
That deployment model is what drives the blast radius. This isn’t just a self‑hosted “Excel‑like” problem: in SaaS, the execution surface can live inside a vendor‑operated environment that holds customer data and integrations. In that model, a sandbox escape is not just local server RCE - it becomes RCE in the SaaS control plane that runs tenants’ workflows. More broadly, Grist functions as a programmable data plane: tables, formulas, and automation sit between SaaS systems, internal databases, and operational processes. When that sandbox fails, the blast radius expands to credentials, data access paths, and potentially downstream systems tied to the Grist environment.
Grist is also used across a wide range of sectors, which amplifies the impact of any sandbox escape. Public sources show adoption in government (including France’s public sector), education (1,000+ organizations in higher ed), and named case studies in marketing and game design teams. These are not niche edge cases; they demonstrate how Grist is used for real operational data and workflows across industries.
Those formulas run inside a Pyodide (WebAssembly) sandbox that is assumed to be safe. That assumption is wrong.
This risk is not theoretical. The report demonstrates three reliable escape paths: os.system() via class hierarchy traversal, ctypes.CDLL(None).system() via direct C library access, and emscripten_run_script_string()via Emscripten runtime access. The result: a spreadsheet cell becomes a pathway to host‑level compromise.
This is the same class of systemic risk we highlighted in the n8n Pyodide sandbox escape (N8Scape): when an automation or data platform becomes a trusted execution plane, a sandbox escape becomes a trust‑boundary collapse, not just a single‑server bug.
Data as the Exploit Vector
What makes this vulnerability genuinely novel is that it weaponizes a legitimate data package. The attack doesn’t look like classic boundary‑breaking code injection; it rides the same data‑processing path Grist uses to evaluate formulas. That turns routine spreadsheet data into an execution vector, which means security review must treat data pipelines and formula engines as high‑risk execution layers, not just “content.”

Why This Matters: The SaaS Blast Radius.
This isn’t just a self‑hosted “Excel‑like” problem. Grist runs as a managed SaaS as well, which means the execution surface can live inside a vendor‑operated environment that holds customer data and integrations. In that model, a sandbox escape is not just local server RCE - it becomes RCE inside the SaaS control plane that powers many tenants’ workflows.
Grist is a programmable data plane: tables, formulas, and automation sit between SaaS systems, internal databases, and operational workflows. Any system that executes formulas on sensitive data is effectively an execution engine. When that sandbox fails, the blast radius expands to credentials, data access paths, and potentially downstream systems tied to the Grist environment.
Vulnerability Summary
The vulnerable surface is Grist’s Python formula execution, where untrusted formulas run inside Pyodide. The sandbox’s design allows traversal through Python’s class hierarchy and leaves ctypes available, which together open access to Emscripten runtime functions that should never be reachable from a formula cell.
That combination enables host command execution and JavaScript execution in the host runtime, with practical outcomes like filesystem access and secret exposure. The issue is tracked as GHSA-7xvx-8pf2-pv5g with CVSS 9.1.
The Sandbox Model: What Grist Blocks vs. What It Leaves Open
Grist executes formulas in Pyodide (WASM). The intent is to keep formula code isolated. However, the sandbox uses a blocklist-style approach, and the report demonstrates multiple alternate paths to reach the same capabilities.
Key escape enablers:
- Python’s object model allows traversal to
object.__subclasses__()and classes that reference full__builtins__. - ctypes is available inside Pyodide, which exposes the Emscripten runtime symbol table.
- Emscripten exposes host hooks such as
emscripten_run_script_string, which executes JavaScript in the host runtime.
Technical Deep Dive

1) os.system() via class hierarchy traversal
The blog shows that warnings.catch_warnings exposes full __builtins__, allowing import of os and direct system calls:

Why it works: Pyodide restricts builtins at the top level, but Python’s object model still provides paths to the original __builtins__ object.
2) ctypes.CDLL(None).system() via C library access
Because ctypes remains available, attackers can call libc-style functions directly:

Why it works: ctypes.CDLL(None) loads the process’s exported symbols in the Emscripten runtime, including system().
3) emscripten_run_script_string() to execute JavaScript
This is the most powerful path: if you can call emscripten_run_script_string, you can execute JavaScript in the host runtime and then call require('child_process') or access process.env:

Why it works: Emscripten exposes JavaScript execution primitives; Pyodide doesn’t remove those exports, and ctypes lets Python reach them.
Proof of Concept (End-to-End Context)

In this setup, formula payloads sent via HTTP reach wasm_worker.js, which runs Pyodide and evaluates the formula string. The escape payloads above achieve command execution on the host.
Real-World Impact: Data at Risk
Once a formula can execute commands or JavaScript, a Grist instance stops being “just a spreadsheet.” It becomes a stepping stone to:
- Database credentials and API keys in environment variables.
- Sensitive files accessible by the host runtime (configs, tokens, secrets)
- Lateral movement into adjacent systems if the Grist host is trusted.
This mirrors the systemic risk found in other automation platforms: a single execution surface with privileged access can collapse organizational trust boundaries when its sandbox fails.
POC Clip Link:
https://www.youtube.com/watch?v=qn80PVOic1I
How Grist fixed the issue
Grist’s fix was to move Pyodide formula execution under Deno by default (released in Grist 1.7.9). That matters because it changes the failure mode: instead of “a Pyodide escape immediately becomes host compromise,” the host runtime is now mediated by Deno’s permission model. In the standard patched configuration, the same escape primitives demonstrated in this write‑up no longer translate into reliable host-level command execution out of the box, because Deno blocks sensitive capabilities unless they are explicitly granted.
There is an important caveat: if an operator sets GRIST_PYODIDE_SKIP_DENO=1, Deno is bypassed and the risk returns. That flag should be treated as an explicit opt‑in to a weaker isolation boundary and avoided anywhere untrusted or semi‑trusted formulas may run.
Operational next steps
Start by upgrading to the patched release and validating that your deployment is actually using the patched execution path (Pyodide under Deno, not skipped). Then treat formula execution as a privileged capability rather than “spreadsheet content”: in collaborative deployments, restrict who can create or modify formulas and assume that imported documents may contain hostile formula logic. Finally, reduce blast radius so that future sandbox failures don’t immediately become a full environment compromise: run Grist with least privilege, keep secrets out of easy reach (environment variables and readable config files), and constrain network access so a compromise can’t trivially pivot into adjacent internal systems.
Takeaway
Spreadsheets have evolved into programmable data hubs. When formula execution relies on a permissive sandbox, a single escape can turn “data logic” into “host execution.” The Grist-Core findings show why sandboxing needs to be capability-based and defense-in-depth, not a fragile blocklist. The cost of failure is not just a bug - it is a data-plane breach.
If you’re operating Grist or similar systems, treat formula execution as a privileged capability and harden accordingly.
Timeline and Disclosure
- 30 Dec 2025: VladimirEliTokarev opened the original advisory GHSA‑xgwp‑m72r‑c4x2 (3 weeks prior; 8 comments).
- 20 Jan 2026: Patch released (Grist 1.7.9) with Pyodide running under Deno by default.
- 21 Jan 2026: Public advisory published (GHSA‑7xvx‑8pf2‑pv5g).
Source: GitHub advisory
CVSS 9.1 -Plain‑English Justification
The CVSS score was calculated using CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:H/I:H/A:H:
- AV:N: The attacker can trigger the issue remotely via a malicious document and formula execution.
- AC:H: Exploitation requires a specific sandbox configuration (GRIST_SANDBOX_FLAVOR=pyodide).
- PR:N: No privileges are required if a victim opens a malicious document in a vulnerable configuration.
- UI:N: No additional user interaction is required beyond the document being processed.
- S:C: The escape crosses the sandbox boundary into host execution.
- C:H / I:H / A:H: Once RCE is achieved, confidentiality, integrity, and availability are fully impacted.
Collaboration with the Grist Security Team
We appreciate the prompt response from the Grist security team and their quick patch to run Pyodide under Deno.
Source: GitHub advisory
References:
Cyera n8n Pyodide sandbox escape analysis,
Grist advisory GHSA-7xvx-8pf2-pv5g,
Clever Cloud announcement,
Grist Education,
MissionSource case study,
Savage Game Design case study





