Flowise's `/api/v1/chatflows/apikey/:apikey` endpoint fails to scope queries to the caller's workspace, meaning any authenticated user with a valid API key in one workspace can retrieve the full configuration — system prompts, custom code, internal URLs, and credential IDs — of chatflows belonging to every other workspace in the deployment. Because API key assignment is opt-in in Flowise, the vast majority of chatflows in a multi-tenant instance are exposed by default, amplifying the blast radius significantly. No public exploit is listed and EPSS data is unavailable, but the attack requires only a simple curl call with no special tooling, making it trivially reproducible by any tenant. Organizations running Flowise in multi-workspace or SaaS-style configurations should upgrade to 3.1.2 immediately; as a short-term workaround, assign an API key to every chatflow to remove them from the unscoped OR clause.
What is the risk?
Rated medium by the reporter, but operationally this is high-impact for multi-tenant Flowise deployments. Exploitability is trivial — a single unauthenticated-looking HTTP GET with a valid API key is all that is required, and the default behavior (omitting `keyonly`) triggers the vulnerable code path. The primary risk mitigant is that an attacker must already hold a valid API key in the target instance, narrowing the threat to insider threats, compromised accounts, or shared-instance scenarios. Given Flowise's 79 prior CVEs and its role as a no-code AI agent builder used by teams with limited security maturity, patching velocity may be slow across the installed base.
Attack Kill Chain
What systems are affected?
| Package | Ecosystem | Vulnerable Range | Patched |
|---|---|---|---|
| flowise | npm | <= 3.1.1 | 3.1.2 |
Do you use flowise? You're affected.
Severity & Risk
What should I do?
5 steps-
Patch: Upgrade flowise to 3.1.2 which adds workspace-scoped filtering to the
getChatflowByApiKeyservice query. -
Workaround (if patching is blocked): Assign an explicit API key to every chatflow — this removes them from the
apikeyid IS NULLOR clause and breaks the cross-workspace leak. -
Audit: Review server-side access logs for requests to
/api/v1/chatflows/apikey/lacking the?keyonly=trueparameter, particularly from API keys belonging to low-privilege or external workspaces. -
Credential rotation: Any TTS/STT credential IDs stored in chatflow configs should be considered compromised and rotated if multi-workspace access existed prior to patching.
-
Network controls: Restrict the Flowise API port to internal networks or place an API gateway in front that enforces workspace-level JWT claims before proxying requests.
Classification
Compliance Impact
This CVE is relevant to:
Frequently Asked Questions
What is GHSA-c2c9-mfw7-p8hw?
Flowise's `/api/v1/chatflows/apikey/:apikey` endpoint fails to scope queries to the caller's workspace, meaning any authenticated user with a valid API key in one workspace can retrieve the full configuration — system prompts, custom code, internal URLs, and credential IDs — of chatflows belonging to every other workspace in the deployment. Because API key assignment is opt-in in Flowise, the vast majority of chatflows in a multi-tenant instance are exposed by default, amplifying the blast radius significantly. No public exploit is listed and EPSS data is unavailable, but the attack requires only a simple curl call with no special tooling, making it trivially reproducible by any tenant. Organizations running Flowise in multi-workspace or SaaS-style configurations should upgrade to 3.1.2 immediately; as a short-term workaround, assign an API key to every chatflow to remove them from the unscoped OR clause.
Is GHSA-c2c9-mfw7-p8hw actively exploited?
No confirmed active exploitation of GHSA-c2c9-mfw7-p8hw has been reported, but organizations should still patch proactively.
How to fix GHSA-c2c9-mfw7-p8hw?
1. Patch: Upgrade flowise to 3.1.2 which adds workspace-scoped filtering to the `getChatflowByApiKey` service query. 2. Workaround (if patching is blocked): Assign an explicit API key to every chatflow — this removes them from the `apikeyid IS NULL` OR clause and breaks the cross-workspace leak. 3. Audit: Review server-side access logs for requests to `/api/v1/chatflows/apikey/` lacking the `?keyonly=true` parameter, particularly from API keys belonging to low-privilege or external workspaces. 4. Credential rotation: Any TTS/STT credential IDs stored in chatflow configs should be considered compromised and rotated if multi-workspace access existed prior to patching. 5. Network controls: Restrict the Flowise API port to internal networks or place an API gateway in front that enforces workspace-level JWT claims before proxying requests.
What systems are affected by GHSA-c2c9-mfw7-p8hw?
This vulnerability affects the following AI/ML architecture patterns: agent frameworks, no-code AI workflow builders, multi-tenant AI deployments, AI chatbot platforms.
What is the CVSS score for GHSA-c2c9-mfw7-p8hw?
No CVSS score has been assigned yet.
Technical Details
NVD Description
## Summary The `/api/v1/chatflows/apikey/:apikey` endpoint (whitelisted, accessible with API key auth only) returns all chatflows bound to the provided API key AND all chatflows across the entire system that have no API key assigned. This crosses workspace boundaries, allowing a user in Workspace A who has a valid API key to read the full configuration (including flowData, chatbotConfig, system prompts, and node configurations) of chatflows from Workspace B, Workspace C, and all other workspaces, as long as those chatflows have no API key assigned. ## Details The controller at `packages/server/src/controllers/chatflows/index.ts:90-107` validates the API key and calls the service: ```typescript const getChatflowByApiKey = async (req: Request, res: Response, next: NextFunction) => { try { const apikey = await apiKeyService.getApiKey(req.params.apikey) if (\!apikey) { return res.status(401).send("Unauthorized") } const apiResponse = await chatflowsService.getChatflowByApiKey(apikey.id, req.query.keyonly) return res.json(apiResponse) // Returns full chatflow objects with flowData } catch (error) { next(error) } } ``` The service at `packages/server/src/services/chatflows/index.ts:223-245` builds the database query: ```typescript const getChatflowByApiKey = async (apiKeyId: string, keyonly?: unknown): Promise<any> => { const appServer = getRunningExpressApp() let query = appServer.AppDataSource.getRepository(ChatFlow) .createQueryBuilder("cf") .where("cf.apikeyid = :apikeyid", { apikeyid: apiKeyId }) if (keyonly === undefined) { // When keyonly is not set (default), also return ALL chatflows with no API key query = query.orWhere("cf.apikeyid IS NULL").orWhere("cf.apikeyid = ''") } const dbResponse = await query.orderBy("cf.name", "ASC").getMany() return dbResponse // Returns full ChatFlow entities including flowData } ``` When `keyonly` is not provided as a query parameter (which is the default case), the query expands to include: - All chatflows bound to the provided API key (same workspace, expected behavior) - ALL chatflows with `apikeyid IS NULL` (any workspace, no workspace filter) - ALL chatflows with empty `apikeyid` (any workspace, no workspace filter) There is NO `workspaceId` filter in this query. The response includes the full `ChatFlow` entity, which contains: - `flowData` - the complete workflow graph including system prompts, model names, internal URLs, custom code - `chatbotConfig` - chatbot configuration including allowed origins - `apiConfig` - API configuration and override settings - `textToSpeech` / `speechToText` - TTS/STT configuration including credential IDs - `analytic` - analytics configuration ## PoC ```bash # Step 1: Attacker has a valid API key for Workspace A API_KEY="<attacker-workspace-a-api-key>" # Step 2: Query the chatflows/apikey endpoint WITHOUT keyonly parameter # Returns the attacker chatflows PLUS all chatflows without API keys from ALL workspaces curl -s "http://localhost:3000/api/v1/chatflows/apikey/" | jq ".[].workspaceId" # Step 3: With keyonly parameter, only chatflows bound to the API key are returned curl -s "http://localhost:3000/api/v1/chatflows/apikey/?keyonly=true" | jq ".[].workspaceId" ``` ## Impact - **Cross-Workspace Information Disclosure**: A user in any workspace can read the full configuration of chatflows from all other workspaces that do not have an API key assigned. This breaks workspace isolation. - **Intellectual Property Exposure**: System prompts, custom function code, and workflow architecture of chatflows from other workspaces/organizations are exposed. - **Credential Reference Leakage**: The `textToSpeech` and `speechToText` fields include credential IDs, which can be abused via the TTS generate endpoint. - **Amplified by Default**: Most chatflows are created without an API key assigned (API keys are opt-in), so the majority of chatflows in a multi-workspace deployment are affected. ## Recommended Fix Add workspace scoping to the `getChatflowByApiKey` query by passing the API key workspace ID and filtering the OR clause: ```typescript // packages/server/src/services/chatflows/index.ts const getChatflowByApiKey = async (apiKeyId: string, keyonly?: unknown, workspaceId?: string): Promise<any> => { const appServer = getRunningExpressApp() let query = appServer.AppDataSource.getRepository(ChatFlow) .createQueryBuilder("cf") .where("cf.apikeyid = :apikeyid", { apikeyid: apiKeyId }) if (keyonly === undefined && workspaceId) { // Only include unprotected chatflows from the SAME workspace query = query.orWhere( "(cf.apikeyid IS NULL OR cf.apikeyid = :empty) AND cf.workspaceId = :workspaceId", { empty: "", workspaceId } ) } const dbResponse = await query.orderBy("cf.name", "ASC").getMany() return dbResponse } ```
Exploitation Scenario
An adversary (a competitor, a disgruntled employee, or an attacker who phished an API key) authenticates to a shared Flowise instance with a low-privilege API key tied to Workspace A. They issue `GET /api/v1/chatflows/apikey/<their-key>` — no special parameters, just the default call. The TypeORM query executes `WHERE apikeyid = '<their-id>' OR apikeyid IS NULL OR apikeyid = ''` with no workspace filter, returning every chatflow across the entire platform that lacks an API key. The attacker iterates through the response, extracting `flowData` JSON blobs that contain GPT-4 system prompts encoding proprietary business logic, internal API endpoint URLs, hardcoded credentials passed as node parameters, and TTS credential IDs. They then replay those credential IDs against the `/api/v1/chatflows/<id>/tts` endpoint to verify access. Total time from API key to full intellectual property dump: under five minutes.
Weaknesses (CWE)
References
Timeline
Related Vulnerabilities
CVE-2025-59528 10.0 Flowise: Unauthenticated RCE via MCP config injection
Same package: flowise CVE-2026-40933 9.9 Flowise: RCE via MCP stdio command injection
Same package: flowise CVE-2025-61913 9.9 Flowise: path traversal in file tools leads to RCE
Same package: flowise CVE-2026-30821 9.8 flowise: Arbitrary File Upload enables RCE
Same package: flowise CVE-2026-30824 9.8 Flowise: auth bypass exposes NVIDIA NIM container endpoints
Same package: flowise