Ce mail provient de l'extérieur, restons vigilants

=====================================================================

                            CERT-Renater

                Note d'Information No. 2026/VULN410
_____________________________________________________________________

DATE                : 22/04/2026

HARDWARE PLATFORM(S): /

OPERATING SYSTEM(S): Systems running flowise (npm) versions prior to
                                          3.1.0.

=====================================================================
https://github.com/advisories/GHSA-3hjv-c53m-58jj
https://github.com/advisories/GHSA-v38x-c887-992f
https://github.com/FlowiseAI/Flowise/security/advisories/GHSA-x5w6-38gp-mrqh
https://github.com/FlowiseAI/Flowise/security/advisories/GHSA-5fw2-mwhh-9947
https://github.com/FlowiseAI/Flowise/security/advisories/GHSA-w47f-j8rh-wx87
https://github.com/FlowiseAI/Flowise/security/advisories/GHSA-3prp-9gf7-4rxx
https://github.com/FlowiseAI/Flowise/security/advisories/GHSA-6f7g-v4pp-r667
_____________________________________________________________________

Flowise: CSV Agent Prompt Injection Remote Code Execution Vulnerability
Critical severity GitHub Reviewed Published Apr 15, 2026 in
FlowiseAI/Flowise • Updated Apr 21, 2026

Vulnerability details

Package
flowise (npm)

Affected versions
<= 3.0.13

Patched versions
3.1.0

flowise-components (npm)
Affected versions
<= 3.0.13
Patched versions
3.1.0


Description

Abstract

Trend Micro's Zero Day Initiative has identified a vulnerability
affecting FlowiseAI Flowise.


Vulnerability Details

    Version tested: 3.0.13
    Installer file: https://github.com/FlowiseAI/Flowise
    Platform tested: Ubuntu 25.10

Analysis

This vulnerability allows remote attackers to execute arbitrary
code on affected installations of FlowiseAI Flowise. Authentication
is not required to exploit this vulnerability.

The specific flaw exists within the run method of the CSV_Agents
class. The issue results from the lack of proper sandboxing when
evaluating an LLM-generated Python script. An attacker can leverage
this vulnerability to execute code in the context of the user
running the server.


Product Information

FlowiseAI Flowise version 3.0.13 — https://github.com/FlowiseAI/Flowise


Setup Instructions

npm install -g flowise@3.0.13
npx flowise start

Root Cause Analysis

FlowiseAI Flowise is an open source low-code tool for developers to
build customized large language model (LLM) applications and AI agents.
It supports integration with various LLMs, data sources, and tools in
order to facilitate rapid development and deployment of AI solutions.
Flowise offers a web interface with a drag-and-drop editor, as well
as an API, through an Express web server accessible over HTTP on port
3000/TCP.

One such feature of Flowise is the ability to create chatflows.
Chatflows use a drag-and-drop editor that allows a developer to
place nodes which control how an interaction with an LLM will
occur. One such node is the CSV Agent node that represents an
Agent used to answer queries on a provided CSV file.

When a user makes a query against a chatflow using the CSV Agent
node, the run method of the CSV_Agents class is called. This method
first reads the contents of the CSV file passed to the node and
converts it to a base64 string. It then sets up a pyodide
environment and creates a Python script to be executed in this
environment. This Python script uses pandas to extract the column
names and their types from the provided CSV file. The method then
creates a system prompt for an LLM using this data as follows:

You are working with a pandas dataframe in Python. The name of
the dataframe is df.

The columns and data types of a dataframe are given below as a
Python dictionary with keys showing column names and values
showing the data types.


{dict}

I will ask question, and you will output the Python code using
pandas dataframe to answer my question. Do not provide any
explanations. Do not respond with anything except the output
of the code.

Security: Output ONLY pandas/numpy operations on the dataframe
(df). Do not use import, exec, eval, open, os, subprocess, or
any other system or file operations. The code will be validated
and rejected if it contains such constructs.

Question: {question}
Output Code:

Where {dict} is the extracted column names and {question} is
the initial prompt provided by the user.

This system prompt is sent to an LLM in order for it to generate
a Python script based on the user's prompt, and the
LLM-generated response is stored in a variable named pythonCode.
The method then evaluates the pythonCode variable in a pyodide
environment.

While the LLM-generated Python script is evaluated in a
non-sandboxed environment, there is a list of forbidden patterns
that are checked before the script is executed on the server.
The function validatePythonCodeForDataFrame() enumerates through
a list named FORBIDDEN_PATTERNS, which contains pairs of regex
patterns and reasons. Each regex pattern is run against the
Python script, and if the pattern is found in the script, the
script is invalidated and is not run, responding to the request
with a reason for rejection.

The input validation can be bypassed, which can still lead to
running arbitrary OS commands on the server. An example of this
is the pattern /\bimport\s+(?!pandas|numpy\b)/g, which intends
to search for lines of code that import a module other than
pandas or numpy. This can be bypassed by importing along with
pandas or numpy. For example, consider the following lines of
code:

import pandas as np, os as pandas
pandas.system("xcalc")

Here, pandas is imported, but so is the os module, with
pandas as its alias. OS commands can then be invoked
with pandas.system().

Using prompt injection techniques, an unauthenticated
attacker with the ability to send prompts to a chatflow
using the CSV Agent node may convince an LLM to respond
with a malicious Python script that executes
attacker-controlled commands on the Flowise server.

It is also possible for an authenticated attacker to exploit
this vulnerability by specifying an attacker-controlled
server in a chatflow. This server would respond to prompts
with an attacker-controlled Python script instead of an
LLM-generated response, which would then be evaluated on
the server.


Relevant Source Code
packages/components/nodes/agents/CSVAgent/core.ts

import type { PyodideInterface } from 'pyodide'
import * as path from 'path'
import { getUserHome } from '../../../src/utils'

let pyodideInstance: PyodideInterface | undefined

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
}

export const systemPrompt = `You are working with a
pandas dataframe in Python. The name of the dataframe
is df.

The columns and data types of a dataframe are given
below as a Python`*
### References
- https://github.com/FlowiseAI/Flowise/security/advisories/GHSA-3hjv-c53m-58jj

@igor-magun-wd igor-magun-wd published to
FlowiseAI/Flowise Apr 15, 2026

Published to the GitHub Advisory Database Apr 21, 2026
Reviewed Apr 21, 2026
Last updated Apr 21, 2026


Severity
Critical
9.2/ 10

CVSS v4 base metrics
Exploitability Metrics
Attack Vector Network
Attack Complexity High
Attack Requirements Present
Privileges Required None
User interaction None
Vulnerable System Impact Metrics
Confidentiality High
Integrity High
Availability High
Subsequent System Impact Metrics
Confidentiality None
Integrity None
Availability None
CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N

EPSS score

Weaknesses
Weakness CWE-184

CVE ID
CVE-2026-41264

GHSA ID
GHSA-3hjv-c53m-58jj

Source code
FlowiseAI/Flowise

Credits

    @zdi-disclosures zdi-disclosures Reporter

________________________________________________________________


Flowise: Airtable_Agent Code Injection Remote Code Execution
Vulnerability
Critical severity GitHub Reviewed Published Apr 15, 2026 in
FlowiseAI/Flowise • Updated Apr 18, 2026
Vulnerability details

Package
flowise (npm)

Affected versions
<= 3.0.13

Patched versions
3.1.0


flowise-components (npm)
Affected versions
<= 3.0.13
Patched versions
3.1.0


Description

ZDI-CAN-29412: FlowiseAI Flowise Airtable_Agent Code Injection
Remote Code Execution Vulnerability

-- ABSTRACT -------------------------------------

Trend Micro's Zero Day Initiative has identified a vulnerability
affecting the following products:

Flowise - Flowise

-- VULNERABILITY DETAILS ------------------------

    Version tested: 3.0.13
    Installer file: hxxps://github.com/FlowiseAI/Flowise
    Platform tested: Ubuntu 25.10


Analysis

FlowiseAI Flowise Airtable Agent pythonCode Prompt Injection
Remote Code Execution Vulnerability

This vulnerability allows remote attackers to execute
arbitrary code on affected installations of FlowiseAI Flowise.
Authentication is not required to exploit this vulnerability.

The specific flaw exists within the run method of the
Airtable_Agents class. The issue results from the lack of
proper sandboxing when evaluating an LLM generated python
script. An attacker can leverage this vulnerability to
execute code in the context of the user running the server.


Product information

FlowiseAI Flowise version 3.0.13 (https://github.com/FlowiseAI/Flowise)

Setup Instructions

npm install -g flowise@3.0.13
npx flowise start
Root Cause Analysis

FlowiseAI Flowise is an open source low-code tool for
developers to build customized large language model (LLM)
applications and AI agents. It supports integration with
various LLMs, data sources, and tools in order to facilitate
rapid development and deployment of AI solutions. Flowise
offers a web interface with a drag-and-drop editor, as well
as an API, through an Express web server accessible over
HTTP on port 3000/TCP.

One such feature of Flowise is the ability to create chatflows.
Chatflows use a drag and drop editor that allow a developer
to place nodes which control how an interaction with a LLM
will occur. One such node is the Airtable Agent node that
represents an Agent used to answer queries on a provided
Airtable table.

When a user makes a query against a chatflow using the
Airtable Agent node, the run method of the Airtable_Agents
class will be called. This method will first request the
contents of the Airtable table passed to the node and
convert it to a base64 string. It will then set up a
pyodide environment and create a python script to be
executed in this environment. This python script will use
pandas to extract the column names and their types from the
Airtable table. The method will then create a system prompt
for an LLM using this data as follows:

You are working with a pandas dataframe in Python. The name
of the dataframe is df.

The columns and data types of a dataframe are given below
as a Python dictionary with keys showing column names and
values showing the data types.
{dict}

I will ask question, and you will output the Python code
using pandas dataframe to answer my question. Do not provide
any explanations. Do not respond with anything except the
output of the code.

Security: Output ONLY pandas/numpy operations on the
dataframe (df). Do not use import, exec, eval, open, os,
subprocess, or any other system or file operations. The
code will be validated and rejected if it contains such
constructs.

Question: {question}
Output Code:

Where {dict} is the extracted column names and {question}
is the initial prompt provided by the user.

This system prompt will be sent to an LLM in order for it
to generate a python script based on the user's prompt,
and the LLM generated response will be stored in a
variable name pythonCode. The method will then evaluate
the pythonCode variable in a pyodide environment.

While the LLM-generated Python script is evaluated in a
non-sandboxed environment, there is a list of forbidden
patterns that are checked for before the script is
executed on the server. The function
validatePythonCodeForDataFrame() enumerates through a
list, named FORBIDDEN_PATTERNS, which contains pairs of
regex pattern and reasons. Each regex pattern is run
against the Python script, and if the pattern is found
in the script, the script is invalidated and is not run,
responding to the request with a reason for rejection.

The input validation can be bypassed, which can still
lead to running arbitrary OS commands on the server. An
example of this is the pattern /\bimport\s+(?!pandas|numpy\b)/g,
which intends to search for lines of code which import
a module other than pandas or numpy. This can be bypassed
by importing along with pandas or numpy. For example,
consider the following lines of code:

import pandas as np, os as pandas
pandas.system("xcalc")

pandas is imported, but so is the os module, with pandas
as its alias. OS commands can then be invoked with
pandas.system().

Using prompt injection techniques, an unauthenticated
attacker with the ability to send prompts to a chatflow
using the Airtable Agent node may convince an LLM to
respond with a malicious python script that executes
attacker controlled commands on the flowise server.

An attacker can use this vulnerability to execute
arbitrary python code, which can lead to arbitrary
system commands being executed on the target server.

It is also possible for an authenticated attacker to
exploit this vulnerability by specifying an attacker
controlled server in a chatflow. This server would
respond to prompts with an attacker controlled python
script instead of an LLM generated response, which
would then be evaluated on the server.

It is also possible for an authenticated attacker to
exploit this vulnerability by specifying an attacker
controlled Airtable table in a chatflow. This airtable
table would contain columns whose name contain prompt
injections, that are later passed to an LLM to use
when generating a python script.

comments documenting the issue have been added to the
following code snippet. Added comments are prepended
with "!!!".

From packages/components/nodes/agents/AirtableAgent/core.ts

import type { PyodideInterface } from 'pyodide'
import * as path from 'path'
import { getUserHome } from '../../../src/utils'

let pyodideInstance: PyodideInterface | undefined

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
}

export const systemPrompt = `You are working with a pandas
dataframe in Python. The name of the dataframe is df.

The columns and data types of a dataframe are given below as a
Python dictionary with keys showing column names and values
showing the data types.
{dict}

I will ask question, and you will output the Python code using
pandas dataframe to answer my question. Do not provide any
explanations. Do not respond with anything except the output
of the code.

Security: Output ONLY pandas/numpy operations on the dataframe
(df). Do not use import, exec, eval, open, os, subprocess, or
any other system or file operations. The code will be
validated and rejected if it contains such constructs.

Question: {question}
Output Code:`

export const finalSystemPrompt = `You are given the question:
{question}. You have an answer to the question: {answer}.
Rephrase the answer into a standalone answer.
Standalone Answer:`

From packages/components/nodes/agents/AirtableAgent/AirtableAgent.ts

import axios from 'axios'
import { BaseLanguageModel } from '@langchain/core/language_models/base'
import { AgentExecutor } from 'langchain/agents'
import { LLMChain } from 'langchain/chains'
import { ICommonObject, INode, INodeData, INodeParams, IServerSideEventStreamer, PromptTemplate } from '../../../src/Interface'
import { getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils'
import { ConsoleCallbackHandler, CustomChainHandler, additionalCallbacks } from '../../../src/handler'
import { LoadPyodide, finalSystemPrompt, systemPrompt } from './core'
import { validatePythonCodeForDataFrame } from '../../../src/pythonCodeValidator'
import { checkInputs, Moderation } from '../../moderation/Moderation'
import { formatResponse } from '../../outputparsers/OutputParserHelpers'

class Airtable_Agents implements INode {
    label: string
    name: string
    version: number
    description: string
    type: string
    icon: string
    category: string
    baseClasses: string[]
    credential: INodeParams
    inputs: INodeParams[]

    // !!! [... Truncated for Readability ...]

    // !!! input variable holds prompt from user
    async run(nodeData: INodeData, input: string, options: ICommonObject): Promise<string | object> {
        const model = nodeData.inputs?.model as BaseLanguageModel
        const baseId = nodeData.inputs?.baseId as string
        const tableId = nodeData.inputs?.tableId as string
        const returnAll = nodeData.inputs?.returnAll as boolean
        const limit = nodeData.inputs?.limit as string
        const moderations = nodeData.inputs?.inputModeration as Moderation[]

        // !!! the chatflow may contain moderation nodes that search for prompt injections, but these may not protect from every type of injection
        if (moderations && moderations.length > 0) {
            try {
                // Use the output of the moderation chain as input for the Vectara chain
                input = await checkInputs(moderations, input)
            } catch (e) {
                await new Promise((resolve) => setTimeout(resolve, 500))
                // if (options.shouldStreamResponse) {
                //     streamResponse(options.sseStreamer, options.chatId, e.message)
                // }
                return formatResponse(e.message)
            }
        }

        const shouldStreamResponse = options.shouldStreamResponse
        const sseStreamer: IServerSideEventStreamer = options.sseStreamer as IServerSideEventStreamer
        const chatId = options.chatId

        const credentialData = await getCredentialData(nodeData.credential ?? '', options)
        const accessToken = getCredentialParam('accessToken', credentialData, nodeData)

        let airtableData: ICommonObject[] = []

        // !!! Get Airtable data
        if (returnAll) {
            airtableData = await loadAll(baseId, tableId, accessToken)
        } else {
            airtableData = await loadLimit(limit ? parseInt(limit, 10) : 100, baseId, tableId, accessToken)
        }

        let base64String = Buffer.from(JSON.stringify(airtableData)).toString('base64')

        const loggerHandler = new ConsoleCallbackHandler(options.logger, options?.orgId)
        const callbacks = await additionalCallbacks(nodeData, options)

        const pyodide = await LoadPyodide()

        // First load the csv file and get the dataframe dictionary of column types
        // For example using titanic.csv: {'PassengerId': 'int64', 'Survived': 'int64', 'Pclass': 'int64', 'Name': 'object', 'Sex': 'object', 'Age': 'float64', 'SibSp': 'int64', 'Parch': 'int64', 'Ticket': 'object', 'Fare': 'float64', 'Cabin': 'object', 'Embarked': 'object'}
        let dataframeColDict = ''
        try {
            const code = `import pandas as pd
import base64
import json

base64_string = "${base64String}"

decoded_data = base64.b64decode(base64_string)

json_data = json.loads(decoded_data)

df = pd.DataFrame(json_data)
my_dict = df.dtypes.astype(str).to_dict()
print(my_dict)
json.dumps(my_dict)`
            dataframeColDict = await pyodide.runPythonAsync(code)
        } catch (error) {
            throw new Error(error)
        }

        // !!! ask LLM to come up with python script...
        // Then tell GPT to come out with ONLY python code
        // For example: len(df), df[df['SibSp'] > 3]['PassengerId'].count()
        let pythonCode = ''
        if (dataframeColDict) {
            const chain = new LLMChain({
                llm: model,
                // !!! prompt passed to LLM
                prompt: PromptTemplate.fromTemplate(systemPrompt),
                verbose: process.env.DEBUG === 'true' ? true : false
            })
            const inputs = {
                // !!! Airtable column names are also subbed into the system prompt which may also contain prompt injections
                dict: dataframeColDict,
                // !!! question, which is later subbed into the system prompt, is given the value of the user's prompt (which may contain prompt injections)
                question: input
            }
            const res = await chain.call(inputs, [loggerHandler, ...callbacks])
            // !!! the LLM's responce is assigned to the pythonCode variable
            pythonCode = res?.text
            // Regex to get rid of markdown code blocks syntax
            pythonCode = pythonCode.replace(/^```[a-z]+\n|\n```$/gm, '')
        }

        // Then run the code using Pyodide (only after validating to prevent RCE)
        let finalResult = ''
        if (pythonCode) {
            const validation = validatePythonCodeForDataFrame(pythonCode)
            if (!validation.valid) {
                throw new Error(
                    `Generated code was rejected for security reasons (${
                        validation.reason ?? 'unsafe construct'
                    }). Please rephrase your question to use only pandas DataFrame operations.`
                )
            }
            try {
                // !!! The python code is evaluated in a non-sandboxed environment
                const code = `import pandas as pd\n${pythonCode}`
                // TODO: get print console output
                finalResult = await pyodide.runPythonAsync(code)
            } catch (error) {
                throw new Error(`Sorry, I'm unable to find answer for question: "${input}" using following code: "${pythonCode}"`)
            }
        }

        // Finally, return a complete answer
        if (finalResult) {
            const chain = new LLMChain({
                llm: model,
                prompt: PromptTemplate.fromTemplate(finalSystemPrompt),
                verbose: process.env.DEBUG === 'true' ? true : false
            })
            const inputs = {
                question: input,
                answer: finalResult
            }

            if (options.shouldStreamResponse) {
                const handler = new CustomChainHandler(shouldStreamResponse ? sseStreamer : undefined, chatId)
                const result = await chain.call(inputs, [loggerHandler, handler, ...callbacks])
                return result?.text
            } else {
                const result = await chain.call(inputs, [loggerHandler, ...callbacks])
                return result?.text
            }
        }

        return pythonCode
    }
}

Proof of Concept

A proof of concept for this vulnerability is provided
in ./poc.py. It expects the following syntax:

    python3 poc.py --method [server OR chatflow OR prompt_injection] [--user <USER> --passwd <password> --host <HOST> --r_host <R_HOST> --r_port <R_PORT> --l_port <L_PORT> --port <PORT> --cmd <CMD> --chatflow_id <CHAT_ID> --airtable_token <AIRTABLE_TOKEN> --base_id <BASE_ID> --table_id <TABLE_ID>]

Where USER is a username of a user on the server, PASSWORD
is the user's password, HOST is the ip address of the
vulnerable flowise server, R_HOST is the ip address of a
malicious server started by this poc, R_PORT is the port
a malicious server started by this poc is listening on
(default: 5000), L_PORT is the port a malicious server
started by this poc should listening on (default: 5000),
PORT is the port the vulnerable flowise server is
listening on (default: 3000), CMD is the command to
execute on the flowise server (default: xcalc), CHAT_ID
is the chatflow id of a chatflow using the Airflow Agent
node, AIRTABLE_TOKEN is an api key for an Airtable
account, BASE_ID is the base id to find an airflow table
in, and TABLE_ID is the airflow table to use.

This poc has three modes of operation controlled by the
method argument. The method argument may have any of the
values "server" OR "chatflow" OR "prompt_injection".

method = "server"

By default the poc will start a malicious server listening
on the port specified by the <L_PORT> value. This server
will respond to requests made to the "/api/chat" endpoint
with a JSON object containing an LLM response that contains
a malicious python script. This python script will execute
a command specified by the value.

method = "chatflow"

By default, the poc will first establish an authenticated
session on the server using the and arguments. It will then
send a POST request to the "/api/v1/chatflow" endpoint with
a JSON body containing a crafted chatflow using an Airflow
Agent node and a ChatOllama node configured with a server
specified by the <R_HOST> and <R_PORT> arguments. The
Airflow Agent node will be configured using the
<AIRTABLE_TOKEN>, <BASE_ID>, and <TABLE_ID> arguments. The
default values of these arguments will be valid for 14 days
from 2026-02-22. The poc will then send a POST request to
the "/api/v1/internal-prediction/" endpoint in order to
trigger a prediction using the chatflow.

It is intended that the server specified in the chatflow is
a server started using the server method of this poc. When
making a prediction against this chatflow, flowise will send
a request to the specified server in order to generate an
LLM response. The response recieved by flowise will be
evaluated as a python script. Upon successful exploitation,
The argument passed to the server method of this poc will
be executed on the vulnerable flowise server.

method = "prompt_injection"

By default, the poc will send a POST request to the
"/api/v1/prediction/chat_id" endpoint, where chat_id is a
vulnerable chatflow id specified by the <CHAT_ID> parameter.
The JSON body of this request will contain a question member
whose value will be a prompt containing a prompt injection.
Upon successful exploitation, The argument will be executed
on the vulnerable flowise server.

Due to the nature of LLM responses, it may take multiple
attempts to be successful or require a different prompt
injection technique depending on the model used.


Testing Environment

The provided proof of concept was tested using FlowiseAI
Flowise version 3.0.13 runing on a Ubuntu 25.10 VM. The
prompt injection method was tested using the Llama3.2 model
running in Ollama.

Credits

Dre Cura (@dre_cura) and Nicholas Zubrisky (@NZubrisky) of
TrendAI Research


How This Differs from CVE-2026-41138

CVE-2026-41138 introduced sanitization for the Pyodide code
ran by the Airtable Agent. This advisory demonstrates a
bypass of that sanitization, which we addressed separately
by disallowing imports outright.


References

    GHSA-v38x-c887-992f


Severity
Critical
9.2/ 10

CVSS v4 base metrics
Exploitability Metrics
Attack Vector Network
Attack Complexity High
Attack Requirements Present
Privileges Required None
User interaction None
Vulnerable System Impact Metrics
Confidentiality High
Integrity High
Availability High
Subsequent System Impact Metrics
Confidentiality None
Integrity None
Availability None
CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N

EPSS score

Weaknesses
Weakness CWE-184

CVE ID
No known CVE

GHSA ID
GHSA-v38x-c887-992f

Source code
FlowiseAI/Flowise

Credits

    @zdi-disclosures zdi-disclosures Reporter
_____________________________________________________________________


Password Reset Link Sent Over Unsecured HTTP
High
igor-magun-wd published GHSA-x5w6-38gp-mrqh Apr 15, 2026

Package
flowise (npm)

Affected versions
<=3.0.13

Patched versions
3.1.0


Description

Summary:
The password reset functionality on cloud.flowiseai.com sends a reset
password link over the unsecured HTTP protocol instead of HTTPS. This
behavior introduces the risk of a man-in-the-middle (MITM) attack,
where an attacker on the same network as the user (e.g., public
Wi-Fi) can intercept the reset link and gain unauthorized access to
the victim’s account.

Steps to Reproduce:

    Sign up for a new account on https://cloud.flowiseai.com/register.
    Navigate to the https://cloud.flowiseai.com/forgot-password page
and enter your email.
    Open your inbox and locate the password reset email.
    Copy the reset link and inspect its protocol – it uses
http:// instead of https://.

POC:
http://url6444.mail.flowiseai.com/ls/click?upn=u001.wa3d8yQsDRACvrFO3KPOeg4btvV98-2FRrNXRtYO9s9CtK622C9ChG4-2BvVg73Tvckl-2B5NZdaQcY4lfu7-2FJ5x9CldlKHZK4mop-2Bv-2FhMDPBX-2FtRDjG7vM-2FSMz1nPIQL3FS94nJSjGnZOW38kMxxMCP92yr092lV1KNGMVDr8xaCpM3k-3D1zEv_0Wzb2YTtJ6lxixf7gbrDfWWVoz-2B4mHPzoyxr9IPI-2Fas8GiBp1THEcPQTeIcCYlgaV0UaD8Y2wiA4ZRRCAp-2BjS0SMkthmibNAiBs2GZjXIaV-2F2wTIaJJdFXWkhTB-2Fc8hJjDhpLnRfayLJ5HyG9gftPNPM-2F9t9DvyHB-2FYLpZzAvou6jB8Nr-2BBFjyWBFrNq0g6su6i-2BwFySXSA-2Bzyg94PQKOA-3D-3D

Impact:
If a victim receives this insecure link and uses it over an
untrusted network, an attacker can sniff the traffic and
capture the reset token. This allows the attacker to hijack
the victim's password reset session, potentially compromising
their account.

Mitigation:
Ensure all sensitive URLs, especially password reset links,
are generated and transmitted over secure
https:// endpoints only.

Best Practice:
Use HTTPS in all password-related email links.
Implement HSTS (HTTP Strict Transport Security) to enforce
secure connections.

Reference
https://hackerone.com/reports/1888915
Severity
High
7.5/ 10

CVSS v4 base metrics
Exploitability Metrics
Attack Vector Network
Attack Complexity High
Attack Requirements Present
Privileges Required None
User interaction Active
Vulnerable System Impact Metrics
Confidentiality High
Integrity High
Availability High
Subsequent System Impact Metrics
Confidentiality None
Integrity None
Availability None
CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:A/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N

CVE ID
CVE-2026-41275

Weaknesses
No CWEs

Credits

    @charmedai charmedai Reporter
_____________________________________________________________________


Unauthenticated TTS endpoint accepts arbitrary credential IDs —
enables API credit abuse via stored credentials
High
igor-magun-wd published GHSA-5fw2-mwhh-9947 Apr 15, 2026

Package
flowise (npm)

Affected versions
<= 3.0.13

Patched versions
3.1.0


Description

Summary

The text-to-speech generation endpoint
(POST /api/v1/text-to-speech/generate) is whitelisted (no auth) and
accepts a credentialId directly in the request body. When called
without a chatflowId, the endpoint uses the provided credentialId
to decrypt the stored credential (e.g., OpenAI or ElevenLabs API
key) and generate speech.
Root Cause

// packages/server/src/controllers/text-to-speech/index.ts:58-64
} else {
    // Use TTS config from request body
    provider = bodyProvider
    credentialId = bodyCredentialId  // ← attacker-controlled credential ID
    voice = bodyVoice
    model = bodyModel
}

Docker Validation

POST /api/v1/text-to-speech/generate with arbitrary credentialId
in body: endpoint processes request, sends SSE tts_start event,
only fails when credential doesn't exist — proves code path runs
without authentication.


Impact

    Use victim's API keys (OpenAI, ElevenLabs, Azure, Google)
without authorization
    Burn API credits on the victim's account
    Generate unlimited speech content at victim's expense
    Combined with credential ID leak from Finding 2, this is
trivially exploitable


Suggested Fix

Remove the TTS endpoint from WHITELIST_URLS or validate that
the credential belongs to the chatflow being used:

// Only allow credentialId when it matches the chatflow's TTS configuration
if (!chatflowId) {
    return res.status(401).json({ message: 'Authentication required' })
}

References

    packages/server/src/controllers/text-to-speech/index.ts lines 10-162
    packages/server/src/utils/constants.ts line 41 (whitelist entry)


Credits

    Shinobi Security - https://github.com/shinobisecurity

Severity
High
8.2/ 10

CVSS v4 base metrics
Exploitability Metrics
Attack Vector Network
Attack Complexity High
Attack Requirements None
Privileges Required None
User interaction None
Vulnerable System Impact Metrics
Confidentiality None
Integrity None
Availability High
Subsequent System Impact Metrics
Confidentiality None
Integrity None
Availability None
CVSS:4.0/AV:N/AC:H/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N

CVE ID
CVE-2026-41279

Weaknesses
No CWEs

Credits

    @DeathsPirate DeathsPirate Reporter
_____________________________________________________________________


Public chatflow endpoints return unsanitized flowData including
plaintext API keys, passwords, and credential IDs
High
igor-magun-wd published GHSA-w47f-j8rh-wx87 Apr 15, 2026

Package
flowise (npm)

Affected versions
<= 3.0.13

Patched versions
3.1.0


Description

Summary

The GET /api/v1/public-chatflows/:id endpoint returns the full chatflow
object without sanitization for public chatflows. Docker validation
revealed this is worse than initially assessed: the
sanitizeFlowDataForPublicEndpoint function does NOT exist in the
released v3.0.13 Docker image. Both public-chatflows AND
public-chatbotConfig return completely raw flowData including
credential IDs, plaintext API keys, and password-type fields.
Root Cause

// packages/server/src/controllers/chatflows/index.ts:218-220
const chatflow = await chatflowsService.getChatflowById(req.params.id)
if (!chatflow) return res.status(StatusCodes.NOT_FOUND).json(...)
if (chatflow.isPublic) return res.status(StatusCodes.OK).json(chatflow) // ← NO sanitization!

Docker Validation (v3.0.13)

Created public chatflow with credential IDs and passwords in
flowData:

{
  "flowData": "{\"nodes\":[{\"data\":{\"credential\":\"e92a39bf-...\",\"inputs\":{\"password\":\"sk-supersecretkey123\",\"apiKey\":\"should-not-leak\"}}}]}"
}

The sanitizeFlowDataForPublicEndpoint function only exists in
unreleased HEAD, and even there, only public-chatbotConfig
calls it — public-chatflows never does.


Impact

    Credential IDs leaked — enables OAuth2 token theft chain
(Finding 1)
    Plaintext API keys and passwords leaked — direct
third-party account compromise
    Node configurations leaked — reveals internal architecture
and endpoint URLs
    Both public-chatflows and public-chatbotConfig are
affected in the released version


Suggested Fix

Apply sanitization to both public endpoints:

const sanitized = sanitizeFlowDataForPublicEndpoint(chatflow)
return res.status(StatusCodes.OK).json(sanitized)

Ensure the sanitization function strips all credential,
password, apiKey, and secretKey fields from flowData.


References

    packages/server/src/controllers/chatflows/index.ts lines 209-236
    packages/server/src/utils/sanitizeFlowData.ts lines 11-34 (exists only in unreleased HEAD)


Credits

    Shinobi Security - https://github.com/shinobisecurity

Severity
High
8.7/ 10

CVSS v4 base metrics
Exploitability Metrics
Attack Vector Network
Attack Complexity Low
Attack Requirements None
Privileges Required None
User interaction None
Vulnerable System Impact Metrics
Confidentiality High
Integrity None
Availability None
Subsequent System Impact Metrics
Confidentiality None
Integrity None
Availability None
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N

CVE ID
CVE-2026-41278

Weaknesses
No CWEs

Credits

    @DeathsPirate DeathsPirate Reporter
_____________________________________________________________________


Mass Assignment in DocumentStore Create Endpoint Leads to
Cross-Workspace Object Takeover (IDOR)
High
igor-magun-wd published GHSA-3prp-9gf7-4rxx Apr 15, 2026

Package
flowise (npm)

Affected versions
<= 3.0.13

Patched versions
3.1.0


Description

Summary

A Mass Assignment vulnerability in the DocumentStore creation endpoint
allows authenticated users to control the primary key (id) and
internal state fields of DocumentStore entities.

Because the service uses repository.save() with a client-supplied
primary key, the POST create endpoint behaves as an implicit UPSERT
operation. This enables overwriting existing DocumentStore objects.

In multi-workspace or multi-tenant deployments, this can lead to
cross-workspace object takeover and broken object-level
authorization (IDOR), allowing an attacker to reassign or modify
DocumentStore objects belonging to other workspaces.


Details

The DocumentStore entity defines a globally unique primary key:

@PrimaryGeneratedColumn('uuid')
id: string

The create logic is implemented as:

const documentStore = repo.create(newDocumentStore)
const dbResponse = await repo.save(documentStore)

Here is no DTO allowlist or field filtering before persistence.
The entire request body is mapped directly to the entity.

TypeORM save() behavior:

    If the primary key (id) exists → UPDATE
    If not → INSERT

Because id is accepted from the client, the create endpoint
effectively functions as an UPSERT endpoint.

This allows an authenticated user to submit:

{
  "id": "<existing_store_id>",
  "name": "modified",
  "description": "modified",
  "status": "SYNC",
  "embeddingConfig": "...",
  "vectorStoreConfig": "...",
  "recordManagerConfig": "..."
}

If a DocumentStore with the supplied id already exists, save()
performs an UPDATE rather than creating a new record.

Importantly:

The primary key is globally unique (uuid)
It is not composite with workspaceId
The create path does not enforce ownership validation before
calling save()
This introduces a broken object-level authorization risk.

If an attacker can obtain or enumerate a valid DocumentStore
UUID belonging to another workspace, they can:
Submit a POST create request with that UUID.
Trigger an UPDATE on the existing record.
Potentially overwrite fields including workspaceId, effectively
reassigning the object to their own workspace.

Because the service layer does not verify that the existing
record belongs to the caller’s workspace before updating,
this may result in cross-workspace object takeover.

Additionally, several service functions retrieve DocumentStore
entities by id without consistently scoping by workspaceId,
increasing the risk of IDOR if controller-level protections
are bypassed or misconfigured.


PoC

    Create a normal DocumentStore in Workspace A.
    Capture its id from the API response.
    From Workspace B (or another authenticated context), submit:

POST /api/v1/document-store
Content-Type: application/json

{
  "id": "<id_from_workspace_A>",
  "name": "hijacked",
  "description": "hijacked"
}

Because the service uses repository.save() with a
client-supplied primary key:

    The existing record is updated.
    The object may become reassigned depending on how
workspaceId is handled at controller level.
    If workspaceId is overwritten during the create flow,
the store is effectively migrated to the attacker’s workspace.
    This demonstrates object takeover via UPSERT semantics
on a create endpoint.


Impact

This vulnerability enables:

    Mass Assignment on server-managed fields
    Overwrite of existing objects via implicit UPSERT behavior
    Broken Object Level Authorization (BOLA)
    Potential cross-workspace object takeover in multi-tenant
deployments
    In a SaaS or shared-workspace environment, an attacker who
can obtain or guess a valid UUID may modify or reassign
DocumentStore objects belonging to other tenants.

Because DocumentStore objects control embedding providers,
vector store configuration, and record management logic,
successful takeover can affect data indexing, retrieval,
and AI workflow execution.

This represents a high-risk authorization flaw in
multi-tenant environments.


Severity
High
7.6/ 10

CVSS v4 base metrics
Exploitability Metrics
Attack Vector Network
Attack Complexity Low
Attack Requirements Present
Privileges Required Low
User interaction None
Vulnerable System Impact Metrics
Confidentiality High
Integrity High
Availability Low
Subsequent System Impact Metrics
Confidentiality None
Integrity None
Availability None
CVSS:4.0/AV:N/AC:L/AT:P/PR:L/UI:N/VC:H/VI:H/VA:L/SC:N/SI:N/SA:N

CVE ID
CVE-2026-41277

Weaknesses
Weakness CWE-284
Weakness CWE-639
Weakness CWE-915

Credits

    @berkdedekarginoglu berkdedekarginoglu Reporter
_____________________________________________________________________


Unauthenticated OAuth 2.0 Access Token Disclosure via Public Chatflow
in Flowise
High
igor-magun-wd published GHSA-6f7g-v4pp-r667 Apr 15, 2026

Package
flowise (npm)

Affected versions
<= 3.0.13

Patched versions
3.1.0


Description

Summary

Flowise contains an authentication bypass vulnerability that allows
an unauthenticated attacker to obtain OAuth 2.0 access tokens
associated with a public chatflow.

By accessing a public chatflow configuration endpoint, an attacker
can retrieve internal workflow data, including OAuth credential
identifiers, which can then be used to refresh and obtain valid
OAuth 2.0 access tokens without authentication.


Details

Flowise is designed to allow public chatflows to be accessed by
unauthenticated end users via public URLs or embedded widgets. As
a result, chatflowId values are intentionally exposed to
unauthenticated clients and must not be treated as secrets.

However, the endpoint GET /api/v1/public-chatbotConfig/<chatflowId>
returns internal flowData without authentication. The returned
flowData includes workflow node definitions containing OAuth
credential identifiers (credential field).

Separately, the endpoint
POST /api/v1/oauth2-credential/refresh/<credentialId> allows OAuth.
2.0 tokens to be refreshed without authentication or authorization
checks.

Because credential identifiers can be obtained from the
unauthenticated public chatflow configuration endpoint, these two
behaviors can be combined to allow unauthenticated OAuth 2.0
access token disclosure.

PoC

Prerequisites

    Self-hosted Flowise instance
    A public chatflow configured with an OAuth 2.0 credential
(e.g., Gmail OAuth2)

Step 1: Obtain chatflowId

The chatflowId is exposed to unauthenticated users via public
chatflow URLs, embedded widgets, or browser network requests when
accessing a public chatflow.

Example: d37b9812-72c1-4c64-b152-665f307f755e
Step 2: Retrieve internal flowData without authentication

curl -s \
  http://localhost:3000/api/v1/public-chatbotConfig/d37b9812-72c1-4c64-b152-665f307f755e

The response includes flowData containing an OAuth credential identifier, for example:

"credential": "6efe0e20-ba6f-4fbb-9960-658feffa0542"

Step 3: Refresh OAuth 2.0 token without authentication

curl -X POST \
  http://localhost:3000/api/v1/oauth2-credential/refresh/6efe0e20-ba6f-4fbb-9960-658feffa0542

The response returns valid OAuth 2.0 access token data,
including an access_token.


Impact

An unauthenticated attacker can obtain OAuth 2.0 access tokens
for third-party services configured in Flowise, potentially
leading to unauthorized data access, API abuse, or account
compromise.

This vulnerability affects self-hosted deployments because public
chatflows are commonly exposed to the internet and require
unauthenticated access by design. Treating chatflowId as a
secret does not mitigate the issue.


Severity
High
7.7/ 10

CVSS v4 base metrics
Exploitability Metrics
Attack Vector Network
Attack Complexity Low
Attack Requirements None
Privileges Required None
User interaction None
Vulnerable System Impact Metrics
Confidentiality None
Integrity Low
Availability None
Subsequent System Impact Metrics
Confidentiality High
Integrity None
Availability None
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:L/VA:N/SC:H/SI:N/SA:N

CVE ID
CVE-2026-41273

Weaknesses
Weakness CWE-306

Credits

    @melonattacker melonattacker Reporter


=========================================================
+ CERT-RENATER        |    tel : 01-53-94-20-44         +
+ 23/25 Rue Daviel    |    fax : 01-53-94-20-41         +
+ 75013 Paris         |   email:cert@support.renater.fr +
=========================================================




