PT-2026-45063 · Pypi · Praisonai-Platform

Published

2026-05-29

·

Updated

2026-05-29

·

CVE-2026-47409

CVSS v3.1

8.1

High

VectorAV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H

Summary

Type: Authorization bypass enabling owner lockout. The DELETE /workspaces/{workspace id}/members/{user id} endpoint is gated only by require workspace member(workspace id) (default min role="member"). Any member can remove any other member, including the workspace owner, using a single DELETE. There is no caller-role check, no target-role check, no "cannot remove last owner" guard. File: src/praisonai-platform/praisonai platform/api/routes/workspaces.py, lines 130-140; services/member service.py, lines 71-78. Root cause: MemberService.remove(workspace id, user id) performs the deletion without any caller-permission check or owner-protection logic. The route accepts the URL-supplied user id and dispatches it straight through. The role hierarchy (MemberService.has role) is implemented but never invoked here. A member-tier attacker can issue DELETE .../members/<owner user id> and immediately lock the legitimate owner out of the workspace.

Affected Code

File 1: src/praisonai-platform/praisonai platform/api/routes/workspaces.py, lines 130-140.
@router.delete("/{workspace id}/members/{user id}", status code=status.HTTP 204 NO CONTENT)
async def remove member(
  workspace id: str,
  user id: str,
  user: AuthIdentity = Depends(require workspace member),     # <-- BUG: defaults to min role="member"
  session: AsyncSession = Depends(get db),
):
  member svc = MemberService(session)
  removed = await member svc.remove(workspace id, user id)    # <-- removes any member, including owner
  if not removed:
    raise HTTPException(status code=404, detail="Member not found")
File 2: src/praisonai-platform/praisonai platform/services/member service.py, lines 71-78.
async def remove(self, workspace id: str, user id: str) -> bool:
  """Remove a member from a workspace."""
  member = await self.get(workspace id, user id)
  if member is None:
    return False
  await self. session.delete(member)                # <-- BUG: no caller-role check, no last-owner protection
  await self. session.flush()
  return True
Why it's wrong: member-removal is the textbook capability that must be gated on owner role. Removing the workspace owner is a permanent denial-of-service against the legitimate owner unless another owner exists. There must be (a) a caller min-role gate of "owner" or "admin", (b) a check that prevents removing a member whose role is higher than the caller's, and (c) a check that the workspace is left with at least one owner. None of these exist.

Exploit Chain

  1. Attacker is a member of workspace W with role "member". State: attacker holds JWT.
  2. Attacker enumerates the workspace owner's user id via GET /workspaces/W/members (list members has the same default-member gate, separate finding). Owner UUID O id is now known. State: attacker holds O id.
  3. Attacker sends DELETE /workspaces/W/members/O id with Authorization: Bearer <attacker jwt>. State: control flow enters remove member.
  4. require workspace member(W, attacker) passes (attacker is a member). MemberService.remove(W, O id) deletes the owner's member row. State: Member(workspace id=W, user id=O id, role="owner") is gone.
  5. Owner attempts GET /workspaces/W/... and require workspace member(W, O id) returns 403. State: legitimate owner is now locked out of their own workspace.
  6. Combined with the update member role companion advisory, the attacker first promotes themselves to owner, then removes the legitimate owner, then has uncontested control. Combined with delete workspace, the attacker wipes the workspace after kicking the owner.
  7. Final state: with one member-level token, the attacker locks the legitimate owner out of their own workspace permanently. The owner has no recourse other than database-level admin intervention.

Security Impact

Severity: sec-high. CVSS 8.1: network attack, low complexity, low privileges, no user interaction, scope unchanged, no confidentiality, high integrity (membership table corrupted), high availability (legitimate owner cannot access their own workspace). Attacker capability: with one workspace-member token plus one DELETE request, the attacker permanently locks any other member (including the workspace owner) out of the workspace. Preconditions: praisonai-platform is deployed multi-tenant; attacker has any membership token; owner's user id is reachable via the (unauthenticated-for-member) list members endpoint. Differential: source-inspection-verified. The asymmetry between require workspace member's tunable min role parameter and this endpoint's use of the default value confirms the gap. With the suggested fix below, member-tier tokens fail the gate, and removing the workspace's last owner triggers the additional guard.

Suggested Fix

--- a/src/praisonai-platform/praisonai platform/api/routes/workspaces.py
+++ b/src/praisonai-platform/praisonai platform/api/routes/workspaces.py
@@ -130,11 +130,21 @@
 @router.delete("/{workspace id}/members/{user id}", status code=status.HTTP 204 NO CONTENT)
 async def remove member(
   workspace id: str,
   user id: str,
-  user: AuthIdentity = Depends(require workspace member),
+  user: AuthIdentity = Depends( require workspace owner),
   session: AsyncSession = Depends(get db),
 ):
   member svc = MemberService(session)
+  target = await member svc.get(workspace id, user id)
+  if target is not None and target.role == "owner":
+    # Refuse to remove the last owner.
+    owners = [m for m in await member svc.list members(workspace id) if m.role == "owner"]
+    if len(owners) <= 1:
+      raise HTTPException(status code=409, detail="Cannot remove the last workspace owner")
   removed = await member svc.remove(workspace id, user id)
   if not removed:
     raise HTTPException(status code=404, detail="Member not found")
The four companion workspace-mutation endpoints exhibit the same default-min-role gap and are filed as their own advisories.

Fix

Missing Authorization

Improper Privilege Management

Weakness Enumeration

Related Identifiers

CVE-2026-47409
GHSA-W388-2392-PX73

Affected Products

Praisonai-Platform