# Flowise: CSV Agent Prompt Injection Remote Code Execution Vulnerability

Prompt injection into Flowise's CSV Agent makes the LLM emit Python that evades a regex blocklist and runs unsandboxed in pyodide, yielding RCE as the server user.

- Payout: Research
- Severity: critical
- Program: Flowise
- Found by: zdi-disclosures
- Disclosed: 2026-08-04
- CVE: CVE-2026-70477
- Write-up quality: 8/10
- Original: https://github.com/advisories/GHSA-5xvg-pmgg-3mxr
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-5xvg-pmgg-3mxr
- Tags: area:ai domain:ai impact:rce kind:research platform:ghsa sev:critical tech:llm tech:nodejs tech:python technique:source-review vuln:command-injection vuln:prompt-injection vuln:rce

**Target:** CSV Agent node in Flowise chatflow (pyodide Python execution)
**Root cause:** User input is substituted into an LLM prompt whose response is treated as Python code; the only guard is a static regex blocklist in validatePythonCodeForDataFrame, and pyodide is not sandboxed from the host OS.
**Steps:**
1. Send a query to a chatflow using the CSV Agent node (unauthenticated) or configure a chatflow pointing at an attacker-controlled LLM server.
2. Craft input so the LLM returns Python that reconstructs forbidden identifiers via string concatenation, `chr()` encoding, aliasing, `__getattribute__`, frame inspection, MRO traversal, `df.query()` or decorator syntax.
3. Payload passes `validatePythonCodeForDataFrame` and is prepended with `import pandas as pd\nimport numpy as np` then run via `pyodide.runPythonAsync(code)`.
4. Arbitrary Python executes with host OS access; e.g. `python3 poc.py --mode prompt_injection --cmd <CMD>`.
**How found:** Reviewed CSVAgent.ts and noticed the LLM response is assigned to pythonCode and only checked by a static regex blocklist before pyodide execution; then enumerated Python constructs (decorators, chr(), MRO, frame objects, df.query) that reconstruct blocked identifiers.
**Exploitation:** Unauthenticated attacker injects a prompt into a CSV Agent chatflow so the LLM emits a blocklist-evading Python payload; alternatively an authenticated attacker points the chatflow's model at their own server returning the payload directly, bypassing the LLM. The code runs in unsandboxed pyodide with full OS interface access, giving code execution as the service account.
**Insight:** Regex blocklists over LLM-generated code are trivially bypassed via identifier reconstruction (chr(), concatenation, __getattribute__, frame/MRO traversal) — when auditing AI-agent code exec, enumerate every path to __import__/exec rather than trusting the denylist, and check whether the interpreter (pyodide) is actually sandboxed from the host.

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

**poc.py command with server/chatflow/prompt_injection modes**

```
python3 poc.py --mode [server OR chatflow OR prompt_injection] [--user <USER> --passwd <PASSWORD> --host <HOST> --r_host <R_HOST> --r_port <R_PORT> --l_port <L_PORT> --port <PORT> --cmd <CMD> --attack <ATTACK> --chatflow_id <CHAT_ID>]
```

