praisonai-platform: Any workspace member can promote themselves or others to owner via PATCH /workspaces/{id}/members/{user_id}
Date: 2026-05
Affected: pip/praisonai-platform
Attack vector: auth-bypass
Category: vulnerability-disclosure
Severity: Critical
CVE: CVE-2026-47416
CWE: CWE-269
CWE-862
CVSS: 9.6 (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:N)

## Summary **Type:** Vertical privilege escalation. The `PATCH /workspaces/{workspace_id}/members/{user_id}` endpoint is gated by `require_workspace_member(workspace_id)`, which defaults to `min_role="member"` and is never overridden by the route. The handler then calls `MemberService.update_role(workspace_id, user_id, body.role)` which sets the target member's role to whatever the request body specifies, with no check that the caller has owner-or-admin privilege, no check that the new role is not higher than the caller's own, and no check that the caller is not silently promoting themselves. **File:** `src/praisonai-platform/praisonai_platform/api/routes/workspaces.py`, lines 115-127; `services/member_service.py`, lines 55-69; `api/deps.py`, lines 54-73. **Root cause:** `require_workspace_member` exists with a `min_role` parameter (deps.py:58) but FastAPI's `Depends(require_workspace_member)` cannot pass arguments, so every route uses the default `"member"`. The route then passes the URL-supplied `user_id` and the body-supplied `role` directly to `MemberService.update_role`, which contains zero permission checks: it loads the member by composite key and assigns `member.role = new_role`. A user with the lowest possible privilege ("member") thus sets their own role to "owner" with one HTTP PATCH, completing a member-to-owner privilege escalation in a single request. ## Affected Code **File 1:** `src/praisonai-platform/praisonai_platform/api/routes/workspaces.py`, lines 115-


Mitigations:

Architectural layers (MAESTRO):