CVE-2026-47415

HIGHPre-NVD 8.38.3
EchelonGraph scoreLOW confidence

This high-severity CVE scores 8.3 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.3
EchelonGraph verdictPlan a fixSerious severity, but no confirmed exploitation yet.
  • High severity, but no confirmed exploitation yet
CISA-KEV: Not listedEPSS: 0%CVSS: 8.3Exploit: NoneExposed: 0

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

praisonai-platform: Issue endpoints accept any issue_id without workspace ownership check, cross-workspace read/update/delete IDOR

Summary

Type: Insecure Direct Object Reference. The issue CRUD endpoints (GET / PATCH / DELETE /workspaces/{workspace_id}/issues/{issue_id}) gate access on require_workspace_member(workspace_id) only, then resolve issue_id through IssueService.get(issue_id) which is a primary-key lookup with no workspace constraint. A user who is a member of any workspace W1 can read, modify, or delete issues that belong to a different workspace W2. File: src/praisonai-platform/praisonai_platform/services/issue_service.py, lines 72-156; route handlers at src/praisonai-platform/praisonai_platform/api/routes/issues.py, lines 82-137. Root cause: the route extracts workspace_id from the URL path, uses it solely for the membership gate, then calls IssueService.get(issue_id) / IssueService.update(issue_id, ...) / IssueService.delete(issue_id) without re-checking which workspace the issue actually belongs to. IssueService.get runs a single-key lookup; update and delete call self.get(issue_id) first and then mutate the returned row, inheriting the same gap. The MemberService in this same codebase uses a composite (workspace_id, user_id) key, proving the author knows the safe pattern; it was simply not applied to the issue, agent, project, comment, or label services.

Affected Code

File 1: src/praisonai-platform/praisonai_platform/services/issue_service.py, lines 72-75 and 97-156.

class IssueService:
    ...

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

async def update( self, issue_id: str, title: Optional[str] = None, ... ) -> Optional[Issue]: issue = await self.get(issue_id) # <-- inherits the same gap if issue is None: return None ... return issue

async def delete(self, issue_id: str) -> bool: issue = await self.get(issue_id) # <-- inherits the same gap if issue is None: return False await self._session.delete(issue) await self._session.flush() return True

File 2: src/praisonai-platform/praisonai_platform/api/routes/issues.py, lines 82-137.

@router.get("/{issue_id}", response_model=IssueResponse)
async def get_issue(
    workspace_id: str,
    issue_id: str,
    user: AuthIdentity = Depends(require_workspace_member),         # only checks membership in workspace_id
    session: AsyncSession = Depends(get_db),
):
    svc = IssueService(session)
    issue = await svc.get(issue_id)                                 # <-- workspace_id never threaded through
    if issue is None:
        raise HTTPException(status_code=404, detail="Issue not found")
    return IssueResponse.model_validate(issue)

@router.patch("/{issue_id}", response_model=IssueResponse) async def update_issue( workspace_id: str, issue_id: str, body: IssueUpdate, user: AuthIdentity = Depends(require_workspace_member), session: AsyncSession = Depends(get_db), ): svc = IssueService(session) issue = await svc.update( # <-- writes to any issue in the DB issue_id, title=body.title, description=body.description, status=body.status, priority=body.priority, assignee_type=body.assignee_type, assignee_id=body.assignee_id, project_id=body.project_id, ) ...

delete_issue (lines 127-137) repeats the pattern.

Why it's wrong: workspace_id from the route is used solely as a membership predicate ("are you in some workspace W?"), never as a resource-ownership predicate ("is the issue you are addressing actually inside W?"). The standard FastAPI/SQLAlchemy fix is to make the resource-lookup query include the workspace constraint and treat absence as 404, so a foreign-workspace issue is indistinguishable from a non-existent one. The update_issue handler additionally allows the attacker to overwrite project_id, which can re-assign the foreign issue to an unrelated project the attacker also does not own — escalating the scope of the write primitive.

Exploit Chain

  • Attacker registers a workspace W_attacker (where they are a member) and harvests a target issue UUID I_T from any side channel: the activity feed (activity.py:log records issue_id=...), comment threads, error messages, exported issue dumps, issue mentions in agent prompts, or operator screenshots. Issue IDs are uuid4 strings but they are not secret. State: attacker holds I_T.
  • Attacker authenticates and POSTs Authorization: Bearer to GET /workspaces/W_attacker/issues/I_T. require_workspace_member(W_attacker, attacker) passes (attacker is a member of W_attacker). State: control flow enters get_issue with workspace_id=W_attacker, issue_id=I_T.
  • IssueService.get(I_T) runs session.get(Issue, "I_T"), which is SELECT * FROM issues WHERE id = 'I_T' LIMIT 1 with no workspace_id = 'W_attacker' filter. The row is returned in full — including title, description (often confidential bug-report content, customer PII, embedded credentials, or internal roadmap data), status, priority, assignee_id, created_by, and project_id. State: response body is the JSON-serialised foreign issue.
  • Attacker repeats with PATCH /workspaces/W_attacker/issues/I_T and a body of {"description": "", "status": "closed", "project_id": ""}. update_issue calls svc.update(I_T, ...) which loads the target row and mutates the listed fields. State: the foreign workspace's issue is silently re-described, re-statused, and re-projected.
  • Attacker calls DELETE /workspaces/W_attacker/issues/I_T to destroy the target issue. IssueService.delete loads the row and calls session.delete(). State: target issue is gone from the foreign workspace.
  • Final state: any attacker with one workspace-member token can enumerate, exfiltrate, rewrite, and delete every issue in the multi-tenant deployment given the issue UUIDs (which leak through the side channels above). The act_svc.log(workspace_id, "issue.updated", "issue", issue.id, ...) call at line 118 records the event under W_attacker rather than W_target, so the foreign workspace's audit trail does not record the tampering — making detection harder.

Security Impact

Severity: sec-high. CVSS 8.1: network attack, low complexity, low privileges (any workspace member), no user interaction, scope unchanged, high confidentiality (full issue body including any embedded secrets), high integrity (arbitrary writes including project re-assignment), low availability (DELETE wipes target issues). Attacker capability: with one workspace-member token plus a harvested issue UUID, an attacker reads the target issue's title, description, status, priority, assignee_id, and project_id; rewrites any of those fields (silent edit, false closure, malicious re-assignment); re-projects the issue to an unrelated project to confuse triagers; or deletes the issue altogether to destroy evidence of customer reports. Preconditions: praisonai-platform is deployed multi-tenant; the attacker has any membership token; the target issue's UUID is known or guessable (UUIDs leak through activity feeds, comment threads, error messages, exported dumps, and operator screenshots). Differential: source-inspection-verified end-to-end. The asymmetry between IssueService.get(issue_id) (no workspace check) and MemberService.get(workspace_id, user_id) (composite key check) in the same codebase confirms the pattern. With the suggested fix below applied, IssueService.get(workspace_id, issue_id) returns None for foreign-workspace issues, the route handler returns 404, and the foreign data is indistinguishable from a missing record.

Suggested Fix

Make every single-row resource lookup take the workspace predicate; treat foreign-workspace rows as 404.

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

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

  • issue = await self.get(issue_id)
+ issue = await self.get(workspace_id, issue_id) ...
  • async def delete(self, issue_id: str) -> bool:
+ async def delete(self, workspace_id: str, issue_id: str) -> bool:
  • issue = await self.get(issue_id)
+ issue = await self.get(workspace_id, issue_id)

Update the route handlers in routes/issues.py to thread workspace_id through. The same pattern (single-key resource lookup gated only by workspace-member check) exists in AgentService, ProjectService, CommentService, and LabelService; each is a separate exploitable IDOR and should be filed as its own advisory so each gets a CVE.

CVSS v3
8.3
EG Score
8.3(low)
EPSS
13.5%
KEV
Not listed

Published

June 1, 2026

Last Modified

June 1, 2026

Vendor Advisories for CVE-2026-47415(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 / 40× 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 15:46 UTCEG score recompute
  2. 2026-07-02 02:19 UTCEG score recompute
  3. 2026-07-01 13:14 UTCEG score recompute
  4. 2026-07-01 00:32 UTCEG score recompute
  5. 2026-06-30 00:36 UTCEG score recompute
  6. 2026-06-29 11:50 UTCEG score recompute
  7. 2026-06-28 20:47 UTCEG score recompute
  8. 2026-06-28 08:07 UTCEG score recompute
  9. 2026-06-27 19:24 UTCEG score recompute
  10. 2026-06-27 06:43 UTCEG score recompute
  11. 2026-06-26 18:02 UTCEG score recompute
  12. 2026-06-26 05:21 UTCEG score recompute
  13. 2026-06-25 16:40 UTCEG score recompute
  14. 2026-06-25 03:56 UTCEG score recompute
  15. 2026-06-24 15:00 UTCEG score recompute
  16. 2026-06-24 02:07 UTCEG score recompute
  17. 2026-06-18 22:02 UTCEG score recompute
  18. 2026-06-18 01:21 UTCEG score recompute
  19. 2026-06-17 12:28 UTCEG score recompute
  20. 2026-06-16 23:32 UTCEG score recompute
  21. 2026-06-16 10:52 UTCEG score recompute
  22. 2026-06-15 22:11 UTCEG score recompute
  23. 2026-06-15 09:18 UTCEG score recompute
  24. 2026-06-14 23:18 UTCEPSS rescore
  25. 2026-06-14 20:15 UTCEG score recompute
Show 27 more
  1. 2026-06-14 07:35 UTCEG score recompute
  2. 2026-06-13 23:00 UTCEPSS rescore
  3. 2026-06-13 18:55 UTCEG score recompute
  4. 2026-06-13 05:50 UTCEG score recompute
  5. 2026-06-12 23:12 UTCEPSS rescore
  6. 2026-06-12 17:09 UTCEG score recompute
  7. 2026-06-12 04:28 UTCEG score recompute
  8. 2026-06-11 15:48 UTCEG score recompute
  9. 2026-06-11 03:01 UTCEG score recompute
  10. 2026-06-10 14:21 UTCEG score recompute
  11. 2026-06-10 01:40 UTCEG score recompute
  12. 2026-06-09 13:00 UTCEG score recompute
  13. 2026-06-09 00:20 UTCEG score recompute
  14. 2026-06-08 11:39 UTCEG score recompute
  15. 2026-06-07 22:59 UTCEG score recompute
  16. 2026-06-07 10:18 UTCEG score recompute
  17. 2026-06-06 21:38 UTCEG score recompute
  18. 2026-06-06 08:57 UTCEG score recompute
  19. 2026-06-05 20:16 UTCEG score recompute
  20. 2026-06-05 07:36 UTCEG score recompute
  21. 2026-06-04 18:55 UTCEG score recompute
  22. 2026-06-04 06:14 UTCEG score recompute
  23. 2026-06-03 17:33 UTCEG score recompute
  24. 2026-06-03 04:52 UTCEG score recompute
  25. 2026-06-02 16:11 UTCEG score recompute
  26. 2026-06-02 03:30 UTCEG score recompute
  27. 2026-06-01 14:49 UTCEG score recompute

Frequently asked(5)

What is CVE-2026-47415?
CVE-2026-47415 is a high vulnerability published on June 1, 2026. praisonai-platform: Issue endpoints accept any issue_id without workspace ownership check, cross-workspace read/update/delete IDOR Summary Type: Insecure Direct Object Reference. The issue CRUD endpoints (GET / PATCH / DELETE /workspaces/{workspaceid}/issues/{issueid}) gate access on…
When was CVE-2026-47415 disclosed?
CVE-2026-47415 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-47415 actively exploited?
CVE-2026-47415 is not currently on CISA's Known Exploited Vulnerabilities catalog. FIRST EPSS estimates a 13.5% percentile likelihood of exploitation in the next 30 days — higher percentiles indicate greater predicted risk.
What is the CVSS score of CVE-2026-47415?
CVE-2026-47415 has a CVSS v4.0 base score of 8.3 (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-47415?
Patch to the fixed version published by the affected vendor. Where vendor advisories exist for CVE-2026-47415, 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-47415

Explore →

Is Your Infrastructure Affected by CVE-2026-47415?

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