CVE-2026-47418

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 90% 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: Project endpoints accept any project_id without workspace ownership check, cross-workspace read/update/delete IDOR

Summary

Type: Insecure Direct Object Reference. The project CRUD endpoints (GET / PATCH / DELETE /workspaces/{workspace_id}/projects/{project_id} and GET .../{project_id}/stats) gate access on require_workspace_member(workspace_id) only, then resolve project_id through ProjectService.get(project_id) / update(project_id, ...) / delete(project_id) / get_stats(project_id). None of these calls thread workspace_id through to constrain the lookup. A user who is a member of any workspace W1 can read, modify, delete, or read stats for projects that belong to a different workspace W2. File: src/praisonai-platform/praisonai_platform/services/project_service.py, lines 47-108; route handlers at src/praisonai-platform/praisonai_platform/api/routes/projects.py, lines 51-108. Root cause: identical to the agent and issue IDORs in this codebase. The route accepts workspace_id from URL, uses it solely for the membership gate, then calls ProjectService.get(project_id) which is session.get(Project, project_id) — a primary-key-only lookup with no workspace_id predicate. update and delete call self.get(project_id) first, inheriting the gap. get_stats likewise has no workspace check.

Affected Code

File 1: src/praisonai-platform/praisonai_platform/services/project_service.py, lines 47-108.

class ProjectService:
    ...

async def get(self, project_id: str) -> Optional[Project]: """Get project by ID.""" return await self._session.get(Project, project_id) # <-- BUG: no workspace_id predicate

async def update( self, project_id: str, ... ) -> Optional[Project]: project = await self.get(project_id) # <-- inherits the gap ...

async def delete(self, project_id: str) -> bool: project = await self.get(project_id) # <-- inherits the gap ...

async def get_stats(self, project_id: str) -> dict: ... # <-- also no workspace check; returns issue counts for any project

File 2: src/praisonai-platform/praisonai_platform/api/routes/projects.py, lines 51-108.

@router.get("/{project_id}", response_model=ProjectResponse)
async def get_project(
    workspace_id: str,
    project_id: str,
    user: AuthIdentity = Depends(require_workspace_member),
    session: AsyncSession = Depends(get_db),
):
    svc = ProjectService(session)
    project = await svc.get(project_id)                             # <-- workspace_id never threaded through
    if project is None:
        raise HTTPException(status_code=404, detail="Project not found")
    return ProjectResponse.model_validate(project)

@router.patch("/{project_id}", response_model=ProjectResponse) async def update_project(...): svc = ProjectService(session) project = await svc.update(project_id, title=body.title, ...) # <-- writes to any project in the DB

@router.delete("/{project_id}", ...) async def delete_project(...): deleted = await svc.delete(project_id) # <-- deletes any project in the DB

@router.get("/{project_id}/stats") async def project_stats(...): return await svc.get_stats(project_id) # <-- returns stats for any project in the DB

Why it's wrong: workspace_id from the route is treated as a UI hint (gates "are you in some workspace W?") rather than an authoritative predicate (should also gate "is the project you are addressing actually inside W?"). The MemberService in this same codebase uses a composite (workspace_id, user_id) key and demonstrates the safe pattern; the project service simply did not apply it.

Exploit Chain

  • Attacker registers a workspace W_attacker (where they are a member) and harvests a target project UUID P_T. Project IDs leak through the activity feed (act_svc.log records entity_id), issue records (every issue carries project_id), webhook payloads, error messages, exported issue dumps, or operator screenshots. State: attacker holds P_T.
  • Attacker authenticates and sends GET /workspaces/W_attacker/projects/P_T. require_workspace_member(W_attacker, attacker) passes. State: control flow enters get_project with workspace_id=W_attacker, project_id=P_T.
  • ProjectService.get(P_T) runs session.get(Project, "P_T"), which is SELECT * FROM projects WHERE id = 'P_T' LIMIT 1 with no workspace_id filter. The row is returned: title, description (often the project's confidential roadmap), status, lead_type, lead_id, icon, created_at, workspace_id (the foreign workspace's UUID is itself disclosed). State: response body is the JSON-serialised foreign project.
  • Attacker repeats with PATCH /workspaces/W_attacker/projects/P_T and {"title": "", "description": "", "status": "archived"}. update_project calls svc.update(P_T, ...) and mutates the foreign row. State: target project is silently re-titled, re-described, and archived.
  • Attacker calls DELETE /workspaces/W_attacker/projects/P_T to delete the foreign project entirely. State: target project is gone (every issue still referencing it now has a dangling project_id).
  • Attacker calls GET /workspaces/W_attacker/projects/P_T/stats to read aggregate issue counts (open/closed/in-progress) for the foreign project — useful for competitive intelligence even when full-issue read is not possible.
  • Final state: any attacker with one workspace-member token can enumerate, exfiltrate, rewrite, and delete every project in the multi-tenant deployment given the project UUIDs.

Security Impact

Severity: sec-high. CVSS: network attack, low complexity, low privileges, no user interaction, scope unchanged, high confidentiality (project content + cross-workspace metadata via the leaked workspace_id field), high integrity (arbitrary writes / deletes), no availability claim (issue rows survive parent-project deletion). Attacker capability: read, edit, archive, delete, and stats-fingerprint any project in the multi-tenant deployment given the project UUID. Beyond plain content disclosure, the response also includes workspace_id, allowing the attacker to map the deployment's workspace topology (which workspaces exist, which projects each owns). Preconditions: praisonai-platform is deployed multi-tenant; the attacker has any membership token; the target project's UUID is known or guessable. Differential: source-inspection-verified end-to-end. The asymmetry between ProjectService.get(project_id) (no workspace check) and MemberService.get(workspace_id, user_id) (composite key check) confirms the gap. With the suggested fix below, ProjectService.get(workspace_id, project_id) returns None for foreign-workspace projects and the route handler returns 404.

Suggested Fix

Same shape as the companion agent and issue advisories. Make the resource-lookup query include the workspace predicate; treat foreign-workspace rows as 404.

--- a/src/praisonai-platform/praisonai_platform/services/project_service.py
+++ b/src/praisonai-platform/praisonai_platform/services/project_service.py
@@ -45,9 +45,12 @@ class ProjectService:
         await self._session.flush()
         return project
  • async def get(self, project_id: str) -> Optional[Project]:
  • """Get project by ID."""
  • return await self._session.get(Project, project_id)
+ async def get(self, workspace_id: str, project_id: str) -> Optional[Project]: + """Get project by ID, scoped to a workspace.""" + stmt = select(Project).where( + Project.id == project_id, Project.workspace_id == workspace_id + ) + return (await self._session.execute(stmt)).scalar_one_or_none()

async def update( self, + workspace_id: str, project_id: str, ... ) -> Optional[Project]:

  • project = await self.get(project_id)
+ project = await self.get(workspace_id, project_id)
  • async def delete(self, project_id: str) -> bool:
+ async def delete(self, workspace_id: str, project_id: str) -> bool:
  • project = await self.get(project_id)
+ project = await self.get(workspace_id, project_id)
  • async def get_stats(self, project_id: str) -> dict:
+ async def get_stats(self, workspace_id: str, project_id: str) -> dict: + # Also constrain the underlying issue counts query by workspace_id.

Update the route handlers in routes/projects.py to thread workspace_id through every call. The same single-key-lookup pattern is filed separately for AgentService, IssueService, CommentService, and LabelService.

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

Published

June 1, 2026

Last Modified

June 1, 2026

Vendor Advisories for CVE-2026-47418(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 4× in last 7d / 37× 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-02 18:36 UTCEG score recompute
  2. 2026-07-02 03:01 UTCEG score recompute
  3. 2026-07-01 13:49 UTCEG score recompute
  4. 2026-07-01 00:39 UTCEG score recompute
  5. 2026-06-30 00:58 UTCEG score recompute
  6. 2026-06-29 11:50 UTCEG score recompute
  7. 2026-06-28 20:44 UTCEG score recompute
  8. 2026-06-28 07:36 UTCEG score recompute
  9. 2026-06-27 18:28 UTCEG score recompute
  10. 2026-06-27 05:19 UTCEG score recompute
  11. 2026-06-26 15:58 UTCEG score recompute
  12. 2026-06-25 18:45 UTCEG score recompute
  13. 2026-06-25 05:37 UTCEG score recompute
  14. 2026-06-24 15:15 UTCEG score recompute
  15. 2026-06-24 02:07 UTCEG score recompute
  16. 2026-06-18 22:02 UTCEG score recompute
  17. 2026-06-18 01:51 UTCEG score recompute
  18. 2026-06-17 12:44 UTCEG score recompute
  19. 2026-06-16 23:35 UTCEG score recompute
  20. 2026-06-16 10:28 UTCEG score recompute
  21. 2026-06-15 21:20 UTCEG score recompute
  22. 2026-06-15 08:11 UTCEG score recompute
  23. 2026-06-14 23:18 UTCEPSS rescore
  24. 2026-06-14 19:02 UTCEG score recompute
  25. 2026-06-14 05:53 UTCEG score recompute
Show 24 more
  1. 2026-06-13 16:32 UTCEG score recompute
  2. 2026-06-13 03:22 UTCEG score recompute
  3. 2026-06-12 23:12 UTCEPSS rescore
  4. 2026-06-12 14:14 UTCEG score recompute
  5. 2026-06-12 01:06 UTCEG score recompute
  6. 2026-06-11 11:58 UTCEG score recompute
  7. 2026-06-10 22:51 UTCEG score recompute
  8. 2026-06-10 09:43 UTCEG score recompute
  9. 2026-06-09 20:35 UTCEG score recompute
  10. 2026-06-09 07:26 UTCEG score recompute
  11. 2026-06-08 18:17 UTCEG score recompute
  12. 2026-06-08 04:25 UTCEG score recompute
  13. 2026-06-07 15:18 UTCEG score recompute
  14. 2026-06-07 02:10 UTCEG score recompute
  15. 2026-06-06 13:02 UTCEG score recompute
  16. 2026-06-05 23:53 UTCEG score recompute
  17. 2026-06-05 10:45 UTCEG score recompute
  18. 2026-06-04 21:38 UTCEG score recompute
  19. 2026-06-04 08:30 UTCEG score recompute
  20. 2026-06-03 19:22 UTCEG score recompute
  21. 2026-06-03 06:14 UTCEG score recompute
  22. 2026-06-02 17:06 UTCEG score recompute
  23. 2026-06-02 03:58 UTCEG score recompute
  24. 2026-06-01 14:49 UTCEG score recompute

Frequently asked(5)

What is CVE-2026-47418?
CVE-2026-47418 is a high vulnerability published on June 1, 2026. praisonai-platform: Project endpoints accept any project_id without workspace ownership check, cross-workspace read/update/delete IDOR Summary Type: Insecure Direct Object Reference. The project CRUD endpoints (GET / PATCH / DELETE /workspaces/{workspaceid}/projects/{projectid} and GET…
When was CVE-2026-47418 disclosed?
CVE-2026-47418 was first published in the National Vulnerability Database on June 1, 2026. EchelonGraph re-ingests CVE updates from NVD on a 2-hour cycle, so this page reflects the latest published state.
Is CVE-2026-47418 actively exploited?
CVE-2026-47418 is not currently on CISA's Known Exploited Vulnerabilities catalog. FIRST EPSS estimates a 9.7% percentile likelihood of exploitation in the next 30 days — higher percentiles indicate greater predicted risk.
What is the CVSS score of CVE-2026-47418?
CVE-2026-47418 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-47418?
Patch to the fixed version published by the affected vendor. Where vendor advisories exist for CVE-2026-47418, 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-47418

Explore →

Is Your Infrastructure Affected by CVE-2026-47418?

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