PraisonAI Platform (praisonai-platform ≤ 0.1.2) allows any authenticated workspace member to permanently remove any other member—including the workspace owner—via a single DELETE request, because the removal endpoint never checks the caller's role and the role-hierarchy enforcement code present in the codebase is simply never invoked on this path. With CVSS 8.1 (High), network-accessible, low complexity, no user interaction required, and a trivial one-request exploit chain (enumerate owner ID via the member list endpoint, issue one DELETE), the barrier to exploitation is essentially zero for any disgruntled or compromised member-tier account; the same package carries 59 prior CVEs, a strong signal of structural security debt that makes companion-advisory chaining—role escalation plus workspace deletion—a realistic follow-on. There is no public exploit and it is not in CISA KEV, but the attack is deterministic and recoverable only via direct database-level admin intervention once the owner is locked out. Upgrade to praisonai-platform ≥ 0.1.4 immediately, audit workspace membership tables for unexpected owner removals, and alert on DELETE requests to `/workspaces/*/members/*` originating from non-owner tokens.
What is the risk?
High risk. Network-accessible endpoint requiring only a valid member-tier credential—the lowest privilege level in the system—with no user interaction and low attack complexity. The exploit is single-request and fully deterministic, making it trivially automatable at scale across all workspaces where an attacker holds any membership token. Integrity and availability impact are both High: ownership of the workspace is irrevocably transferred to the attacker with no self-service recovery path for the victim. The 59 prior CVEs in the same package are a material indicator of systemic security posture issues, elevating the probability of compound exploitation through companion advisories covering role escalation and workspace deletion to achieve full takeover.
Attack Kill Chain
What systems are affected?
| Package | Ecosystem | Vulnerable Range | Patched |
|---|---|---|---|
| praisonai-platform | pip | <= 0.1.2 | 0.1.4 |
Do you use praisonai-platform? You're affected.
Severity & Risk
Attack Surface
What should I do?
6 steps-
Patch immediately: upgrade praisonai-platform to ≥ 0.1.4 (the fix gates
remove_memberon_require_workspace_ownerand adds a last-owner protection check). -
Audit workspace membership tables for unexpected deletions of owner-role members, particularly those not associated with an admin-initiated action or support ticket.
-
Until patched, alert on HTTP DELETE to
/workspaces/*/members/*endpoints from tokens whose decoded role is not 'owner' or 'admin'—these requests should be blocked at the WAF or API gateway level as a compensating control. -
Review all companion workspace-mutation endpoints (add_member, update_member_role, delete_workspace) for the same default-min-role gap described in sibling advisories.
-
Rotate workspace API credentials and re-verify ownership for any tenant where suspicious member-removal activity is detected in audit logs.
-
Enforce network-level access restrictions on the workspace management API to known trusted IP ranges if immediate patching is not feasible.
Classification
Compliance Impact
This CVE is relevant to:
Frequently Asked Questions
What is CVE-2026-47409?
PraisonAI Platform (praisonai-platform ≤ 0.1.2) allows any authenticated workspace member to permanently remove any other member—including the workspace owner—via a single DELETE request, because the removal endpoint never checks the caller's role and the role-hierarchy enforcement code present in the codebase is simply never invoked on this path. With CVSS 8.1 (High), network-accessible, low complexity, no user interaction required, and a trivial one-request exploit chain (enumerate owner ID via the member list endpoint, issue one DELETE), the barrier to exploitation is essentially zero for any disgruntled or compromised member-tier account; the same package carries 59 prior CVEs, a strong signal of structural security debt that makes companion-advisory chaining—role escalation plus workspace deletion—a realistic follow-on. There is no public exploit and it is not in CISA KEV, but the attack is deterministic and recoverable only via direct database-level admin intervention once the owner is locked out. Upgrade to praisonai-platform ≥ 0.1.4 immediately, audit workspace membership tables for unexpected owner removals, and alert on DELETE requests to `/workspaces/*/members/*` originating from non-owner tokens.
Is CVE-2026-47409 actively exploited?
No confirmed active exploitation of CVE-2026-47409 has been reported, but organizations should still patch proactively.
How to fix CVE-2026-47409?
1. Patch immediately: upgrade praisonai-platform to ≥ 0.1.4 (the fix gates `remove_member` on `_require_workspace_owner` and adds a last-owner protection check). 2. Audit workspace membership tables for unexpected deletions of owner-role members, particularly those not associated with an admin-initiated action or support ticket. 3. Until patched, alert on HTTP DELETE to `/workspaces/*/members/*` endpoints from tokens whose decoded role is not 'owner' or 'admin'—these requests should be blocked at the WAF or API gateway level as a compensating control. 4. Review all companion workspace-mutation endpoints (add_member, update_member_role, delete_workspace) for the same default-min-role gap described in sibling advisories. 5. Rotate workspace API credentials and re-verify ownership for any tenant where suspicious member-removal activity is detected in audit logs. 6. Enforce network-level access restrictions on the workspace management API to known trusted IP ranges if immediate patching is not feasible.
What systems are affected by CVE-2026-47409?
This vulnerability affects the following AI/ML architecture patterns: agent frameworks, multi-tenant platforms, API endpoints.
What is the CVSS score for CVE-2026-47409?
CVE-2026-47409 has a CVSS v3.1 base score of 8.1 (HIGH).
AI Security Impact
Affected AI Architectures
MITRE ATLAS Techniques
AML.T0012 Valid Accounts AML.T0048.003 User Harm AML.T0049 Exploit Public-Facing Application AML.T0053 AI Agent Tool Invocation Compliance Controls Affected
Technical Details
Original Advisory
## 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. ```python @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. ```python 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 ```diff --- 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.
Exploitation Scenario
An attacker with a legitimate 'member' role in a shared PraisonAI workspace—a contractor, former employee, or owner of a compromised account—calls `GET /workspaces/{id}/members` (accessible by any member under the same default-gate logic) to enumerate all workspace members and retrieve the workspace owner's `user_id`. The attacker then sends `DELETE /workspaces/{id}/members/{owner_user_id}` with their member JWT. The request passes authentication (valid token), bypasses authorization (no role check exists), and the owner's membership row is deleted. The owner immediately receives HTTP 403 on any subsequent workspace request with no in-application recourse. The attacker then invokes the companion `update_member_role` advisory to promote their account to 'owner', gaining uncontested administrative control over all AI agent configurations, tool definitions, API key bindings, and orchestration pipelines in the workspace—with the original owner unable to recover access outside of direct database-level intervention by a platform administrator.
Weaknesses (CWE)
CVSS Vector
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H References
Timeline
Related Vulnerabilities
CVE-2026-47392 9.9 praisonaiagents: RCE via Python sandbox bypass
Same package: praisonai GHSA-vc46-vw85-3wvm 9.8 PraisonAI: RCE via malicious workflow YAML execution
Same package: praisonai CVE-2026-39890 9.8 PraisonAI: YAML deserialization enables unauthenticated RCE
Same package: praisonai GHSA-9qhq-v63v-fv3j 9.8 PraisonAI: RCE via MCP command injection
Same package: praisonai CVE-2026-47410 9.8 praisonai-platform: hardcoded JWT → full account takeover
Same package: praisonai