groundy
security

Flowise's CVE-2026-41264: LLM-Written `import` Becomes Unauthenticated RCE

CVE-2026-41264 (CVSS 9.8) shows how Flowise's CSV Agent regex allowlist fails when the LLM writes the code: aliasing os as pandas bypasses the filter for unauthenticated RCE.

8 min···6 sources ↓

Flowise’s CSV Agent evaluated LLM-generated Python through a regex import filter intended to confine execution to pandas and numpy. The flaw: the filter assumed a human attacker typing an import statement, but the LLM generates the code; a prompt-injected query tricking the model into aliasing os as pandas bypassed the gate entirely, yielding unauthenticated remote code execution.

The Vulnerability: How a Regex Import Filter Became RCE

FlowiseAI disclosed CVE-2026-41264 in mid-April 2026, affecting flowise and flowise-components through version 3.0.131. NVD scores it 9.8 (CVSS v3.1); GitHub’s own advisory scores the same flaw 9.2 under CVSS v4.0, where the prompt-injection precondition shows up as elevated Attack Complexity plus an Attack Requirements metric (AT:P) that v3.1 has no slot for [Updated June 2026]. Both rate it Critical, and NVD now classifies the root cause as CWE-184, Incomplete List of Disallowed Inputs, the textbook label for an allowlist that enumerates bad patterns instead of constraining good ones. At its core is the CSV Agent’s run() method, which takes a user’s natural-language query about a CSV file, passes it to an LLM, and executes the resulting Python code server-side in a Pyodide environment2.

The defense was a regex (/\bimport\s+(?!pandas|numpy\b)/g) designed to block any import except pandas and numpy. The advisory describes the execution context as “non-sandboxed”2, meaning a successful bypass gives the attacker full OS access, not merely a WASM escape.

Two vectors exist. The first is unauthenticated: an attacker sends a crafted prompt to /api/v1/prediction/<chat_id> and tricks the LLM into emitting malicious Python2. The second requires authentication: an attacker configures a custom LLM server that returns attacker-controlled Python directly2. In both cases, the code reaches the same evaluator.

The Bypass: import pandas as np, os as pandas

The regex blocked imports that did not match pandas or numpy. It did not block aliasing. An attacker could craft a prompt that caused the LLM to generate:

import pandas as np, os as pandas
pandas.system("...")

Here os is imported under the name pandas, which satisfies the negative lookahead. The code then calls pandas.system(...), i.e. os.system(...), giving shell access. The vulnerability was discovered by Dre Cura and Nicholas Zubrisky of TrendAI Research and disclosed through Trend Micro’s Zero Day Initiative as ZDI-CAN-294112.

Why Pyodide Is Not the Sandbox Here

Pyodide runs Python in WebAssembly, which provides process-level isolation from the host OS. But isolation is not the same as sanitization. The Flowise advisory explicitly calls the execution environment “non-sandboxed”2, and for good reason: the CSV Agent runs the generated Python through eval() or equivalent execution paths inside a Node.js process that has already loaded the Pyodide runtime with host-level permissions. WASM does not help when the Python code being evaluated can call os.system() against a filesystem the host can see.

The contrast with Pyodide’s intended deployment is instructive. When Pyodide runs inside the browser, the WASM sandbox really is the boundary: there is no host shell to reach, os.system resolves into an emulated environment with nothing behind it, and the worst an attacker gets is control of their own tab. Server-side Pyodide inverts that guarantee. The runtime now sits inside a Node process with full ambient authority, and the emulated filesystem and network shims map onto resources the host can actually see. The same library that is a containment boundary on the client becomes a thin abstraction over the host on the server. Flowise read “runs in WASM” as “is sandboxed” and shipped the inverted case.

The Broader Pattern: When the LLM Is the Attacker’s Typist

The critical assumption behind the original regex was that the attacker writes the import line. In a traditional code-injection vulnerability (SQLi, command injection, XSS) that assumption holds: the attacker supplies a string that the application executes. Here, the attacker supplies a prompt, and the LLM translates that prompt into a program. The import statement is syntactically valid, semantically reasonable, and produced by a model trained to be helpful. The regex never had a chance because it was guarding against the wrong threat model.

This shifts the vulnerable surface from “can the attacker inject code?” to “can the attacker inject a prompt that produces code the sanitizer cannot recognize?” Every framework that lets an LLM emit executable Python against a lexical filter (Langflow, Dify, n8n AI nodes, and others) faces the same structural risk. There is no evidence yet that these frameworks share the exact same bypass, but the architectural analogy is direct: if your sanitizer assumes it is reading attacker-written text, it will miss LLM-rewritten text. The same family of mistake shows up across the agent ecosystem: InstructLab’s CVE-2026-6859 hardcoded trust_remote_code=True, and Microsoft Semantic Kernel patched two RCE paths that ran eval() on model-influenced strings. The common thread is a component that treats LLM output as trusted data.

The Same Bug, One File Over: CVE-2026-41265

The strongest evidence that this is a structural defect rather than a one-off oversight is that Flowise shipped it twice. On April 23, 2026, the same researchers and the same ZDI process disclosed CVE-2026-41265 against the Airtable Agent’s run() method, scored 9.2 and fixed in the same 3.1.0 release [Updated June 2026]. The Airtable agent fetches records, builds a system prompt asking an LLM for pandas-based Python, and evaluates the result in Pyodide behind the same FORBIDDEN_PATTERNS regex. The proof-of-concept payload is byte-for-byte the one that breaks the CSV agent: import pandas as np, os as pandas. Two nodes, written to the same template, inherited the same flawed assumption about who writes the import line. Anyone auditing a node-based agent builder should assume that wherever one LLM-to-code node exists, others were copy-pasted from it.

Flowise has a longer RCE history that frames why these landed. The framework reached roughly 12,000 internet-facing instances by April 2026, and a separate CustomMCP-node flaw, CVE-2025-59528 (CVSS 10.0), drew confirmed in-the-wild exploitation from a Starlink IP after sitting public for more than six months. Earlier 2025 issues (CVE-2025-8943, an OS command path, and CVE-2025-26319, arbitrary file upload) round out a pattern of a low-code builder exposing privileged execution to the network. CVE-2026-41264 itself is not in CISA’s KEV catalog and carries a modest EPSS of 0.529% (41st percentile), so it is not the one being mass-scanned today. The risk is qualitative, not yet statistical: an unauthenticated request that turns a chatbot into os.system, on a class of software operators routinely expose without an auth layer in front of it.

What Other Frameworks Should Audit Now

Frameworks with LLM-to-code pipelines should audit three specific things:

  1. Who generates the executable text? If the LLM writes it, a regex that blocks human-authored attack strings is insufficient.
  2. Is the execution environment actually sandboxed? Running inside Pyodide, WASM, or a Docker container is not the same as denying filesystem, network, and subprocess access. Check what the Python code can reach, not just what the runtime is compiled to.
  3. Are allowlists enforced at the AST or namespace level? The Flowise fix moved from a regex to blocking all imports with /\bimport\b/g because pandas and numpy are pre-imported by the executor3. Namespace-level enforcement, which exposes only specific modules in the execution globals, is stronger than text-level enforcement.

Patch Timeline and Mitigation

Flowise merged the fix on March 5, 2026, in PR #58793. The change replaced the permissive regex with /\bimport\b/g, which blocks all import statements since pandas and numpy are already available in the execution context. The same PR added 61 unit tests covering import bypasses3. The advisory was published in mid-April 2026, and the ZDI disclosure (ZDI-CAN-29411) followed shortly after12.

Users on flowise-components ≤3.0.13 should upgrade to 3.1.0 or later. If patching is not immediately possible, restricting network access to the prediction endpoint and disabling the CSV Agent entirely are the only reliable mitigations. The authenticated vector (custom LLM servers) means that even prompt-hardening cannot fully eliminate risk if untrusted model endpoints are permitted.

The deny-all import block is a stopgap, and the right fix is to stop trusting the text at all. Two layers do that. The first is parsing instead of pattern-matching: run the generated code through ast.parse() and walk the tree, rejecting any Import, ImportFrom, Call to __import__, attribute access on dunder names, or exec/eval node before anything executes. An AST visitor sees os aliased to pandas as an alias(name='os', asname='pandas') node regardless of how the import statement is spelled, which is exactly the information a regex throws away. RestrictedPython and similar libraries automate this by compiling to a restricted bytecode that never exposes the dangerous builtins in the first place. The second layer assumes the first will eventually fail and contains the blast radius at the OS: run the executor in a real sandbox (gVisor, a seccomp-bpf profile, an unprivileged container with no network namespace and a read-only root) so that a successful os.system call lands in a box with nothing worth reaching. Defense in depth here is not optional, because the threat model is open-ended in a way SQL injection never was: the attacker is steering a code generator, and prompt-hardening only raises the cost of producing a payload the filter misses. Benchmarks of indirect injection have repeatedly underestimated how reliably models can be steered, so treating the prompt boundary as a security control rather than a nuisance filter is the mistake to avoid.

Frequently Asked Questions

Is CVE-2026-41264 listed in NVD yet?

Yes. It was RESERVED at first disclosure but is now published in the National Vulnerability Database, with NVD assigning CVSS v3.1 9.8 and GitHub assigning CVSS v4.0 9.2 [Updated June 2026]. The two scores diverge because v4.0 models the prompt-injection precondition as higher Attack Complexity and adds an Attack Requirements axis; both still land in Critical. Scanners that pull from NVD, GitHub Advisory Database, or ZDI will now flag affected Flowise installations.

Was the regex allowlist the first attempt to sanitize CSV Agent code?

No. PR #5701 introduced the original regex-based sanitizer for Pyodide execution, using the negative lookahead pattern. PR #58793 later replaced it with a blanket import block, marking a shift from permissive allowlisting to deny-all, an admission that the allowlist approach was structurally unworkable once aliasing bypasses appeared.

Could the blanket import block in 3.1.0 still be bypassed?

The import keyword is only one way to load modules in Python. __import__(), importlib.import_module(), and exec() on attacker-supplied strings can all reach the OS layer without writing an import statement. If these builtins remain in the Pyodide execution globals, the same prompt-injection vector could bypass the new regex with no import statement at all.

Does the blanket import block limit future Flowise components?

The fix works because the CSV Agent’s Pyodide executor pre-imports pandas and numpy. Any future component that needs additional libraries in its execution context would require either expanding the pre-imported set, increasing the attack surface, or moving to namespace-level globals filtering, which is architecturally different from text-level regex blocking.

sources · 6 cited

  1. NVD: CVE-2026-41264 Detailnvd.nist.govprimaryaccessed 2026-06-26