Every CVE Search/ Get API key

CVE-2026-69264

Flowise: RCE via CSVAgent csvFile data URI base64 segment is interpolated into Python source without validation

9.4 CriticalFix available

Prior to 3.1.3, Flowise CSVAgent interpolates an attacker-controlled segment of the csvFile data URI directly into a Python source-code template that is then executed by Pyodide. Because Pyodide is loaded with the default js bridge to globalThis, which on Node.js exposes eval and dynamic import, the attacker can break out of the Python string literal, hand a JavaScript string to js.eval, dynamically import Node built-in modules such as fs and child_process, and execute arbitrary file I/O or OS commands as the Flowise process. The two validator paths around this code, validatePythonCodeForDataFrame and validateCustomReadCSVFunction, are never applied to the bootstrap template. A workspace user with chatflows:create or agentflows/chatflows update permission can plant a CSV Agent node with a crafted csvFile; once the chatflow is exposed via POST /api/v1/prediction/:id, any unauthenticated request triggers host remote code execution. This issue is fixed in version 3.1.3.

Attack profile

How easy it is to exploit

Attack vector
Network
Complexity
Low
Privileges needed
Low
User interaction
None

What an attacker can affect

Confidentiality
High
Integrity
High
Availability
High

Exploit likelihood

1.11%Up 0.5 points since Aug 5, 2026
90%50%10%1%

What each source reports

CVE List Score used 9.4

Flowise: RCE via CSVAgent csvFile data URI base64 segment is interpolated into Python source without validation

Prior to 3.1.3, Flowise CSVAgent interpolates an attacker-controlled segment of the csvFile data URI directly into a Python source-code template that is then executed by Pyodide. Because Pyodide is loaded with the default js bridge to globalThis, which on Node.js exposes eval and dynamic import, the attacker can break out of the Python string literal, hand a JavaScript string to js.eval, dynamically import Node built-in modules such as fs and child_process, and execute arbitrary file I/O or OS commands as the Flowise process. The two validator paths around this code, validatePythonCodeForDataFrame and validateCustomReadCSVFunction, are never applied to the bootstrap template. A workspace user with chatflows:create or agentflows/chatflows update permission can plant a CSV Agent node with a crafted csvFile; once the chatflow is exposed via POST /api/v1/prediction/:id, any unauthenticated request triggers host remote code execution. This issue is fixed in version 3.1.3.

Record
CVE-2026-69264
CVSS 4.0
9.4 CriticalCVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H
Weakness
CWE-94, CWE-95
Updated
2026-11-02
EUVDEUVD-2026-52801 9.4

Prior to 3.1.3, Flowise CSVAgent interpolates an attacker-controlled segment of the csvFile data URI directly into a Python source-code template that is then executed by Pyodide. Because Pyodide is loaded with the default js bridge to globalThis, which on Node.js exposes eval and dynamic import, the attacker can break out of the Python string literal, hand a JavaScript string to js.eval, dynamically import Node built-in modules such as fs and child_process, and execute arbitrary file I/O or OS commands as the Flowise process. The two validator paths around this code, validatePythonCodeForDataFrame and validateCustomReadCSVFunction, are never applied to the bootstrap template. A workspace user with chatflows:create or agentflows/chatflows update permission can plant a CSV Agent node with a crafted csvFile; once the chatflow is exposed via POST /api/v1/prediction/:id, any unauthenticated request triggers host remote code execution. This issue is fixed in version 3.1.3.

Record
EUVD-2026-52801
CVSS 4.0
9.4 CriticalCVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H
Updated
2026-11-02
GitHub AdvisoriesGHSA-4j8x-x6v7-w9rq 9.4

Flowise: RCE via CSVAgent csvFile data URI base64 segment is interpolated into Python source without validation

Summary

Flowise's CSVAgent interpolates an attacker-controlled segment of the csvFile data URI directly into a Python source-code template that is then executed by Pyodide. Because Pyodide is loaded with the default js bridge to globalThis (which on Node.js exposes eval and dynamic import()), the attacker can break out of the Python string literal, hand a JS string to js.eval, dynamically import any Node built-in module (fs, child_process, …), and execute arbitrary file I/O or OS commands as the Flowise process. The two validator paths around this code (validatePythonCodeForDataFrame and validateCustomReadCSVFunction) are never applied to the bootstrap template.

A workspace user with chatflows:create (or any agentflows/chatflows update permission) plants a CSV Agent node with a crafted csvFile. Once the chatflow is exposed via the (whitelisted, public) POST /api/v1/prediction/:id endpoint, any unauthenticated request triggers the host RCE.

Details

Vulnerable file: packages/components/nodes/agents/CSVAgent/CSVAgent.ts

The run() method extracts the file segment from the data URI by splitting on , and using two pop() calls (lines 127–138):

} else {
    if (csvFileBase64.startsWith('[') && csvFileBase64.endsWith(']')) {
        files = JSON.parse(csvFileBase64)
    } else {
        files = [csvFileBase64]
    }

    for (const file of files) {
        if (!file) continue
        const splitDataURI = file.split(',')
        splitDataURI.pop()                           // discards trailing filename segment
        base64String += splitDataURI.pop() ?? ''     // captures the segment we attack
    }
}

The captured base64String is then interpolated verbatim into a Python source string at lines 156–171:

const code = `import pandas as pd
import base64
from io import StringIO
import json

base64_string = "${base64String}"      // ← line 161: interpolation sink

decoded_data = base64.b64decode(base64_string)
csv_data = StringIO(decoded_data.decode('utf-8'))

df = pd.${customReadCSVFunc}
my_dict = df.dtypes.astype(str).to_dict()
print(my_dict)
json.dumps(my_dict)`
dataframeColDict = await pyodide.runPythonAsync(code)   // ← line 171: sink

Validator gaps:

  • validateCustomReadCSVFunction(customReadCSVFunc) runs on line 147, but this only validates the customReadCSV field, not base64String.
  • validatePythonCodeForDataFrame(pythonCode) runs on line 198, but only against the LLM-emitted Python that runs later — never against this bootstrap template.
  • No content check (^[A-Za-z0-9+/=]*$) is applied to base64String before interpolation.

Pyodide configuration (packages/components/nodes/agents/CSVAgent/core.ts, lines 7–16):

export async function LoadPyodide(): Promise<PyodideInterface> {
    if (pyodideInstance === undefined) {
        const { loadPyodide } = await import('pyodide')
        const obj: any = { packageCacheDir: path.join(getUserHome(), '.flowise', 'pyodideCacheDir') }
        pyodideInstance = await loadPyodide(obj)
        await pyodideInstance.loadPackage(['pandas', 'numpy'])
    }
    return pyodideInstance
}

Pyodide is loaded with default options. On Node.js, the default js module inside Pyodide bridges to globalThis, exposing the JS eval function and top-level dynamic import(). From injected Python, the attacker runs:

import js
await js.eval(
    "(async () => {"
    "  const fs = await import('fs');"
    "  fs.writeFileSync('proof.txt', 'pwned');"
    "})()"
)

…which executes in the host Node.js process, not inside Pyodide's WASM sandbox. Substituting await import('child_process') for await import('fs') yields arbitrary OS-command execution via cp.execSync(...) with the same primitive.

Node-version note. The original PoC for this issue used js.process.mainModule.require("child_process"), which is a one-liner but only works on Node…

Record
GHSA-4j8x-x6v7-w9rq
CVSS 4.0
9.4 CriticalCVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H
Weakness
CWE-94, CWE-95
Updated
2026-11-02
NVD 9.4

Prior to 3.1.3, Flowise CSVAgent interpolates an attacker-controlled segment of the csvFile data URI directly into a Python source-code template that is then executed by Pyodide. Because Pyodide is loaded with the default js bridge to globalThis, which on Node.js exposes eval and dynamic import, the attacker can break out of the Python string literal, hand a JavaScript string to js.eval, dynamically import Node built-in modules such as fs and child_process, and execute arbitrary file I/O or OS commands as the Flowise process. The two validator paths around this code, validatePythonCodeForDataFrame and validateCustomReadCSVFunction, are never applied to the bootstrap template. A workspace user with chatflows:create or agentflows/chatflows update permission can plant a CSV Agent node with a crafted csvFile; once the chatflow is exposed via POST /api/v1/prediction/:id, any unauthenticated request triggers host remote code execution. This issue is fixed in version 3.1.3.

Record
CVE-2026-69264
CVSS 4.0
9.4 CriticalCVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
CVSS 3.1
9.8 CriticalCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Weakness
CWE-94, CWE-95
Updated
2026-11-02
OSV 9.4

Flowise: RCE via CSVAgent csvFile data URI base64 segment is interpolated into Python source without validation

Prior to 3.1.3, Flowise CSVAgent interpolates an attacker-controlled segment of the csvFile data URI directly into a Python source-code template that is then executed by Pyodide. Because Pyodide is loaded with the default js bridge to globalThis, which on Node.js exposes eval and dynamic import, the attacker can break out of the Python string literal, hand a JavaScript string to js.eval, dynamically import Node built-in modules such as fs and child_process, and execute arbitrary file I/O or OS commands as the Flowise process. The two validator paths around this code, validatePythonCodeForDataFrame and validateCustomReadCSVFunction, are never applied to the bootstrap template. A workspace user with chatflows:create or agentflows/chatflows update permission can plant a CSV Agent node with a crafted csvFile; once the chatflow is exposed via POST /api/v1/prediction/:id, any unauthenticated request triggers host remote code execution. This issue is fixed in version 3.1.3.

Record
CVE-2026-69264
CVSS 4.0
9.4 CriticalCVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H
Weakness
CWE-94, CWE-95
Updated
2026-11-02
OSVGHSA-4j8x-x6v7-w9rq 9.4

Flowise: RCE via CSVAgent csvFile data URI base64 segment is interpolated into Python source without validation

Summary

Flowise's CSVAgent interpolates an attacker-controlled segment of the csvFile data URI directly into a Python source-code template that is then executed by Pyodide. Because Pyodide is loaded with the default js bridge to globalThis (which on Node.js exposes eval and dynamic import()), the attacker can break out of the Python string literal, hand a JS string to js.eval, dynamically import any Node built-in module (fs, child_process, …), and execute arbitrary file I/O or OS commands as the Flowise process. The two validator paths around this code (validatePythonCodeForDataFrame and validateCustomReadCSVFunction) are never applied to the bootstrap template.

A workspace user with chatflows:create (or any agentflows/chatflows update permission) plants a CSV Agent node with a crafted csvFile. Once the chatflow is exposed via the (whitelisted, public) POST /api/v1/prediction/:id endpoint, any unauthenticated request triggers the host RCE.

Details

Vulnerable file: packages/components/nodes/agents/CSVAgent/CSVAgent.ts

The run() method extracts the file segment from the data URI by splitting on , and using two pop() calls (lines 127–138):

} else {
    if (csvFileBase64.startsWith('[') && csvFileBase64.endsWith(']')) {
        files = JSON.parse(csvFileBase64)
    } else {
        files = [csvFileBase64]
    }

    for (const file of files) {
        if (!file) continue
        const splitDataURI = file.split(',')
        splitDataURI.pop()                           // discards trailing filename segment
        base64String += splitDataURI.pop() ?? ''     // captures the segment we attack
    }
}

The captured base64String is then interpolated verbatim into a Python source string at lines 156–171:

const code = `import pandas as pd
import base64
from io import StringIO
import json

base64_string = "${base64String}"      // ← line 161: interpolation sink

decoded_data = base64.b64decode(base64_string)
csv_data = StringIO(decoded_data.decode('utf-8'))

df = pd.${customReadCSVFunc}
my_dict = df.dtypes.astype(str).to_dict()
print(my_dict)
json.dumps(my_dict)`
dataframeColDict = await pyodide.runPythonAsync(code)   // ← line 171: sink

Validator gaps:

  • validateCustomReadCSVFunction(customReadCSVFunc) runs on line 147, but this only validates the customReadCSV field, not base64String.
  • validatePythonCodeForDataFrame(pythonCode) runs on line 198, but only against the LLM-emitted Python that runs later — never against this bootstrap template.
  • No content check (^[A-Za-z0-9+/=]*$) is applied to base64String before interpolation.

Pyodide configuration (packages/components/nodes/agents/CSVAgent/core.ts, lines 7–16):

export async function LoadPyodide(): Promise<PyodideInterface> {
    if (pyodideInstance === undefined) {
        const { loadPyodide } = await import('pyodide')
        const obj: any = { packageCacheDir: path.join(getUserHome(), '.flowise', 'pyodideCacheDir') }
        pyodideInstance = await loadPyodide(obj)
        await pyodideInstance.loadPackage(['pandas', 'numpy'])
    }
    return pyodideInstance
}

Pyodide is loaded with default options. On Node.js, the default js module inside Pyodide bridges to globalThis, exposing the JS eval function and top-level dynamic import(). From injected Python, the attacker runs:

import js
await js.eval(
    "(async () => {"
    "  const fs = await import('fs');"
    "  fs.writeFileSync('proof.txt', 'pwned');"
    "})()"
)

…which executes in the host Node.js process, not inside Pyodide's WASM sandbox. Substituting await import('child_process') for await import('fs') yields arbitrary OS-command execution via cp.execSync(...) with the same primitive.

Node-version note. The original PoC for this issue used js.process.mainModule.require("child_process"), which is a one-liner but only works on Node…

Record
GHSA-4j8x-x6v7-w9rq
CVSS 4.0
9.4 CriticalCVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H
Weakness
CWE-94, CWE-95
Updated
2026-11-02

Affected products

Affected packages

Affected packages
PackageAffectedFixed
flowise
npm
through 3.1.2
3.1.3
flowise-components
npm
through 3.1.2
3.1.3
flowise
npm
before 3.1.3
3.1.3
flowise-components
npm
before 3.1.3
3.1.3

History

History
RecordedChangedSources
first indexedCVE List, EUVD, GitHub Advisories, NVD, OSV

Dates are when Every CVE recorded each change. Upstream dates may differ.

In short

CVE-2026-69264 is a critical vulnerability (CVSS 9.4) in Flowiseai Flowise, published August 4, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 1.11%, the 66th percentile. Fixed in flowise 3.1.3.

References

This page doesn't list everything

Left out here: the rest of 2 source descriptions. The JSON record has them.

MoveEnter Open