CVE-2026-69264
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.
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
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 Critical
CVSS: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 Critical
CVSS: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 thecustomReadCSVfield, notbase64String.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 tobase64Stringbefore 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 Critical
CVSS: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 Critical
CVSS: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 Critical
CVSS: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 Critical
CVSS: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 thecustomReadCSVfield, notbase64String.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 tobase64Stringbefore 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 Critical
CVSS: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
| Package | Affected | Fixed |
|---|---|---|
flowisenpm |
through 3.1.2 | 3.1.3 |
flowise-componentsnpm |
through 3.1.2 | 3.1.3 |
flowisenpm |
before 3.1.3 | 3.1.3 |
flowise-componentsnpm |
before 3.1.3 | 3.1.3 |
History
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | CVE 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
github.com/FlowiseAI/Flowise/security/advisories/GHSA-4j8x-x6v7-w9rq
github.com/FlowiseAI/Flowise/pull/6499
github.com/FlowiseAI/Flowise/commit/f4e2794f6a576b94578f2fdafbf49c2fb304626c
github.com/FlowiseAI/Flowise/releases/tag/[email protected]
github.com/advisories/GHSA-4j8x-x6v7-w9rq
github.com/CVEProject/cvelistV5/tree/main/cves/2026/69xxx/CVE-2026-69264.json
nvd.nist.gov/vuln/detail/CVE-2026-69264
github.com/FlowiseAI/Flowise
Left out here: the rest of 2 source descriptions. The JSON record has them.