PT-2026-45485 · Pypi · Praisonai-Platform

Published

2026-06-01

·

Updated

2026-06-01

·

CVE-2026-47412

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 destructive action. The DELETE /workspaces/{workspace id} endpoint is gated only by require workspace member(workspace id) (default min role="member"). Any member of the workspace can issue a single DELETE to wipe the entire workspace, including every project, issue, comment, agent, label, and member record (cascading via the foreign-key relationships). There is no owner-role gate, no confirmation token, no soft-delete window, no recovery path. File: src/praisonai-platform/praisonai platform/api/routes/workspaces.py, lines 77-86; services/workspace service.py's delete() method. Root cause: the route uses Depends(require workspace member) which defaults to min role="member" and is never overridden. The service method WorkspaceService.delete(workspace id) performs the destructive operation without any caller-permission verification. The role hierarchy (MemberService.has role, member service.py:80-96) is implemented but unused for this endpoint.

Affected Code

File: src/praisonai-platform/praisonai platform/api/routes/workspaces.py, lines 77-86.
@router.delete("/{workspace id}", status code=status.HTTP 204 NO CONTENT)
async def delete workspace(
  workspace id: str,
  user: AuthIdentity = Depends(require workspace member),     # <-- BUG: defaults to min role="member"
  session: AsyncSession = Depends(get db),
):
  ws svc = WorkspaceService(session)
  deleted = await ws svc.delete(workspace id)           # <-- destructive, no role check
  if not deleted:
    raise HTTPException(status code=404, detail="Workspace not found")
Why it's wrong: workspace deletion is the most destructive single action in this product — it wipes every member, project, issue, comment, agent, and label belonging to the tenant. The standard convention is to gate this on owner role, ideally with a confirmation parameter (typed workspace name) and a recovery window. This endpoint does none of that. The require workspace member(min role) parameter exists precisely for this kind of tightening but is never invoked with anything other than the default.

Exploit Chain

  1. Attacker is a member of workspace W (joined via invite, signup default, or any other route into membership). State: attacker holds JWT with Member(workspace id=W, user id=attacker, role="member").
  2. Attacker sends DELETE /workspaces/W with Authorization: Bearer <attacker jwt>. State: control flow enters delete workspace.
  3. require workspace member(W, attacker) passes (attacker is a member, default min role="member" satisfied). WorkspaceService.delete(W) removes the workspace row; SQLAlchemy cascade rules drop every related row (members, projects, issues, comments, agents, labels). State: workspace W no longer exists.
  4. Final state: a low-privilege member has wiped the workspace. The legitimate owner has no recovery: no soft-delete, no audit-trail event for the deletion (the Activity log row would have been deleted too as part of the cascade). The same primitive at scale (script that DELETEs every workspace id the attacker can enumerate) becomes a multi-tenant griefing tool.

Security Impact

Severity: sec-high. CVSS 8.1: network attack, low complexity, low privileges, no user interaction, scope unchanged, no confidentiality (just destruction), high integrity (every workspace child row wiped), high availability (workspace gone for legitimate owner). Attacker capability: with one workspace-member token plus one DELETE request, the attacker irreversibly deletes the workspace and every child resource. The deletion is silent and immediate. Preconditions: praisonai-platform is deployed multi-tenant; the attacker has any membership token in the target workspace. Differential: source-inspection-verified. The asymmetry between require workspace member's clearly-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 at the dependency, the destructive action never reaches the service layer, and the endpoint returns 403 instead of 204.

Suggested Fix

--- a/src/praisonai-platform/praisonai platform/api/routes/workspaces.py
+++ b/src/praisonai-platform/praisonai platform/api/routes/workspaces.py
@@ -75,11 +75,15 @@
+def require workspace owner(workspace id: str, user, session):
+  return require workspace member(workspace id, user, session, min role="owner")
+
 @router.delete("/{workspace id}", status code=status.HTTP 204 NO CONTENT)
 async def delete workspace(
   workspace id: str,
-  user: AuthIdentity = Depends(require workspace member),
+  user: AuthIdentity = Depends( require workspace owner),
   session: AsyncSession = Depends(get db),
 ):
   ws svc = WorkspaceService(session)
   deleted = await ws svc.delete(workspace id)
   if not deleted:
     raise HTTPException(status code=404, detail="Workspace not found")
Defence-in-depth: require a typed-confirmation parameter (e.g. body {"confirm name": "<workspace name>"}) and implement a 30-day soft-delete with restore. The four companion workspace-mutation endpoints (update workspace, add member, update member role, remove member) exhibit the same default-min-role gap and are filed as their own advisories.

Fix

Improper Privilege Management

Missing Authorization

Weakness Enumeration

Related Identifiers

CVE-2026-47412
GHSA-G8RR-7RJ2-F627

Affected Products

Praisonai-Platform