CVE-2026-47409
praisonai-platform: Any workspace member can remove any other member (including the owner) via DELETE /workspaces/{id}/members/{user_id}
PraisonAI Platform is the platform layer for the PraisonAI multi-agent teams system. Versions prior to 0.1.4 have an 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. PraisonAI Platform version 0.1.4 patches the issue.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- Low
- User interaction
- None
What an attacker can affect
- Confidentiality
- None
- Integrity
- High
- Availability
- High
Exploit likelihood
What each source reports
CVE List Score used
8.1
praisonai-platform: Any workspace member can remove any other member (including the owner) via DELETE /workspaces/{id}/members/{user_id}
PraisonAI Platform is the platform layer for the PraisonAI multi-agent teams system. Versions prior to 0.1.4 have an 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. PraisonAI Platform version 0.1.4 patches the issue.
- Record
CVE-2026-47409- CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H - Weakness
- CWE-269, CWE-862
- Updated
- 2026-11-02
EUVDEUVD-2026-46323
8.1
PraisonAI Platform is the platform layer for the PraisonAI multi-agent teams system. Versions prior to 0.1.4 have an 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. PraisonAI Platform version 0.1.4 patches the issue.
- Record
EUVD-2026-46323- CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-w388-2392-px73
8.1
praisonai-platform: Missing authorization on member removal enables full workspace takeover by any user regardless of role
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
- Attacker is a member of workspace
Wwith role "member". State: attacker holds JWT. - Attacker enumerates the workspace owner's
user_idviaGET /workspaces/W/members(list_members has the same default-member gate, separate finding). Owner UUIDO_idis now known. State: attacker holdsO_id. - Attacker sends
DELETE /workspaces/W/members/O_idwithAuthorization: Bearer <attacker_jwt>. State: control flow entersremove_member. 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.- Owner attempts
GET /workspaces/W/...andrequire_workspace_member(W, O_id)returns 403. State: legitimate owner is now locked out of their own workspace. - Combined with the
update_member_rolecompanion advisory, the attacker first promotes themselves to owner, then removes the legitimate owner, then has uncontested control. Combined withdelete_workspace, the attacker wipes the workspace after kicking the owner. - 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…
- Record
GHSA-w388-2392-px73- CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H - Weakness
- CWE-269, CWE-862
- Updated
- 2026-11-02
NVD
8.1
PraisonAI Platform is the platform layer for the PraisonAI multi-agent teams system. Versions prior to 0.1.4 have an 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. PraisonAI Platform version 0.1.4 patches the issue.
- Record
CVE-2026-47409- CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H - Weakness
- CWE-269, CWE-862
- Updated
- 2026-11-02
OSV
8.1
praisonai-platform: Any workspace member can remove any other member (including the owner) via DELETE /workspaces/{id}/members/{user_id}
PraisonAI Platform is the platform layer for the PraisonAI multi-agent teams system. Versions prior to 0.1.4 have an 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. PraisonAI Platform version 0.1.4 patches the issue.
- Record
CVE-2026-47409- CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H - Weakness
- CWE-269, CWE-862
- Updated
- 2026-11-02
OSVGHSA-w388-2392-px73
8.1
praisonai-platform: Missing authorization on member removal enables full workspace takeover by any user regardless of role
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
- Attacker is a member of workspace
Wwith role "member". State: attacker holds JWT. - Attacker enumerates the workspace owner's
user_idviaGET /workspaces/W/members(list_members has the same default-member gate, separate finding). Owner UUIDO_idis now known. State: attacker holdsO_id. - Attacker sends
DELETE /workspaces/W/members/O_idwithAuthorization: Bearer <attacker_jwt>. State: control flow entersremove_member. 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.- Owner attempts
GET /workspaces/W/...andrequire_workspace_member(W, O_id)returns 403. State: legitimate owner is now locked out of their own workspace. - Combined with the
update_member_rolecompanion advisory, the attacker first promotes themselves to owner, then removes the legitimate owner, then has uncontested control. Combined withdelete_workspace, the attacker wipes the workspace after kicking the owner. - 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…
- Record
GHSA-w388-2392-px73- CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H - Weakness
- CWE-269, CWE-862
- Updated
- 2026-11-02
OSVPYSEC-2026-2938
8.1
praisonai-platform: Missing authorization on member removal enables full workspace takeover by any user regardless of role
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
- Attacker is a member of workspace
Wwith role "member". State: attacker holds JWT. - Attacker enumerates the workspace owner's
user_idviaGET /workspaces/W/members(list_members has the same default-member gate, separate finding). Owner UUIDO_idis now known. State: attacker holdsO_id. - Attacker sends
DELETE /workspaces/W/members/O_idwithAuthorization: Bearer <attacker_jwt>. State: control flow entersremove_member. 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.- Owner attempts
GET /workspaces/W/...andrequire_workspace_member(W, O_id)returns 403. State: legitimate owner is now locked out of their own workspace. - Combined with the
update_member_rolecompanion advisory, the attacker first promotes themselves to owner, then removes the legitimate owner, then has uncontested control. Combined withdelete_workspace, the attacker wipes the workspace after kicking the owner. - 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…
- Record
PYSEC-2026-2938- CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H - Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
praisonai-platformPyPI |
through 0.1.2 | 0.1.4 |
praisonai-platformPyPI |
before 0.1.4, plus listed versions 0.1.00.1.10.1.20.1.3 |
0.1.4 |
History
| Recorded | Changed | Sources |
|---|---|---|
| aliases | EUVD | |
| first indexed | CVE List, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-47409 is a high vulnerability (CVSS 8.1) in Mervinpraison Praisonai-platform, published May 29, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.34%, the 28th percentile. Fixed in praisonai-platform 0.1.4.
References
github.com/MervinPraison/PraisonAI/security/advisories/GHSA-w388-2392-px73
github.com/MervinPraison/PraisonAI/pull/1686
github.com/MervinPraison/PraisonAI/commit/24385d64876577620f749957bd4814f162f4ca47
github.com/advisories/GHSA-w388-2392-px73
github.com/CVEProject/cvelistV5/tree/main/cves/2026/47xxx/CVE-2026-47409.json
nvd.nist.gov/vuln/detail/CVE-2026-47409
github.com/MervinPraison/PraisonAI
pypi.org/project/praisonai-platform
Left out here: the rest of 3 source descriptions. The JSON record has them.