{"vulnerability": "CVE-2026-7047", "sightings": [{"uuid": "65c85961-6697-49da-89d4-77ad5773581f", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "CVE-2026-70470", "type": "seen", "source": "https://bsky.app/profile/stackflag.bsky.social/post/3msbn3bud6i2i", "content": "CVE-2026-70470 - flowise\nFlowise has a security issue that could allow an attacker to execute malicious code on your computer. This is because the software does not properly block certain types of code, which\u2026\n\nToo many irrelevant or confusing CVEs? Use stackflag.com\n\n#flowise #npm #CVE #infosec", "creation_timestamp": "2026-08-04T18:24:05.210115Z"}, {"uuid": "0a40689d-25ab-48d9-a1b5-026a0711e7e8", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "86ecb4e1-bb32-44d5-9f39-8a4673af8385", "vulnerability": "CVE-2026-70473", "type": "published-proof-of-concept", "source": "https://github.com/FlowiseAI/Flowise/security/advisories/GHSA-fr6g-7cq8-fg82", "content": "", "creation_timestamp": "2026-08-04T18:35:03.732062Z"}, {"uuid": "f10dbe14-876e-4d06-9dde-78add58bb4e7", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "86ecb4e1-bb32-44d5-9f39-8a4673af8385", "vulnerability": "CVE-2026-70470", "type": "published-proof-of-concept", "source": "https://github.com/FlowiseAI/Flowise/security/advisories/GHSA-52fh-8v99-63c2", "content": "", "creation_timestamp": "2026-08-04T18:35:08.227106Z"}, {"uuid": "162c51dc-959c-4759-a46f-659317ec8be7", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "CVE-2026-70474", "type": "seen", "source": "https://gist.github.com/alon710/1b2f0c6a49d8d6453f101ab57c6809f5", "content": "# CVE-2026-70474: CVE-2026-70474: Incorrect Authorization and Missing Authentication in Flowise OAuth2 Credential Endpoints\n\n&gt; **CVSS Score:** 7.6\n&gt; **Published:** 2026-08-04\n&gt; **Full Report:** https://cvereports.com/reports/CVE-2026-70474\n\n## Summary\nA critical authorization flaw exists in Flowise, a popular drag-and-drop orchestrator for building customized Large Language Model flows. Prior to version 3.1.3, multiple OAuth2 credential endpoints do not filter database lookups by the requesting entity's workspace context. This omission, combined with the exclusion of several endpoints from the global authentication pipeline, permits unauthenticated remote actors to access, manipulate, or steal access tokens linked to external service integrations.\n\n## TL;DR\nUnauthenticated remote attackers can exfiltrate active third-party access tokens, inject rogue OAuth2 states, or access cross-workspace metadata due to missing workspace isolation and overly permissive API routing whitelists in Flowise &lt; 3.1.3.\n\n## Technical Details\n\n- **CWE ID**: CWE-863 (Incorrect Authorization)\n- **Attack Vector**: Network (AV:N)\n- **CVSS Score**: 7.6 (High)\n- **Exploit Status**: PoC / Publicly Documented\n- **KEV Status**: Not Listed\n- **Impact**: Unauthenticated Token Theft &amp; Injection\n\n## Affected Systems\n\n- Flowise (npm package 'flowise')\n\n## Mitigation\n\n- Upgrade Flowise to version 3.1.3 or later\n- Deploy Flowise behind a VPN or Zero-Trust Network Access (ZTNA) gateway\n- Configure WAF rules to block external access to '/api/v1/oauth2-credential/refresh/' and '/api/v1/oauth2-credential/callback' paths\n\n**Remediation Steps:**\n1. Verify the installed version of Flowise in package.json or system headers\n2. Execute 'npm install flowise@3.1.3' or update container images to 'flowiseai/flowise:3.1.3' or newer\n3. Rotate external OAuth2 client secrets and existing authorization tokens linked to Flowise database records\n\n## References\n\n- [GitHub Security Advisory](https://github.com/FlowiseAI/Flowise/security/advisories/GHSA-wch5-xp77-fxg4)\n- [Flowise 3.1.3 Release Tag](https://github.com/FlowiseAI/Flowise/releases/tag/flowise@3.1.3)\n- [Official CVE Record](https://www.cve.org/CVERecord?id=CVE-2026-70474)\n\n\n---\n*Generated by [CVEReports](https://cvereports.com/reports/CVE-2026-70474) - Automated Vulnerability Intelligence*", "creation_timestamp": "2026-08-04T19:31:47.362635Z"}, {"uuid": "e3a7a407-3628-4121-8d91-3e43777ec0fb", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "CVE-2026-70477", "type": "seen", "source": "https://bsky.app/profile/stackflag.bsky.social/post/3msbt4g2hcj2z", "content": "CVE-2026-70477 - flowise\nFlowise, a chatflow platform, has a vulnerability that allows attackers to inject malicious code into the system. This can happen if an attacker sends a special type of\u2026\n\nToo many irrelevant or confusing CVEs? Use stackflag.com\n\n#flowise #flowiseai #npm #CVE #infosec", "creation_timestamp": "2026-08-04T20:12:05.626808Z"}, {"uuid": "0bf1a38a-513f-42e7-961c-7ea7da933290", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "86ecb4e1-bb32-44d5-9f39-8a4673af8385", "vulnerability": "CVE-2026-70479", "type": "published-proof-of-concept", "source": "https://github.com/open-webui/open-webui/security/advisories/GHSA-w2rx-84hp-gg95", "content": "", "creation_timestamp": "2026-08-04T20:35:16.846024Z"}, {"uuid": "f3a0f4e8-ecd4-4ae4-b021-121619648d87", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "86ecb4e1-bb32-44d5-9f39-8a4673af8385", "vulnerability": "CVE-2026-70477", "type": "published-proof-of-concept", "source": "https://github.com/FlowiseAI/Flowise/security/advisories/GHSA-5xvg-pmgg-3mxr", "content": "", "creation_timestamp": "2026-08-04T20:35:19.057425Z"}, {"uuid": "5add3b68-81f1-4d2a-9224-f533b6f30647", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "86ecb4e1-bb32-44d5-9f39-8a4673af8385", "vulnerability": "CVE-2026-70476", "type": "published-proof-of-concept", "source": "https://github.com/FlowiseAI/Flowise/security/advisories/GHSA-gmmw-qg98-6j6p", "content": "", "creation_timestamp": "2026-08-04T20:35:21.576019Z"}, {"uuid": "f5b9f866-802b-479a-b329-0634c503bbe1", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "86ecb4e1-bb32-44d5-9f39-8a4673af8385", "vulnerability": "CVE-2026-70474", "type": "published-proof-of-concept", "source": "https://github.com/FlowiseAI/Flowise/security/advisories/GHSA-wch5-xp77-fxg4", "content": "", "creation_timestamp": "2026-08-04T20:35:23.713165Z"}, {"uuid": "2a457661-f542-411a-83b7-b7187e80b70c", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "CVE-2026-70478", "type": "seen", "source": "https://bsky.app/profile/stackflag.bsky.social/post/3msbwlb3shi23", "content": "CVE-2026-70478 - flowise\nFlowise's OAuth token refresh endpoint can be accessed without a password, allowing an attacker to steal access tokens for connected services like Google or Microsoft. This is\u2026\n\nToo many irrelevant or confusing CVEs? Use stackflag.com\n\n#flowise #flowiseai #npm #CVE #infosec", "creation_timestamp": "2026-08-04T21:14:04.924071Z"}, {"uuid": "7fd0a1dd-1395-43dd-a71e-0041dfe5fa97", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "cve-2026-70478", "type": "seen", "source": "https://infosec.exchange/users/offseq/statuses/117040665415933315", "content": "FlowiseAI Flowise (&lt;3.1.3) has a CRITICAL vuln (CVE-2026-70478): unauthenticated POST endpoint leaks refreshed OAuth tokens if credential ID is known. Upgrade to 3.1.3+ ASAP. https://radar.offseq.com/threat/cve-2026-70478-cwe-200-exposure-of-sensitive-information-to-an-unauthorized-actor-in-flowiseai-flowise-2c912baff770743c #OffSeq #CVE202670478 #OAuth #infosec", "creation_timestamp": "2026-08-05T03:00:27.349808Z"}, {"uuid": "7c800573-2dfa-4f7d-b03f-0bbcbf615182", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "cve-2026-70478", "type": "seen", "source": "https://bsky.app/profile/offseq.bsky.social/post/3mscjwmp65s2c", "content": "FlowiseAI Flowise &lt;3.1.3 has a CRITICAL flaw: unauthenticated API leaks OAuth tokens. Upgrade to 3.1.3+ to prevent unauthorized access. https://radar.offseq.com/threat/cve-2026-70478-cwe-200-exposure-of-sensitive-information-to-an-unauthorized-actor-in-flowiseai-flowise-2c912baff770743c #OffSeq #...", "creation_timestamp": "2026-08-05T03:00:27.743547Z"}, {"uuid": "7478470d-db86-4cf0-850d-93c15cd2a495", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "cve-2026-70477", "type": "seen", "source": "https://infosec.exchange/users/offseq/statuses/117041019348392384", "content": "FlowiseAI Flowise &lt;3.1.3 is affected by CRITICAL CVE-2026-70477 (code injection, CVSS 9.5). Exploitation via CSV Agent node allows arbitrary Python execution. Patch to 3.1.3+ ASAP. https://radar.offseq.com/threat/cve-2026-70477-cwe-94-improper-control-of-generation-of-code-code-injection-in-flowiseai-flowise-52ff32a90a84fd22 #OffSeq #Infosec #CVE #AppSec", "creation_timestamp": "2026-08-05T04:30:26.592054Z"}, {"uuid": "4e3972eb-1f51-467f-815e-4d38dcae6839", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "cve-2026-70477", "type": "seen", "source": "https://bsky.app/profile/offseq.bsky.social/post/3mscoxl33nt2c", "content": "CRITICAL: FlowiseAI Flowise &lt;3.1.3 vulnerable to CVE-2026-70477 code injection. Attackers can run arbitrary Python via CSV Agent node. Upgrade to 3.1.3+ immediately. https://radar.offseq.com/threat/cve-2026-70477-cwe-94-improper-control-of-generation-of-code-code-injection-in-flowiseai-flowise-52...", "creation_timestamp": "2026-08-05T04:30:29.395804Z"}, {"uuid": "50fcbe82-96b1-43e8-b1a6-36b88de8d1b1", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "CVE-2026-7047", "type": "seen", "source": "https://bsky.app/profile/cve.skyfleet.blue/post/3mnlibrygem2y", "content": "CVE-2026-7047 - Frontend User Notes\nCVE ID : CVE-2026-7047\n \n Published : June 6, 2026, 12:16 a.m. | 16\u00a0minutes ago\n \n Description : The Frontend User Notes plugin for WordPress is vulnerable to Cross-Site Request Forgery in all versions up to, and including, 2.1.1. This is du...", "creation_timestamp": "2026-06-06T01:18:54.125261Z"}, {"uuid": "bbeaa5c4-40f7-4383-b5a3-978993072232", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "CVE-2026-7047", "type": "seen", "source": "https://bsky.app/profile/atomicedge.bsky.social/post/3mo5w7l5jt52y", "content": "CVE-2026-7047 frontend-user-notes (CVSS Score 4.3) \n\n#WordPress plugin #vulnerability #cybersecurity #wordpressfirewall #wordpresssecurity #hacking #wpsecurity #atomicedge #cybersecurity #malware #vulnerabilityresearch #cve #redteam #proofofconcept #frontend-user-notes", "creation_timestamp": "2026-06-13T09:16:07.529537Z"}, {"uuid": "8ec76d8c-2e91-49c5-87e5-371a8cbd729f", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "CVE-2026-70472", "type": "seen", "source": "https://gist.github.com/haidang-infosec/402db84bee7aca2f57bb109b31574649", "content": "# CVE-2026-71962: Unauthenticated Cross-Tenant File Disclosure in Flowise via `openai-assistants-file/download`\n\n**CVE ID:** CVE-2026-71962\n**CNA:** VulnCheck\n**Author:** Mai Hai Dang (haidang.infosec@gmail.com)\n**Report date:** 2026-08-09\n**Product:** Flowise ([FlowiseAI/Flowise](https://github.com/FlowiseAI/Flowise), npm package `flowise`)\n**Affected component:** `packages/server` \u2014 REST API\n**Vulnerability class:** Missing Authorization / Insecure Direct Object Reference (IDOR)\n**CWE:** CWE-862 (Missing Authorization) \u2014 primary; CWE-639 (Authorization Bypass Through User-Controlled Key) \u2014 secondary\n**CVSS 3.1:** `CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N` \u2014 **7.5 (High)**\n**CVSS 4.0:** `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` \u2014 **8.7 (High)**\n\n---\n\n## 1. Summary\n\nThe Flowise REST API endpoint `POST /api/v1/openai-assistants-file/download` is served without any authentication check \u2014 it is listed in the server's global `WHITELIST_URLS`, which bypasses both session-cookie and API-key authentication for any request whose path matches it. The handler behind this endpoint (`getFileFromAssistant`) resolves and streams a file from the server's file storage (local disk or S3, depending on deployment) using three client-supplied identifiers \u2014 `chatflowId`, `chatId`, `fileName` \u2014 without ever verifying that the target chatflow is public (`isPublic: true`) or that the requester belongs to the workspace that owns it.\n\nAn unauthenticated requester who knows a valid `chatflowId`/`chatId`/`fileName` tuple can retrieve the corresponding stored file without any authentication or workspace-membership check. This was directly reproduced against a private chatflow (Section 4). Cross-workspace/cross-organization impact is not separately runtime-tested with two tenants, but follows directly from the handler's data flow: `getFileFromAssistant` resolves the owning workspace and organization from the *supplied* `chatflowId` alone (Section 3.3) and never evaluates the requester's identity, so the same zero-authentication request works identically regardless of which workspace or organization the target chatflow belongs to.\n\nThis is a distinct defect from the previously disclosed and patched path-traversal vulnerabilities affecting the same endpoint (GHSA-99pg-hqvx-r4gf, GHSA-q67q-549q-p849); those were fixed by adding strict path-traversal validation to `chatflowId`/`chatId`/`fileName`, which is confirmed present and effective in the version tested. The defect reported here is a pure authorization gap: the request never leaves the *intended* storage path, it simply is never checked for ownership.\n\n## 2. Affected Versions\n\n| Scope | Status |\n|---|---|\n| npm `flowise` `3.1.4` (latest published release, git tag `flowise@3.1.4` = commit `a65f81bb`, 2026-07-29) | Confirmed by runtime reproduction (Section 4) |\n| Current public GitHub `main` branch HEAD | Confirmed by source inspection (code path unchanged relative to `3.1.4`) |\n| npm `flowise` `2.2.4` \u2013 `3.1.3` | Presumed affected, based on `git log`/`git blame` showing the vulnerable code has been present, unmodified, since `2.2.4`; not individually runtime-tested per version |\n\nThe PoC in this report was reproduced by checking out the official release tag directly (`git checkout flowise@3.1.4`), removing any ambiguity about whether the finding applies to unreleased code.\n\n**Version history (established via `git log`/`git blame` against the public repository):**\n- The `WHITELIST_URLS` entry for this exact path was introduced in commit `8d266052` (\"Feature/update upsert API\"), dated 2025-01-09, first shipped in release `2.2.4`. `git log -S` against the literal route string shows this is the only commit that has ever touched it \u2014 it has never been removed or narrowed.\n- The `getFileFromAssistant` handler has never, at any point in its history, contained an `isPublic`, `req.user`, or workspace-membership check (`git log -S\"isPublic\"` on the controller file returns zero matches).\n- The most recent related fix in this file, commit `0b74fc07` (\"fix: flowise-603\", 2026-08-07 \u2014 the fix for the previously reported CVE-2026-67622 credential-confused-deputy issue), added workspace scoping to three sibling handlers in the same file (`getAllOpenaiAssistants`, `getSingleOpenaiAssistant`, `uploadAssistantFiles`) but did not touch `getFileFromAssistant`.\n\n## 3. Technical Details\n\n### 3.1 Authentication bypass (whitelist)\n\n`packages/server/src/utils/constants.ts`:\n```ts\nexport const WHITELIST_URLS = [\n    ...\n    '/api/v1/openai-assistants-file/download',\n    ...\n]\n```\n\n`packages/server/src/index.ts` (global auth middleware):\n```ts\nconst isWhitelisted = whitelistURLs.some((url) =&gt; req.path.startsWith(url))\nif (isWhitelisted) {\n    next()   // skips verifyToken() (session/JWT) and validateAPIKey() entirely\n}\n```\n\n### 3.2 Route has no permission middleware\n\n`packages/server/src/routes/openai-assistants-files/index.ts` \u2014 contrast with the sibling `upload/` route on the same router, which *does* enforce RBAC:\n```ts\nrouter.post('/download/', openaiAssistantsController.getFileFromAssistant)\n\nrouter.post(\n    '/upload/',\n    checkAnyPermission('assistants:create,assistants:update'),\n    getMulterStorage().array('files'),\n    openaiAssistantsController.uploadAssistantFiles\n)\n```\n\n### 3.3 Controller performs no ownership check\n\n`packages/server/src/controllers/openai-assistants/index.ts`, `getFileFromAssistant`:\n```ts\nconst getFileFromAssistant = async (req: Request, res: Response, next: NextFunction) =&gt; {\n    const chatflowId = req.body.chatflowId as string\n    const chatId = req.body.chatId as string\n    const fileName = req.body.fileName as string\n\n    // \"This can be public API, so we can only get orgId from the chatflow\"\n    const chatflow = await appServer.AppDataSource.getRepository(ChatFlow).findOneBy({ id: chatflowId })\n    // no isPublic check\n    // no req.user / workspace check\n\n    const orgId = workspace.organizationId as string\n    res.setHeader('Content-Disposition', contentDisposition(fileName))\n    const fileStream = await streamStorageFile(chatflowId, chatId, fileName, orgId)\n    fileStream.pipe(res)\n}\n```\n\nThe comment in the source (`\"This can be public API...\"`) shows the author was aware this handler must operate without `req.user`, but never added the corresponding public/private gate.\n\n### 3.4 The codebase's own correct pattern for this exact situation\n\n`packages/server/src/controllers/chatflows/index.ts`, `getSinglePublicChatflow` \u2014 the pattern that should have been applied to `getFileFromAssistant`:\n```ts\nconst chatflow = await chatflowsService.getChatflowById(req.params.id)\nif (!chatflow) return res.status(404).json({ message: 'Chatflow not found' })\nif (chatflow.isPublic)\n    return res.status(200).json({ ...chatflow, flowData: sanitizeFlowDataForPublicEndpoint(chatflow.flowData) })\nif (!req.user) return res.status(401).json({ message: 'Unauthorized' })\nif (!workspaceIds.includes(chatflow.workspaceId))\n    return res.status(400).json({ message: 'You are not in the workspace that owns this chatflow' })\n```\nThis is the exact three-step check \u2014 (1) `chatflow.isPublic`, (2) `req.user` presence, (3) `workspaceIds.includes(chatflow.workspaceId)` \u2014 that `getFileFromAssistant` omits entirely. Its presence elsewhere in the same codebase demonstrates this is an oversight, not an intentional design decision.\n\n### 3.5 Ruled out: this is not a path-traversal bug\n\n`packages/components/src/storage/BaseStorageProvider.ts` (`sanitizeFilename`, `validatePathSecurity`) and `LocalStorageProvider.streamStorageFile` validate all three path components and reject `..`/absolute-path escape attempts before touching the filesystem. This closes off the vector exploited by the previously fixed GHSA-99pg-hqvx-r4gf / GHSA-q67q-549q-p849. The finding in this report requires no traversal \u2014 it retrieves a file from exactly the path the application itself would use, for a resource the caller has no authorization to access.\n\n### 3.6 Legitimate file-creation path (storage layout is not test-only)\n\nThe PoC in Section 4 places the victim file directly on disk as a deterministic test fixture rather than through a real chat interaction. This section establishes that the fixture's location is not artificial: it is exactly the layout Flowise's own, ordinary chat-attachment upload workflow writes to.\n\n`packages/server/src/utils/buildChatflow.ts`, in the request-handling path for chat messages that include file/image/audio uploads (`uploads`), including on public-facing chatflows with no authentication required to converse:\n```ts\nconst { totalSize } = await addSingleFileToStorage(mime, bf, filename, orgId, chatflowid, chatId)\n```\n`addSingleFileToStorage(mime, buffer, filename, orgId, chatflowid, chatId)` writes to `////` \u2014 the identical path structure `streamStorageFile` reads from in the vulnerable handler (Section 3.3):\n```\nLegitimate file creation:                     Vulnerable retrieval:\nbuildChatflow.ts (chat upload handling)       getFileFromAssistant()\n  -&gt; addSingleFileToStorage(                    -&gt; streamStorageFile(\n      mime, buffer, filename,                       chatflowId, chatId,\n      orgId, chatflowid, chatId)                     fileName, orgId)\n  -&gt; writes ///  -&gt; reads the same path\n```\nThe file in the PoC was placed directly only to create a deterministic test fixture; its path and naming layout exactly match the storage layout produced by this legitimate, unauthenticated-reachable upload workflow.\n\n### 3.7 Identifier exposure and attack prerequisites\n\nThe identifiers consumed by the vulnerable endpoint \u2014 `chatflowId`, `chatId`, `fileName` \u2014 are ordinary client-side resource locators, not authorization credentials or server-issued capability tokens:\n\n- `chatId` is generated client-side (`const [chatId, setChatId] = useState(uuidv4())`, `packages/ui/src/views/chatmessage/ChatMessage.jsx:213`) and persisted via `localStorage.setItem` (`packages/ui/src/utils/genericHelper.js:889`, `setLocalStorageChatflow`) \u2014 it is never issued or cryptographically bound to a session by the server.\n- The official frontend embeds `chatflowId`, `chatId`, and `fileName` directly, in plaintext, in file URLs it renders in the chat UI, e.g. `${baseURL}/api/v1/get-upload-file?chatflowId=${chatflowid}&amp;chatId=${chatId}&amp;fileName=${...}` (`ChatMessage.jsx:719, 1338, 1349`).\n\n`chatId` is not an authorization credential and is not cryptographically bound to the requester's identity or session. Consequently, possession of a `chatflowId`/`chatId`/`fileName` tuple must not be treated by the server as proof of authorization \u2014 yet `getFileFromAssistant` treats it as sufficient. Because these values are exposed to the browser DOM and network layer by ordinary use of the product, they may consequently appear in client-side logs, reverse-proxy or gateway logs, monitoring/APM systems, shared HTML, or diagnostic material such as exported conversations or bug reports.\n\n**What this evidence does *not* establish:** observing that identifiers for a given (e.g. public) chatflow are exposed in that chatflow's own UI does not by itself demonstrate that an attacker can obtain the tuple for an *arbitrary, unrelated private* target chatflow. This report does not claim that. The exploitation prerequisite is stated precisely as:\n\n&gt; Exploitation requires knowledge of the exact `chatflowId`/`chatId`/`fileName` tuple for the target file. This report does not rely on guessing, brute-forcing, path traversal, or bypassing UUID validation to obtain that tuple \u2014 Section 3.5 confirms no traversal path exists, and none is needed once the tuple is known. How a specific attacker obtains a specific target's tuple is scenario-dependent and outside the scope of this report; the defect is that the server performs no authorization check at all once the tuple is presented, regardless of how it was obtained.\n\n## 4. Proof of Concept\n\nReproduced end-to-end against the **official `flowise@3.1.4` release tag**, built and run via the project's own `Dockerfile` (`docker build -t flowise-local:3.1.4 .`), default configuration (local file storage, SQLite).\n\n**Scope of this PoC:** the following is directly reproduced against a single private chatflow within a single organization/workspace \u2014 a second tenant was not separately stood up. Cross-workspace/cross-organization impact is established by the handler's own logic, not by this specific run (see Section 1).\n\n### 4.1 Setup\n\n1. Created a workspace-owner account via the normal setup UI.\n2. `POST /api/v1/chatflows`, authenticated, `isPublic: false` \u2014 created the private target chatflow (`id = 3055de1b-586b-4756-9e77-da2c19b62f95`, `workspaceId = 53141120-b356-452a-b34d-28549fdbab9d`, `organizationId = b2439594-7c21-4bf7-86b6-b1d90ffd40b8`).\n3. A file was placed at the exact path the application itself uses for chat-generated/uploaded files \u2014 `////` \u2014 as a deterministic test fixture matching the legitimate upload workflow traced in Section 3.6.\n\n### 4.2 Request A \u2014 Control\n\nSent with **no** `Cookie`/`Authorization` header, targeting the equivalent metadata-read endpoint for the same resource:\n```\nGET /api/v1/chatflows/3055de1b-586b-4756-9e77-da2c19b62f95 HTTP/1.1\nHost: localhost:3000\n```\n**Response:** `HTTP/1.1 401 Unauthorized`\n```json\n{\"message\":\"Invalid or Missing token\"}\n```\nThis confirms the platform *does* enforce authentication on the equivalent, properly-guarded endpoint for the identical resource.\n\n### 4.3 Request B \u2014 Exploit\n\nSent with **no** `Cookie`/`Authorization` header at all, against the vulnerable endpoint:\n```\nPOST /api/v1/openai-assistants-file/download HTTP/1.1\nHost: localhost:3000\nContent-Type: application/json\nContent-Length: 123\n\n{\"chatflowId\":\"3055de1b-586b-4756-9e77-da2c19b62f95\",\"chatId\":\"e29b8f3a-victim-chat-01\",\"fileName\":\"secret-report.txt\"}\n```\n**Response:** `HTTP/1.1 200 OK`\n```\nContent-Disposition: attachment; filename=\"secret-report.txt\"\n\nCONFIDENTIAL - victim workspace uploaded file - should NOT be downloadable without auth\n```\n\nRequest A (targeting the exact same private resource, via the properly-guarded metadata endpoint) was correctly rejected; Request B (targeting the file-download endpoint) was not \u2014 isolating the defect to this specific handler rather than a global authentication failure.\n\n### 4.4 Additional confirmation \u2014 full unauthenticated identifier-acquisition chain\n\nIn a related finding against the same instance, the sibling whitelisted endpoint `GET /api/v1/feedback/:chatflowId` (`packages/server/src/controllers/feedback/index.ts`, `packages/server/src/utils/getChatMessageFeedback.ts`) was found to return `ChatMessageFeedback` records \u2014 including the real `chatId` for the chatflow \u2014 for **any** `chatflowId`, with the same missing-authorization pattern and no additional identifiers required:\n```\nGET /api/v1/feedback/3055de1b-586b-4756-9e77-da2c19b62f95 HTTP/1.1\nHost: localhost:3000\n```\n```json\n[{\"id\":\"...\",\"chatflowid\":\"3055de1b-...\",\"chatId\":\"e29b8f3a-victim-chat-01\",\"messageId\":\"...\",\"rating\":\"THUMBS_DOWN\",\"content\":\"...\",\"createdDate\":\"...\"}]\n```\nThe `chatId` returned by this unauthenticated request was then used, unmodified, in Request B above and successfully retrieved the file \u2014 demonstrating that an attacker starting with **only a `chatflowId`** (routinely exposed in public chat-widget embed snippets by design) can obtain the remaining identifiers and exploit CVE-2026-71962 end-to-end without any out-of-band information.\n\n## 5. Impact\n\n- **Confidentiality (High):** Disclosure of user-uploaded and AI/tool-generated files belonging to any chatflow on the instance \u2014 including private, non-public chatflows in other workspaces/organizations \u2014 without any credentials, once the target's identifier tuple is known (Section 3.7).\n- **Scope:** Because `getFileFromAssistant` resolves the target organization/workspace from the *supplied* `chatflowId` rather than from the requester, the tenant-isolation boundary for file storage provides no protection against this endpoint on any multi-tenant deployment (Flowise Cloud, or self-hosted instances serving multiple organizations/workspaces).\n- **Attack complexity \u2014 CVSS rationale:** Once a target file's `chatflowId`/`chatId`/`fileName` tuple is known, exploitation requires exactly one unauthenticated HTTP request, with no race condition, no brute-forcing or guessing of the identifiers, no cryptographic bypass, no path traversal or UUID-validation bypass (Section 3.5), and no unusual or rare deployment condition. Section 4.4 additionally demonstrates a fully self-contained, unauthenticated identifier-acquisition chain requiring only a `chatflowId` to start.\n\n## 6. Suggested Remediation\n\n*Illustrative remediation based on the membership pattern already used by `getSinglePublicChatflow` \u2014 not a compile-tested, drop-in patch.* Apply the same authorization gate already used elsewhere in the codebase to `getFileFromAssistant`. Note this deliberately checks **workspace membership**, not merely the requester's *currently active* workspace (`req.user.activeWorkspaceId`):\n```ts\nconst chatflow = await appServer.AppDataSource.getRepository(ChatFlow).findOneBy({ id: chatflowId })\nif (!chatflow) throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Chatflow ${chatflowId} not found`)\n\nif (!chatflow.isPublic) {\n    if (!req.user) return res.status(StatusCodes.UNAUTHORIZED).json({ message: 'Unauthorized' })\n\n    const workspaceUser = await workspaceUserService.readWorkspaceUserByUserId(req.user.id, queryRunner)\n    const workspaceIds = workspaceUser.map((wu) =&gt; wu.workspaceId)\n    if (!workspaceIds.includes(chatflow.workspaceId)) {\n        return res.status(StatusCodes.FORBIDDEN).json({ message: 'Forbidden' })\n    }\n}\n```\nSuggested status-code convention: `401` when no valid credentials are presented at all, `403` once authenticated but lacking the required workspace membership. As defense in depth, consider binding `chatId` to a server-issued, per-session token rather than trusting a bare client-supplied UUID, even for legitimately public chatflows. The same fix should be applied to `GET /api/v1/feedback/:chatflowId` (Section 4.4).\n\n## 7. Prior Art / Duplicate Check\n\nCross-referenced against all publicly indexed Flowise CVEs and GitHub Security Advisories as of 2026-08-09 that relate to this endpoint or file-storage authorization in general:\n\n| Advisory | Endpoint | Root cause | Relation to this report |\n|---|---|---|---|\n| GHSA-99pg-hqvx-r4gf | same endpoint | Path traversal via unvalidated `chatId` (reads `database.sqlite`) | Different defect, fixed in 3.0.6; traversal guard confirmed present and effective in the version tested here |\n| GHSA-q67q-549q-p849 | same endpoint + `get-upload-file` | Path traversal, `chatflowId` not UUID-validated | Different defect, fixed in 3.0.6 |\n| CVE-2026-67622 | `openai-assistants` (list/retrieve) | Confused-deputy via client-controlled `credential` UUID | Different endpoint and mechanism \u2014 uses OpenAI's own API via a stolen credential reference |\n| CVE-2026-70472 | `openai-assistants-vector-store` | Same credential-UUID confused-deputy pattern | Different endpoint/mechanism |\n| CVE-2026-69252 | `/api/v1/files` (GET/DELETE) | Missing `checkPermission` for authenticated API-key callers | Different endpoint; requires a valid API key (`PR:L`), this finding requires none (`PR:N`) |\n| CVE-2026-70473 | `/upsert-history` | Unscoped, server-wide history disclosure | Different endpoint/data class |\n\nNo existing advisory describes a missing-`isPublic`/ownership check on `openai-assistants-file/download` distinct from the path-traversal issue. A relevant architectural precedent is CVE-2026-69258 (`POST /api/v1/prediction/:id`, also whitelisted by design, CVSS 8.2) \u2014 an intentionally public/whitelisted endpoint later found to have a specific, narrower authorization gap within that intended public surface, similar in shape to the finding reported here.\n\n## 8. Disclosure Timeline\n\n- **2026-08-09** \u2014 Vulnerability identified via source review of the official `flowise@3.1.4` release tag.\n- **2026-08-09** \u2014 Live-reproduced end-to-end against a Docker build of the same tag.\n- **2026-08-09** \u2014 Reported to VulnCheck (disclosures@vulncheck.com).\n- **2026-08-10** \u2014 VulnCheck confirmed and reserved **CVE-2026-71962**.\n- **Vendor status:** FlowiseAI/Flowise's `SECURITY.md` states the project is being sunset and is no longer accepting new vulnerability reports; the repository was scheduled for GitHub archival (2026-08-10 per the project's published sunset notice: https://flowiseai.com/sunset). Standard vendor-coordinated disclosure via GitHub Security Advisories was therefore not available, and this report was submitted through VulnCheck as a CVE Numbering Authority that does not require vendor participation.\n\n## 9. Credit\n\nDiscovered and reported by **Mai Hai Dang** (haidang.infosec@gmail.com).", "creation_timestamp": "2026-08-10T17:18:09.839678Z"}, {"uuid": "67e7d594-8b5c-436d-bb28-c6aeafa5633f", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "CVE-2026-70473", "type": "seen", "source": "https://gist.github.com/haidang-infosec/402db84bee7aca2f57bb109b31574649", "content": "# CVE-2026-71962: Unauthenticated Cross-Tenant File Disclosure in Flowise via `openai-assistants-file/download`\n\n**CVE ID:** CVE-2026-71962\n**CNA:** VulnCheck\n**Author:** Mai Hai Dang (haidang.infosec@gmail.com)\n**Report date:** 2026-08-09\n**Product:** Flowise ([FlowiseAI/Flowise](https://github.com/FlowiseAI/Flowise), npm package `flowise`)\n**Affected component:** `packages/server` \u2014 REST API\n**Vulnerability class:** Missing Authorization / Insecure Direct Object Reference (IDOR)\n**CWE:** CWE-862 (Missing Authorization) \u2014 primary; CWE-639 (Authorization Bypass Through User-Controlled Key) \u2014 secondary\n**CVSS 3.1:** `CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N` \u2014 **7.5 (High)**\n**CVSS 4.0:** `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` \u2014 **8.7 (High)**\n\n---\n\n## 1. Summary\n\nThe Flowise REST API endpoint `POST /api/v1/openai-assistants-file/download` is served without any authentication check \u2014 it is listed in the server's global `WHITELIST_URLS`, which bypasses both session-cookie and API-key authentication for any request whose path matches it. The handler behind this endpoint (`getFileFromAssistant`) resolves and streams a file from the server's file storage (local disk or S3, depending on deployment) using three client-supplied identifiers \u2014 `chatflowId`, `chatId`, `fileName` \u2014 without ever verifying that the target chatflow is public (`isPublic: true`) or that the requester belongs to the workspace that owns it.\n\nAn unauthenticated requester who knows a valid `chatflowId`/`chatId`/`fileName` tuple can retrieve the corresponding stored file without any authentication or workspace-membership check. This was directly reproduced against a private chatflow (Section 4). Cross-workspace/cross-organization impact is not separately runtime-tested with two tenants, but follows directly from the handler's data flow: `getFileFromAssistant` resolves the owning workspace and organization from the *supplied* `chatflowId` alone (Section 3.3) and never evaluates the requester's identity, so the same zero-authentication request works identically regardless of which workspace or organization the target chatflow belongs to.\n\nThis is a distinct defect from the previously disclosed and patched path-traversal vulnerabilities affecting the same endpoint (GHSA-99pg-hqvx-r4gf, GHSA-q67q-549q-p849); those were fixed by adding strict path-traversal validation to `chatflowId`/`chatId`/`fileName`, which is confirmed present and effective in the version tested. The defect reported here is a pure authorization gap: the request never leaves the *intended* storage path, it simply is never checked for ownership.\n\n## 2. Affected Versions\n\n| Scope | Status |\n|---|---|\n| npm `flowise` `3.1.4` (latest published release, git tag `flowise@3.1.4` = commit `a65f81bb`, 2026-07-29) | Confirmed by runtime reproduction (Section 4) |\n| Current public GitHub `main` branch HEAD | Confirmed by source inspection (code path unchanged relative to `3.1.4`) |\n| npm `flowise` `2.2.4` \u2013 `3.1.3` | Presumed affected, based on `git log`/`git blame` showing the vulnerable code has been present, unmodified, since `2.2.4`; not individually runtime-tested per version |\n\nThe PoC in this report was reproduced by checking out the official release tag directly (`git checkout flowise@3.1.4`), removing any ambiguity about whether the finding applies to unreleased code.\n\n**Version history (established via `git log`/`git blame` against the public repository):**\n- The `WHITELIST_URLS` entry for this exact path was introduced in commit `8d266052` (\"Feature/update upsert API\"), dated 2025-01-09, first shipped in release `2.2.4`. `git log -S` against the literal route string shows this is the only commit that has ever touched it \u2014 it has never been removed or narrowed.\n- The `getFileFromAssistant` handler has never, at any point in its history, contained an `isPublic`, `req.user`, or workspace-membership check (`git log -S\"isPublic\"` on the controller file returns zero matches).\n- The most recent related fix in this file, commit `0b74fc07` (\"fix: flowise-603\", 2026-08-07 \u2014 the fix for the previously reported CVE-2026-67622 credential-confused-deputy issue), added workspace scoping to three sibling handlers in the same file (`getAllOpenaiAssistants`, `getSingleOpenaiAssistant`, `uploadAssistantFiles`) but did not touch `getFileFromAssistant`.\n\n## 3. Technical Details\n\n### 3.1 Authentication bypass (whitelist)\n\n`packages/server/src/utils/constants.ts`:\n```ts\nexport const WHITELIST_URLS = [\n    ...\n    '/api/v1/openai-assistants-file/download',\n    ...\n]\n```\n\n`packages/server/src/index.ts` (global auth middleware):\n```ts\nconst isWhitelisted = whitelistURLs.some((url) =&gt; req.path.startsWith(url))\nif (isWhitelisted) {\n    next()   // skips verifyToken() (session/JWT) and validateAPIKey() entirely\n}\n```\n\n### 3.2 Route has no permission middleware\n\n`packages/server/src/routes/openai-assistants-files/index.ts` \u2014 contrast with the sibling `upload/` route on the same router, which *does* enforce RBAC:\n```ts\nrouter.post('/download/', openaiAssistantsController.getFileFromAssistant)\n\nrouter.post(\n    '/upload/',\n    checkAnyPermission('assistants:create,assistants:update'),\n    getMulterStorage().array('files'),\n    openaiAssistantsController.uploadAssistantFiles\n)\n```\n\n### 3.3 Controller performs no ownership check\n\n`packages/server/src/controllers/openai-assistants/index.ts`, `getFileFromAssistant`:\n```ts\nconst getFileFromAssistant = async (req: Request, res: Response, next: NextFunction) =&gt; {\n    const chatflowId = req.body.chatflowId as string\n    const chatId = req.body.chatId as string\n    const fileName = req.body.fileName as string\n\n    // \"This can be public API, so we can only get orgId from the chatflow\"\n    const chatflow = await appServer.AppDataSource.getRepository(ChatFlow).findOneBy({ id: chatflowId })\n    // no isPublic check\n    // no req.user / workspace check\n\n    const orgId = workspace.organizationId as string\n    res.setHeader('Content-Disposition', contentDisposition(fileName))\n    const fileStream = await streamStorageFile(chatflowId, chatId, fileName, orgId)\n    fileStream.pipe(res)\n}\n```\n\nThe comment in the source (`\"This can be public API...\"`) shows the author was aware this handler must operate without `req.user`, but never added the corresponding public/private gate.\n\n### 3.4 The codebase's own correct pattern for this exact situation\n\n`packages/server/src/controllers/chatflows/index.ts`, `getSinglePublicChatflow` \u2014 the pattern that should have been applied to `getFileFromAssistant`:\n```ts\nconst chatflow = await chatflowsService.getChatflowById(req.params.id)\nif (!chatflow) return res.status(404).json({ message: 'Chatflow not found' })\nif (chatflow.isPublic)\n    return res.status(200).json({ ...chatflow, flowData: sanitizeFlowDataForPublicEndpoint(chatflow.flowData) })\nif (!req.user) return res.status(401).json({ message: 'Unauthorized' })\nif (!workspaceIds.includes(chatflow.workspaceId))\n    return res.status(400).json({ message: 'You are not in the workspace that owns this chatflow' })\n```\nThis is the exact three-step check \u2014 (1) `chatflow.isPublic`, (2) `req.user` presence, (3) `workspaceIds.includes(chatflow.workspaceId)` \u2014 that `getFileFromAssistant` omits entirely. Its presence elsewhere in the same codebase demonstrates this is an oversight, not an intentional design decision.\n\n### 3.5 Ruled out: this is not a path-traversal bug\n\n`packages/components/src/storage/BaseStorageProvider.ts` (`sanitizeFilename`, `validatePathSecurity`) and `LocalStorageProvider.streamStorageFile` validate all three path components and reject `..`/absolute-path escape attempts before touching the filesystem. This closes off the vector exploited by the previously fixed GHSA-99pg-hqvx-r4gf / GHSA-q67q-549q-p849. The finding in this report requires no traversal \u2014 it retrieves a file from exactly the path the application itself would use, for a resource the caller has no authorization to access.\n\n### 3.6 Legitimate file-creation path (storage layout is not test-only)\n\nThe PoC in Section 4 places the victim file directly on disk as a deterministic test fixture rather than through a real chat interaction. This section establishes that the fixture's location is not artificial: it is exactly the layout Flowise's own, ordinary chat-attachment upload workflow writes to.\n\n`packages/server/src/utils/buildChatflow.ts`, in the request-handling path for chat messages that include file/image/audio uploads (`uploads`), including on public-facing chatflows with no authentication required to converse:\n```ts\nconst { totalSize } = await addSingleFileToStorage(mime, bf, filename, orgId, chatflowid, chatId)\n```\n`addSingleFileToStorage(mime, buffer, filename, orgId, chatflowid, chatId)` writes to `////` \u2014 the identical path structure `streamStorageFile` reads from in the vulnerable handler (Section 3.3):\n```\nLegitimate file creation:                     Vulnerable retrieval:\nbuildChatflow.ts (chat upload handling)       getFileFromAssistant()\n  -&gt; addSingleFileToStorage(                    -&gt; streamStorageFile(\n      mime, buffer, filename,                       chatflowId, chatId,\n      orgId, chatflowid, chatId)                     fileName, orgId)\n  -&gt; writes ///  -&gt; reads the same path\n```\nThe file in the PoC was placed directly only to create a deterministic test fixture; its path and naming layout exactly match the storage layout produced by this legitimate, unauthenticated-reachable upload workflow.\n\n### 3.7 Identifier exposure and attack prerequisites\n\nThe identifiers consumed by the vulnerable endpoint \u2014 `chatflowId`, `chatId`, `fileName` \u2014 are ordinary client-side resource locators, not authorization credentials or server-issued capability tokens:\n\n- `chatId` is generated client-side (`const [chatId, setChatId] = useState(uuidv4())`, `packages/ui/src/views/chatmessage/ChatMessage.jsx:213`) and persisted via `localStorage.setItem` (`packages/ui/src/utils/genericHelper.js:889`, `setLocalStorageChatflow`) \u2014 it is never issued or cryptographically bound to a session by the server.\n- The official frontend embeds `chatflowId`, `chatId`, and `fileName` directly, in plaintext, in file URLs it renders in the chat UI, e.g. `${baseURL}/api/v1/get-upload-file?chatflowId=${chatflowid}&amp;chatId=${chatId}&amp;fileName=${...}` (`ChatMessage.jsx:719, 1338, 1349`).\n\n`chatId` is not an authorization credential and is not cryptographically bound to the requester's identity or session. Consequently, possession of a `chatflowId`/`chatId`/`fileName` tuple must not be treated by the server as proof of authorization \u2014 yet `getFileFromAssistant` treats it as sufficient. Because these values are exposed to the browser DOM and network layer by ordinary use of the product, they may consequently appear in client-side logs, reverse-proxy or gateway logs, monitoring/APM systems, shared HTML, or diagnostic material such as exported conversations or bug reports.\n\n**What this evidence does *not* establish:** observing that identifiers for a given (e.g. public) chatflow are exposed in that chatflow's own UI does not by itself demonstrate that an attacker can obtain the tuple for an *arbitrary, unrelated private* target chatflow. This report does not claim that. The exploitation prerequisite is stated precisely as:\n\n&gt; Exploitation requires knowledge of the exact `chatflowId`/`chatId`/`fileName` tuple for the target file. This report does not rely on guessing, brute-forcing, path traversal, or bypassing UUID validation to obtain that tuple \u2014 Section 3.5 confirms no traversal path exists, and none is needed once the tuple is known. How a specific attacker obtains a specific target's tuple is scenario-dependent and outside the scope of this report; the defect is that the server performs no authorization check at all once the tuple is presented, regardless of how it was obtained.\n\n## 4. Proof of Concept\n\nReproduced end-to-end against the **official `flowise@3.1.4` release tag**, built and run via the project's own `Dockerfile` (`docker build -t flowise-local:3.1.4 .`), default configuration (local file storage, SQLite).\n\n**Scope of this PoC:** the following is directly reproduced against a single private chatflow within a single organization/workspace \u2014 a second tenant was not separately stood up. Cross-workspace/cross-organization impact is established by the handler's own logic, not by this specific run (see Section 1).\n\n### 4.1 Setup\n\n1. Created a workspace-owner account via the normal setup UI.\n2. `POST /api/v1/chatflows`, authenticated, `isPublic: false` \u2014 created the private target chatflow (`id = 3055de1b-586b-4756-9e77-da2c19b62f95`, `workspaceId = 53141120-b356-452a-b34d-28549fdbab9d`, `organizationId = b2439594-7c21-4bf7-86b6-b1d90ffd40b8`).\n3. A file was placed at the exact path the application itself uses for chat-generated/uploaded files \u2014 `////` \u2014 as a deterministic test fixture matching the legitimate upload workflow traced in Section 3.6.\n\n### 4.2 Request A \u2014 Control\n\nSent with **no** `Cookie`/`Authorization` header, targeting the equivalent metadata-read endpoint for the same resource:\n```\nGET /api/v1/chatflows/3055de1b-586b-4756-9e77-da2c19b62f95 HTTP/1.1\nHost: localhost:3000\n```\n**Response:** `HTTP/1.1 401 Unauthorized`\n```json\n{\"message\":\"Invalid or Missing token\"}\n```\nThis confirms the platform *does* enforce authentication on the equivalent, properly-guarded endpoint for the identical resource.\n\n### 4.3 Request B \u2014 Exploit\n\nSent with **no** `Cookie`/`Authorization` header at all, against the vulnerable endpoint:\n```\nPOST /api/v1/openai-assistants-file/download HTTP/1.1\nHost: localhost:3000\nContent-Type: application/json\nContent-Length: 123\n\n{\"chatflowId\":\"3055de1b-586b-4756-9e77-da2c19b62f95\",\"chatId\":\"e29b8f3a-victim-chat-01\",\"fileName\":\"secret-report.txt\"}\n```\n**Response:** `HTTP/1.1 200 OK`\n```\nContent-Disposition: attachment; filename=\"secret-report.txt\"\n\nCONFIDENTIAL - victim workspace uploaded file - should NOT be downloadable without auth\n```\n\nRequest A (targeting the exact same private resource, via the properly-guarded metadata endpoint) was correctly rejected; Request B (targeting the file-download endpoint) was not \u2014 isolating the defect to this specific handler rather than a global authentication failure.\n\n### 4.4 Additional confirmation \u2014 full unauthenticated identifier-acquisition chain\n\nIn a related finding against the same instance, the sibling whitelisted endpoint `GET /api/v1/feedback/:chatflowId` (`packages/server/src/controllers/feedback/index.ts`, `packages/server/src/utils/getChatMessageFeedback.ts`) was found to return `ChatMessageFeedback` records \u2014 including the real `chatId` for the chatflow \u2014 for **any** `chatflowId`, with the same missing-authorization pattern and no additional identifiers required:\n```\nGET /api/v1/feedback/3055de1b-586b-4756-9e77-da2c19b62f95 HTTP/1.1\nHost: localhost:3000\n```\n```json\n[{\"id\":\"...\",\"chatflowid\":\"3055de1b-...\",\"chatId\":\"e29b8f3a-victim-chat-01\",\"messageId\":\"...\",\"rating\":\"THUMBS_DOWN\",\"content\":\"...\",\"createdDate\":\"...\"}]\n```\nThe `chatId` returned by this unauthenticated request was then used, unmodified, in Request B above and successfully retrieved the file \u2014 demonstrating that an attacker starting with **only a `chatflowId`** (routinely exposed in public chat-widget embed snippets by design) can obtain the remaining identifiers and exploit CVE-2026-71962 end-to-end without any out-of-band information.\n\n## 5. Impact\n\n- **Confidentiality (High):** Disclosure of user-uploaded and AI/tool-generated files belonging to any chatflow on the instance \u2014 including private, non-public chatflows in other workspaces/organizations \u2014 without any credentials, once the target's identifier tuple is known (Section 3.7).\n- **Scope:** Because `getFileFromAssistant` resolves the target organization/workspace from the *supplied* `chatflowId` rather than from the requester, the tenant-isolation boundary for file storage provides no protection against this endpoint on any multi-tenant deployment (Flowise Cloud, or self-hosted instances serving multiple organizations/workspaces).\n- **Attack complexity \u2014 CVSS rationale:** Once a target file's `chatflowId`/`chatId`/`fileName` tuple is known, exploitation requires exactly one unauthenticated HTTP request, with no race condition, no brute-forcing or guessing of the identifiers, no cryptographic bypass, no path traversal or UUID-validation bypass (Section 3.5), and no unusual or rare deployment condition. Section 4.4 additionally demonstrates a fully self-contained, unauthenticated identifier-acquisition chain requiring only a `chatflowId` to start.\n\n## 6. Suggested Remediation\n\n*Illustrative remediation based on the membership pattern already used by `getSinglePublicChatflow` \u2014 not a compile-tested, drop-in patch.* Apply the same authorization gate already used elsewhere in the codebase to `getFileFromAssistant`. Note this deliberately checks **workspace membership**, not merely the requester's *currently active* workspace (`req.user.activeWorkspaceId`):\n```ts\nconst chatflow = await appServer.AppDataSource.getRepository(ChatFlow).findOneBy({ id: chatflowId })\nif (!chatflow) throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Chatflow ${chatflowId} not found`)\n\nif (!chatflow.isPublic) {\n    if (!req.user) return res.status(StatusCodes.UNAUTHORIZED).json({ message: 'Unauthorized' })\n\n    const workspaceUser = await workspaceUserService.readWorkspaceUserByUserId(req.user.id, queryRunner)\n    const workspaceIds = workspaceUser.map((wu) =&gt; wu.workspaceId)\n    if (!workspaceIds.includes(chatflow.workspaceId)) {\n        return res.status(StatusCodes.FORBIDDEN).json({ message: 'Forbidden' })\n    }\n}\n```\nSuggested status-code convention: `401` when no valid credentials are presented at all, `403` once authenticated but lacking the required workspace membership. As defense in depth, consider binding `chatId` to a server-issued, per-session token rather than trusting a bare client-supplied UUID, even for legitimately public chatflows. The same fix should be applied to `GET /api/v1/feedback/:chatflowId` (Section 4.4).\n\n## 7. Prior Art / Duplicate Check\n\nCross-referenced against all publicly indexed Flowise CVEs and GitHub Security Advisories as of 2026-08-09 that relate to this endpoint or file-storage authorization in general:\n\n| Advisory | Endpoint | Root cause | Relation to this report |\n|---|---|---|---|\n| GHSA-99pg-hqvx-r4gf | same endpoint | Path traversal via unvalidated `chatId` (reads `database.sqlite`) | Different defect, fixed in 3.0.6; traversal guard confirmed present and effective in the version tested here |\n| GHSA-q67q-549q-p849 | same endpoint + `get-upload-file` | Path traversal, `chatflowId` not UUID-validated | Different defect, fixed in 3.0.6 |\n| CVE-2026-67622 | `openai-assistants` (list/retrieve) | Confused-deputy via client-controlled `credential` UUID | Different endpoint and mechanism \u2014 uses OpenAI's own API via a stolen credential reference |\n| CVE-2026-70472 | `openai-assistants-vector-store` | Same credential-UUID confused-deputy pattern | Different endpoint/mechanism |\n| CVE-2026-69252 | `/api/v1/files` (GET/DELETE) | Missing `checkPermission` for authenticated API-key callers | Different endpoint; requires a valid API key (`PR:L`), this finding requires none (`PR:N`) |\n| CVE-2026-70473 | `/upsert-history` | Unscoped, server-wide history disclosure | Different endpoint/data class |\n\nNo existing advisory describes a missing-`isPublic`/ownership check on `openai-assistants-file/download` distinct from the path-traversal issue. A relevant architectural precedent is CVE-2026-69258 (`POST /api/v1/prediction/:id`, also whitelisted by design, CVSS 8.2) \u2014 an intentionally public/whitelisted endpoint later found to have a specific, narrower authorization gap within that intended public surface, similar in shape to the finding reported here.\n\n## 8. Disclosure Timeline\n\n- **2026-08-09** \u2014 Vulnerability identified via source review of the official `flowise@3.1.4` release tag.\n- **2026-08-09** \u2014 Live-reproduced end-to-end against a Docker build of the same tag.\n- **2026-08-09** \u2014 Reported to VulnCheck (disclosures@vulncheck.com).\n- **2026-08-10** \u2014 VulnCheck confirmed and reserved **CVE-2026-71962**.\n- **Vendor status:** FlowiseAI/Flowise's `SECURITY.md` states the project is being sunset and is no longer accepting new vulnerability reports; the repository was scheduled for GitHub archival (2026-08-10 per the project's published sunset notice: https://flowiseai.com/sunset). Standard vendor-coordinated disclosure via GitHub Security Advisories was therefore not available, and this report was submitted through VulnCheck as a CVE Numbering Authority that does not require vendor participation.\n\n## 9. Credit\n\nDiscovered and reported by **Mai Hai Dang** (haidang.infosec@gmail.com).", "creation_timestamp": "2026-08-10T17:18:10.057905Z"}, {"uuid": "3d48b064-6075-4c17-a9d2-46b77fd1a2ec", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "86ecb4e1-bb32-44d5-9f39-8a4673af8385", "vulnerability": "CVE-2026-70476", "type": "seen", "source": "https://www.acn.gov.it/portale/w/aggiornamenti-di-sicurezza-per-flowise", "content": "", "creation_timestamp": "2026-08-06T20:45:02.566547Z"}, {"uuid": "8e035d4a-5274-4d6a-99c2-17e4cc2aabe9", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "86ecb4e1-bb32-44d5-9f39-8a4673af8385", "vulnerability": "CVE-2026-70474", "type": "seen", "source": "https://www.acn.gov.it/portale/w/aggiornamenti-di-sicurezza-per-flowise", "content": "", "creation_timestamp": "2026-08-06T20:45:05.108582Z"}, {"uuid": "da574157-4d96-45ac-b878-b1d4303a404a", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "86ecb4e1-bb32-44d5-9f39-8a4673af8385", "vulnerability": "CVE-2026-70473", "type": "seen", "source": "https://www.acn.gov.it/portale/w/aggiornamenti-di-sicurezza-per-flowise", "content": "", "creation_timestamp": "2026-08-06T20:45:07.455562Z"}, {"uuid": "79f18cc1-c20a-43b8-ac90-f6f2989995a5", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "86ecb4e1-bb32-44d5-9f39-8a4673af8385", "vulnerability": "CVE-2026-70470", "type": "seen", "source": "https://www.acn.gov.it/portale/w/aggiornamenti-di-sicurezza-per-flowise", "content": "", "creation_timestamp": "2026-08-06T20:45:09.726910Z"}, {"uuid": "0cbe93e5-f783-438f-ab4c-2868be824138", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "86ecb4e1-bb32-44d5-9f39-8a4673af8385", "vulnerability": "CVE-2026-70475", "type": "seen", "source": "https://www.acn.gov.it/portale/w/aggiornamenti-di-sicurezza-per-flowise", "content": "", "creation_timestamp": "2026-08-06T20:45:28.135014Z"}, {"uuid": "e2645382-2cd4-4d3a-aef9-cafc6944d6b4", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "86ecb4e1-bb32-44d5-9f39-8a4673af8385", "vulnerability": "CVE-2026-70478", "type": "seen", "source": "https://www.acn.gov.it/portale/w/aggiornamenti-di-sicurezza-per-flowise", "content": "", "creation_timestamp": "2026-08-06T20:45:23.664197Z"}, {"uuid": "cdda387a-64b4-4420-bd61-7aad6097486f", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "86ecb4e1-bb32-44d5-9f39-8a4673af8385", "vulnerability": "CVE-2026-70477", "type": "seen", "source": "https://www.acn.gov.it/portale/w/aggiornamenti-di-sicurezza-per-flowise", "content": "", "creation_timestamp": "2026-08-06T20:45:25.907952Z"}, {"uuid": "11458ef3-2453-45d0-9526-55343a261202", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "86ecb4e1-bb32-44d5-9f39-8a4673af8385", "vulnerability": "CVE-2026-70472", "type": "seen", "source": "https://www.acn.gov.it/portale/w/aggiornamenti-di-sicurezza-per-flowise", "content": "", "creation_timestamp": "2026-08-06T20:45:30.398007Z"}, {"uuid": "1430af44-af06-4c0a-b9b0-29d5e6d7b4a0", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "86ecb4e1-bb32-44d5-9f39-8a4673af8385", "vulnerability": "CVE-2026-70471", "type": "seen", "source": "https://www.acn.gov.it/portale/w/aggiornamenti-di-sicurezza-per-flowise", "content": "", "creation_timestamp": "2026-08-06T20:45:32.648547Z"}]}