Flowise: RCE via CSVAgent csvFile data URI base64 segment is interpolated into Python source without validation
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.
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, butthis only validates the customReadCSV field, not base64String.
validatePythonCodeForDataFrame(pythonCode) runs on line 198, but onlyagainst the *LLM-emitted* Python that runs later — never against this
bootstrap template.
^[A-Za-z0-9+/=]*$) is applied to base64String beforeinterpolation.
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 process.mainModule was deprecated and now
> returns undefined on Node 14+. The js.eval + dynamic-import() form
> above works on any Node 13.2+ in both CommonJS and ESM contexts, and was
> confirmed end-to-end against a stock [email protected] running on Node
> 20.20.2 — see [Verified end-to-end against live Flowise](#verified-end-to-end-against-live-flowise)
> below.
Trigger path (post-plant): the route POST /api/v1/prediction/:id is in
WHITELIST_URLS (packages/server/src/utils/constants.ts:12); when the
chatflow has no apikeyid set, it is reachable unauthenticated. A prediction
request runs the chatflow, instantiates CSVAgent, and executes the malicious
bootstrap.
Verified end-to-end on the cloned repo (commit
a3ffe6611b0986d646b9cd8bb8787d4fdcf9be6d, the same commit the prior audit
was based on).
Two files. Save the first
This issue can be reached over the network, attack complexity is low, an attacker needs low-level privileges on the target. No user interaction is required. Rated impact: confidentiality high, integrity high, availability high.
The score comes from this 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
CVE-2026-69264 is classified as CWE-94: Code Injection. Input is incorporated into code that the runtime evaluates, so an attacker can have their own code executed.
CVE-2026-69264 is recorded against 3 packages.
Published on 4 August 2026. No public exploit is currently recorded for this entry. Record sourced from NVD.
github.com (Web)
github.com (Web)
github.com (Web)
github.com (Package)
github.com (Web)
flowise has other advisories on record. If you are patching this one, these are worth checking on the same host:
These advisories are the same class of weakness (CWE-94: Code Injection) in other software:
Details
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
Affected Packages
| Software | From version | Fixed in |
|---|---|---|
| flowise | — | — |
| flowise-components | — | — |
| unknown | — | — |
References
Similar Threats
Exploit Protection
CVE-2026-69264 carries CVSS 9.5 Critical rating. BotEraser checks your installation against this and other known CVE records, and blocks IPs associated with exploit activity.
Check My Site For CVE-2026-69264 →No credit card required · Results in minutes
ⓘ Data Notice: The information presented above has been compiled from publicly available internet sources. Boteraser aggregates this data solely for informational purposes and does not independently classify, evaluate, or endorse any findings about the vulnerabilities listed. The accuracy and completeness of this information is the sole responsibility of the original publishers. Boteraser and its operators accept no liability for any decisions made based on this data.
Stay up to date with the latest from Boteraser.
We use cookies to improve your experience on our site. By using our site, you consent to cookies.
Manage your cookie preferences below:
Essential cookies enable basic functions and are necessary for the proper function of the website.
CloudFlare provides web performance and security solutions, enhancing site speed and protecting against threats.
Service URL: developers.cloudflare.com (opens in a new window)
These cookies are needed for adding comments on this website.
These cookies are used for managing login functionality on this website.
Statistics cookies collect information anonymously. This information helps us understand how visitors use our website.
Google Analytics is a powerful tool that tracks and analyzes website traffic for informed marketing decisions.
Service URL: policies.google.com (opens in a new window)
You can find more information in our Cookie Policy and Privacy Policy.