# CVE-2026-41264: How a Regex Let Prompts Run Code in Flowise

By Charles Givre · 2026-10-02

> CVE-2026-41264 turned a prompt to Flowise's CSV Agent into code execution on the server. What broke, why the 3.1.0 fix was not enough, and how to hunt for it.

```python
import pandas as np, os as pandas
pandas.system("xcalc")
```

Those two lines are the whole bypass in [CVE-2026-41264](/cve/CVE-2026-41264), a CVSS 9.8 bug in [Flowise](https://github.com/FlowiseAI/Flowise), the open source drag-and-drop builder for LLM apps. Flowise's CSV Agent asked a model to write pandas code, checked the code with a regex, and ran it on the server. The regex was meant to stop any import other than pandas or numpy. It looked at the first module name after `import` and stopped there. Alias `os` as `pandas` and the check passes.

The bug is worth studying because of what happened after it was reported. The fix in 3.1.0 was a stricter regex. Over the next three months the validator grew to 390 lines, picked up five more CVEs, and in June Flowise deleted the feature. If you build or test anything that runs model-generated code, that history is the lesson.

## The data path

Trend Micro's Zero Day Initiative reported the bug as ZDI-CAN-29411. The researchers credited are Dre Cura and Nicholas Zubrisky of TrendAI Research. Their analysis is published in full in the [Flowise advisory GHSA-3hjv-c53m-58jj](https://github.com/FlowiseAI/Flowise/security/advisories/GHSA-3hjv-c53m-58jj). The flow inside `CSV_Agents.run()`, as the advisory quotes it:

1. Load the uploaded CSV in [Pyodide](https://pyodide.org/) and extract column names and dtypes with pandas.
2. Fill a prompt template with those dtypes and the user's question. The template ends with a "Security:" paragraph telling the model not to use `import`, `exec`, `eval`, `open`, `os` or `subprocess`.
3. Take the model's reply, strip Markdown fences, and store it as `pythonCode`.
4. Run `validatePythonCodeForDataFrame(pythonCode)`, a loop over 34 forbidden regexes.
5. Execute `import pandas as pd\n` plus `pythonCode` with `pyodide.runPythonAsync()`.

Step 2 is a request to the model, not a control. Step 4 is the only real check, and step 5 is where it fails. An unauthenticated attacker reaches all of it because `/api/v1/prediction/` sits on Flowise's `WHITELIST_URLS`. The ZDI proof of concept puts a prompt injection in the `question` field asking the model to reply with the payload above. The advisory says it was tested against Llama 3.2 in Ollama and can take several attempts, which is normal for anything that depends on model output.

There is also a path with no model at all. A logged-in user creates a chatflow whose ChatOllama node points at a server they control, and that server answers `/api/chat` with Python. The regex is the only thing between that reply and the interpreter.

The version range hides one detail. Steps 2 and 4 came from PR #5701, merged on 2026-02-06, three days after 3.0.13 shipped. The 3.0.13 release has no validator and no "Security:" paragraph: model output went straight to `runPythonAsync()`. ZDI's bypass beat the code headed for the next release. Anyone on 3.0.13 or earlier was exposed without the alias trick. The same node had a second bug, [CVE-2026-41137](/cve/CVE-2026-41137) (CVSS 8.8): a custom read call set by the chatflow builder was pasted into Python as `pd.${customReadCSVFunc}` with no checks.

## Why Pyodide did not contain it

Pyodide is CPython compiled to WebAssembly, and it is easy to assume that means isolation. Inside Node it does not. The [Pyodide Python API docs](https://pyodide.org/en/stable/usage/api/python-api.html) describe the `js` module as "the global JavaScript scope." In a Node process that scope leads to `child_process`. [CVE-2026-69255](/cve/CVE-2026-69255) describes exactly that chain: use Pyodide's js bridge to load `child_process`, then run commands as root in the Flowise container. WebAssembly limits memory access. It does not limit what the host hands the guest.

## The fix that kept failing

Version 3.1.0, released 2026-03-16, was the first release with any validator. It shipped with a stricter import rule from PR #5879 (any use of the word `import` fails), four more reflection patterns, and the custom read field run through the same check. That closes the alias trick and CVE-2026-41137. The CVE records in our [AI CVE index](/cve/) show what came next, all fixed in 3.1.3:

| CVE | CVSS | Bypass |
|---|---|---|
| [CVE-2026-69256](/cve/CVE-2026-69256) | 8.8 | `pd.read_pickle()` in the custom read field deserializes a payload without matching any forbidden word |
| [CVE-2026-69255](/cve/CVE-2026-69255) | 8.8 | CSV contents interpolated into `base64_string = "..."`, so a quote in the data breaks out |
| [CVE-2026-70470](/cve/CVE-2026-70470) | 9.8 | Unicode homoglyphs in dunder names |
| [CVE-2026-70477](/cve/CVE-2026-70477) | 9.8 | Prompt injection that gets model-written code past the denylist, the same path as CVE-2026-41264 |
| [CVE-2026-73487](/cve/CVE-2026-73487) | 9.8 | `pd.read_json()` left open, giving data exfiltration and SSRF |

The homoglyph bug is the one to remember. JavaScript's `\b` only knows ASCII word characters. Python normalizes identifiers with NFKC at parse time, as [PEP 3131](https://peps.python.org/pep-3131/) specifies. Spell `__class__` with MATHEMATICAL BOLD SMALL A (U+1D41A) in place of the `a` and the two languages disagree about what the string says:

```javascript
/\b__class__\b/.test("__cl\u{1D41A}ss__")   // false: validator passes it
```

```python
import unicodedata
unicodedata.normalize("NFKC", "__cl\U0001D41Ass__")   # '__class__'
exec("x = ().__cl\U0001D41Ass__")                     # x is <class 'tuple'>
```

The commit history reads the same way. Flowise blocked `read_pickle` and class definitions on 2026-04-27 (PR #6257), restricted the CSV field to a literal `read_csv()` call on 2026-05-05 (#6313), and added homoglyph and backslash line-continuation normalization on 2026-06-05 (#6476). On 2026-06-10, PR #6499 removed the CSV Agent, the Airtable Agent, the 390-line validator, and its 599-line test file. That shipped in 3.1.3 on 2026-06-25.

So NVD's "fixed in 3.1.0" is accurate for this CVE and misleading for your exposure. If a CSV Agent ran on 3.1.0 through 3.1.2, it was still exploitable.

## If you run Flowise

Check the version first. `npm ls -g flowise --depth=0` covers npm installs. For containers, `docker ps --format '{{.Image}}' | grep -i flowise` shows the tag. Anything below 3.1.3 with a CSV or Airtable Agent in use is exposed.

Then find the flows. The default SQLite store lives at `~/.flowise/database.sqlite`, and the node names are `csvAgent` and `airtableAgent`:

```bash
sqlite3 ~/.flowise/database.sqlite \
  "SELECT id, name, isPublic, apikeyid IS NULL AS no_key
   FROM chat_flow
   WHERE flowData LIKE '%csvAgent%' OR flowData LIKE '%airtableAgent%';"
```

Rows with `no_key = 1` were reachable without credentials. Those flows break on upgrade, so warn their owners first.

To hunt, pull user messages sent to those chatflows and look for code, pandas I/O calls, and characters outside printable ASCII:

```bash
sqlite3 ~/.flowise/database.sqlite \
  "SELECT createdDate, chatflowid, substr(content, 1, 200)
   FROM chat_message
   WHERE role = 'userMessage'
     AND (content LIKE '%import%' OR content LIKE '%read_pickle%'
          OR content LIKE '%read_json%' OR content LIKE '%\_\_%' ESCAPE '\'
          OR content GLOB '*[^ -~]*');"
```

The last clause also matches ordinary newlines and non-English text, so expect noise. The authenticated path leaves no injection in `chat_message`. For that, review chat model nodes whose base URL points somewhere you do not recognize.

Process telemetry catches both paths. Exploitation shows up as [T1190](/mitre/T1190) followed by [T1059.004](/mitre/T1059.004): a `node` process spawning a shell. A [Falco](https://falco.org/docs/concepts/rules/basic-elements/) rule:

```yaml
- rule: Shell or network tool spawned by Flowise
  desc: Node process in a Flowise container started a shell or downloader
  condition: >
    spawned_process and proc.pname = node
    and container.image.repository contains "flowise"
    and proc.name in (sh, bash, dash, curl, wget, nc, python3)
  output: "Flowise child process (cmd=%proc.cmdline parent=%proc.pcmdline image=%container.image.repository)"
  priority: CRITICAL
```

On the ATLAS side, the injection is [AML.T0051](/atlas/AML.T0051) and the code execution is [AML.T0050](/atlas/AML.T0050), Command and Scripting Interpreter.

## What the CSV Agent should have been

[OWASP LLM05:2025 Improper Output Handling](https://genai.owasp.org/llmrisk/llm052025-improper-output-handling/) covers this class of bug: model output passed to an interpreter without being treated as untrusted. CVE-2026-41264 is a textbook case. The fix is the same as for any untrusted code. Run it in a separate process with no network, no credentials, a read-only copy of the data, and a seccomp or gVisor boundary. A denylist on Python source is a losing game. Python has too many ways to name the same object.

Isolation has limits too. A sandboxed interpreter can still return every row of the dataframe to whoever asked, and CVE-2026-73487's `read_json` SSRF is a reminder that egress needs blocking as well. Sometimes the right answer is the one Flowise reached: remove the feature.

None of the bypasses needed machine learning knowledge. An alias, a pickle, a Unicode quirk and an unescaped quote are standard application security findings. That is why the [AI Red-Teaming](/courses/ai-red-teaming) course asks for security testing experience and not a data science background. When the LLM's output feeds an interpreter, testing it is the same as testing any other input validator.

## FAQ

### Which Flowise versions are affected by CVE-2026-41264?

The GitHub advisory GHSA-3hjv-c53m-58jj lists flowise and flowise-components 3.0.13 and earlier, patched in 3.1.0. That range is correct for this specific bypass, but CSV Agent instances on 3.1.0 through 3.1.2 were still exploitable through later bypasses (CVE-2026-70470, CVE-2026-70477, CVE-2026-73487 and others). Flowise removed the CSV Agent and Airtable Agent nodes in 3.1.3, released 2026-06-25. Treat 3.1.3 as the minimum safe version.

### Does exploiting CVE-2026-41264 require authentication?

Not for the prompt injection path. Flowise lists /api/v1/prediction/ in its WHITELIST_URLS, so anyone who can reach a chatflow's prediction endpoint can send it a question unless the chatflow has an API key assigned. A second path in the advisory needs an authenticated user who can build a chatflow pointing at an attacker-run model server, which skips the LLM entirely.

### Would a prompt injection filter or moderation node have stopped this?

No. The CSV Agent supported optional input moderation nodes, and the advisory's code annotations note they may not catch every injection. The authenticated path does not involve a real LLM at all: the attacker's fake model server returns the Python directly. The flaw is that model output reached an interpreter with access to the host. Filtering the input does not change that.


---

Canonical: https://gtkcyber.com/blog/cve-2026-41264-flowise-csv-agent-rce/