CVE-2026-47409

HIGHPre-NVD 8.18.1
EchelonGraph scoreLOW confidence

This high-severity CVE scores 8.1 under the CNA's CVSS (NVD's own analysis pending). EPSS exploit probability: 0.0%, top 87% of all CVEs by exploit prediction. GitHub Security Advisory data not yet ingested — confidence will rise once GHSA publishes (typical lag: hours to days for open-source ecosystem CVEs; never for infrastructure-only CVEs).

Triggered by: NVD CVSS baseline
Sources: cna:github_m, epss
8.1
EchelonGraph verdictPlan a fixSerious severity, but no confirmed exploitation yet.
  • High severity, but no confirmed exploitation yet
CISA-KEV: Not listedEPSS: 0%CVSS: 8.1Exploit: NoneExposed: 0

No vendor fix yet — apply a workaround or compensating control (WAF / firewall / segmentation) and watch for a patch.

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/ 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 W with role "member". State: attacker holds JWT.
  • 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.
  • Attacker sends DELETE /workspaces/W/members/O_id with Authorization: Bearer . State: control flow enters remove_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/... and require_workspace_member(W, O_id) returns 403. State: legitimate owner is now locked out of their own workspace.
  • 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.
  • 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.

CVSS v3
8.1
EG Score
8.1(low)
EPSS
12.8%
KEV
Not listed

Published

May 29, 2026

Last Modified

May 29, 2026

Vendor Advisories for CVE-2026-47409(1)

These vendors published their own advisory mentioning this CVE — often with vendor-specific remediation steps + affected product lists not in NVD.

Affected Packages

(1 across 1 ecosystem)
PyPI(1)
PackageVulnerable rangeFixed inDependents
praisonai-platform0.1.0, 0.1.1, 0.1.2, 0.1.30.1.4

Data Freshness Timeline

(refreshed 3× in last 7d / 26× in last 30d)

Each row is a source pipeline that fetched or updated this CVE on that date, with what changed. For example, "NVD update" means NVD published or revised its analysis for this CVE; "MITRE cvelistV5" means we ingested or refreshed it from the CNA feed. Most recent first.

  1. 2026-07-16 01:45 UTCEG score recompute
  2. 2026-07-14 13:43 UTCEG score recompute
  3. 2026-07-11 16:52 UTCEG score recompute
  4. 2026-07-08 17:13 UTCEG score recompute
  5. 2026-07-02 15:58 UTCEG score recompute
  6. 2026-07-02 03:41 UTCEG score recompute
  7. 2026-07-01 12:44 UTCEG score recompute
  8. 2026-07-01 01:16 UTCEG score recompute
  9. 2026-06-30 01:05 UTCEG score recompute
  10. 2026-06-29 13:34 UTCEG score recompute
  11. 2026-06-29 02:06 UTCEG score recompute
  12. 2026-06-28 14:37 UTCEG score recompute
  13. 2026-06-28 03:08 UTCEG score recompute
  14. 2026-06-27 15:39 UTCEG score recompute
  15. 2026-06-27 04:11 UTCEG score recompute
  16. 2026-06-26 16:10 UTCEG score recompute
  17. 2026-06-26 01:48 UTCEG score recompute
  18. 2026-06-25 14:08 UTCEG score recompute
  19. 2026-06-25 02:37 UTCEG score recompute
  20. 2026-06-24 15:08 UTCEG score recompute
  21. 2026-06-24 02:40 UTCEG score recompute
  22. 2026-06-18 22:19 UTCEG score recompute
  23. 2026-06-18 10:15 UTCEG score recompute
  24. 2026-06-17 22:20 UTCEG score recompute
  25. 2026-06-17 10:46 UTCEG score recompute
Show 38 more
  1. 2026-06-16 22:41 UTCEG score recompute
  2. 2026-06-16 11:14 UTCEG score recompute
  3. 2026-06-15 23:46 UTCEG score recompute
  4. 2026-06-15 12:17 UTCEG score recompute
  5. 2026-06-14 23:35 UTCEG score recompute
  6. 2026-06-14 23:18 UTCEPSS rescore
  7. 2026-06-14 12:07 UTCEG score recompute
  8. 2026-06-14 00:39 UTCEG score recompute
  9. 2026-06-13 23:00 UTCEPSS rescore
  10. 2026-06-13 13:11 UTCEG score recompute
  11. 2026-06-13 01:42 UTCEG score recompute
  12. 2026-06-12 23:12 UTCEPSS rescore
  13. 2026-06-12 14:14 UTCEG score recompute
  14. 2026-06-12 02:46 UTCEG score recompute
  15. 2026-06-11 15:19 UTCEG score recompute
  16. 2026-06-11 03:50 UTCEG score recompute
  17. 2026-06-10 16:21 UTCEG score recompute
  18. 2026-06-10 04:54 UTCEG score recompute
  19. 2026-06-09 17:26 UTCEG score recompute
  20. 2026-06-09 05:50 UTCEG score recompute
  21. 2026-06-08 18:21 UTCEG score recompute
  22. 2026-06-08 04:40 UTCEG score recompute
  23. 2026-06-07 17:13 UTCEG score recompute
  24. 2026-06-07 05:45 UTCEG score recompute
  25. 2026-06-06 18:18 UTCEG score recompute
  26. 2026-06-06 06:50 UTCEG score recompute
  27. 2026-06-05 19:22 UTCEG score recompute
  28. 2026-06-05 07:54 UTCEG score recompute
  29. 2026-06-04 20:26 UTCEG score recompute
  30. 2026-06-04 08:58 UTCEG score recompute
  31. 2026-06-03 21:30 UTCEG score recompute
  32. 2026-06-03 10:02 UTCEG score recompute
  33. 2026-06-02 22:34 UTCEG score recompute
  34. 2026-06-02 11:06 UTCEG score recompute
  35. 2026-06-01 23:38 UTCEG score recompute
  36. 2026-06-01 12:10 UTCEG score recompute
  37. 2026-06-01 00:40 UTCEG score recompute
  38. 2026-05-29 23:13 UTCEG score recompute

Frequently asked(5)

What is CVE-2026-47409?
CVE-2026-47409 is a high vulnerability published on May 29, 2026. 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/{workspaceid}/members/{userid} endpoint is gated only by requireworkspacemember(workspaceid)…
When was CVE-2026-47409 disclosed?
CVE-2026-47409 was first published in the National Vulnerability Database on May 29, 2026. EchelonGraph re-ingests CVE updates from NVD on a 2-hour cycle, so this page reflects the latest published state.
Is CVE-2026-47409 actively exploited?
CVE-2026-47409 is not currently on CISA's Known Exploited Vulnerabilities catalog. FIRST EPSS estimates a 12.8% percentile likelihood of exploitation in the next 30 days — higher percentiles indicate greater predicted risk.
What is the CVSS score of CVE-2026-47409?
CVE-2026-47409 has a CVSS v4.0 base score of 8.1 (CNA self-assessment; NVD's own analysis pending). The EG score is currently aggregating — additional source signals are being incorporated as they become available..
How do I remediate CVE-2026-47409?
Patch to the fixed version published by the affected vendor. Where vendor advisories exist for CVE-2026-47409, EchelonGraph cross-links them in the Vendor Advisories panel below — those typically contain the canonical remediation steps, fixed version numbers, and any vendor-specific mitigations.

Dependency Blast Radius

See which npm, PyPI, Go, and Maven packages are affected by CVE-2026-47409

Explore →

Is Your Infrastructure Affected by CVE-2026-47409?

EchelonGraph automatically scans your cloud infrastructure and maps CVE exposure using blast radius analysis.