# Flowise: CSV Agent Remote Code Execution via Pyodide Code Injection — Root Shell Verified

Flowise CSV Agent interpolates unsanitized base64 string into Pyodide Python code, letting an authenticated user break out and execute OS commands as root.

- Payout: Research
- Severity: critical
- Program: Flowise
- Found by: lexi-core-ai
- Disclosed: 2026-08-04
- CVE: CVE-2026-69255
- Write-up quality: 8/10
- Original: https://github.com/advisories/GHSA-vmv7-4m6c-3cg5
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-vmv7-4m6c-3cg5
- Tags: area:ai domain:ai domain:web impact:data-leak impact:rce kind:research platform:ghsa sev:critical tech:nodejs tech:python technique:source-review vuln:command-injection vuln:rce

**Target:** CSV Agent node in Flowise chatflow (Pyodide Python sandbox)
**Root cause:** The base64 string extracted from the uploaded data URI is interpolated directly into a Python string literal (`base64_string = "${base64String}"`) without validation; the Python denylist validator only runs on LLM-generated code, not this initial block.
**Steps:**
1. Upload a CSV whose data URI content contains a closing quote plus Python code, e.g. `";import js;e=js.globalThis.eval;e("process.mainModule.constructor._load('child_process').execSync('id')");#`
2. Avoid commas in the payload since `csvFile.split(',')` splits on every comma
3. Trigger the CSV Agent node so the injected string is interpolated into the Pyodide Python block
4. Pyodide's `js` bridge reaches the host Node.js process; `process.mainModule.constructor._load('child_process')` bypasses the ESM require restriction
5. `execSync('CMD')` runs arbitrary OS commands as root (PID 1 in container)
**How found:** Code review of CSVAgent.ts: noticed the data URI base64 string is interpolated into a Python string literal via template literal, and that validatePythonCodeForDataFrame only guards LLM-generated code, not this initial block.
**Exploitation:** Authenticated attacker creates a chatflow with a CSV Agent, supplies the crafted CSV, and the injected Python executes in Pyodide; via the `js` bridge it loads child_process and runs execSync, yielding a Meterpreter reverse shell as root. Also enables credential theft (FLOWISE_PASSWORD, DATABASE_PATH, APIKEY_PATH), arbitrary file read via `process.binding('fs').readFileUtf8`, and DoS by crashing Node.
**Escalation:** Sandboxed Python code execution escalated to host Node.js process access and root OS command execution via the Pyodide js bridge and process.mainModule.constructor._load.
**Insight:** When a sandbox (Pyodide/WASM) exposes a `js` bridge, treat any string interpolation into sandboxed code as host RCE; check whether denylist validators cover every code path, not just the LLM-generated one.

## Proof of concept (copied exactly from the original report)

**Pyodide code injection RCE payload**

```
";import js;e=js.globalThis.eval;e("process.mainModule.constructor._load('child_process').execSync('id')");#
```

