CVE-2026-47414
praisonai-platform: Label endpoints accept any label_id and any issue_id without workspace ownership check, cross-workspace label edit/delete and issue-label-link IDOR
PraisonAI Platform is the platform layer for the PraisonAI multi-agent teams system. Versions prior to 0.1.4 have an Insecure Direct Object Reference. Five label endpoints — PATCH /workspaces/{workspace_id}/labels/{label_id}, DELETE .../labels/{label_id}, POST .../issues/{issue_id}/labels/{label_id}, DELETE .../issues/{issue_id}/labels/{label_id}, GET .../issues/{issue_id}/labels — gate access on require_workspace_member(workspace_id) only and pass URL-supplied label_id and issue_id straight through to LabelService without verifying either belongs to the workspace. 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
- Low
- Integrity
- High
- Availability
- Low
Exploit likelihood
What each source reports
CVE List Score used
7.6
praisonai-platform: Label endpoints accept any label_id and any issue_id without workspace ownership check, cross-workspace label edit/delete and issue-label-link IDOR
PraisonAI Platform is the platform layer for the PraisonAI multi-agent teams system. Versions prior to 0.1.4 have an Insecure Direct Object Reference. Five label endpoints — PATCH /workspaces/{workspace_id}/labels/{label_id}, DELETE .../labels/{label_id}, POST .../issues/{issue_id}/labels/{label_id}, DELETE .../issues/{issue_id}/labels/{label_id}, GET .../issues/{issue_id}/labels — gate access on require_workspace_member(workspace_id) only and pass URL-supplied label_id and issue_id straight through to LabelService without verifying either belongs to the workspace. PraisonAI Platform version 0.1.4 patches the issue.
- Record
CVE-2026-47414- CVSS 3.1
- 7.6 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:H/A:L - Weakness
- CWE-639
- Updated
- 2026-11-02
EUVDEUVD-2026-46332
7.6
PraisonAI Platform is the platform layer for the PraisonAI multi-agent teams system. Versions prior to 0.1.4 have an Insecure Direct Object Reference. Five label endpoints — PATCH /workspaces/{workspace_id}/labels/{label_id}, DELETE .../labels/{label_id}, POST .../issues/{issue_id}/labels/{label_id}, DELETE .../issues/{issue_id}/labels/{label_id}, GET .../issues/{issue_id}/labels — gate access on require_workspace_member(workspace_id) only and pass URL-supplied label_id and issue_id straight through to LabelService without verifying either belongs to the workspace. PraisonAI Platform version 0.1.4 patches the issue.
- Record
EUVD-2026-46332- CVSS 3.1
- 7.6 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:H/A:L - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-5jx9-w35f-vp65
7.6
praisonai-platform: Label endpoints' unchecked label_id/issue_id enable cross-workspace label IDOR (edit, delete, link)
Summary
Type: Insecure Direct Object Reference. Five label endpoints — PATCH /workspaces/{workspace_id}/labels/{label_id}, DELETE .../labels/{label_id}, POST .../issues/{issue_id}/labels/{label_id}, DELETE .../issues/{issue_id}/labels/{label_id}, GET .../issues/{issue_id}/labels — gate access on require_workspace_member(workspace_id) only and pass URL-supplied label_id and issue_id straight through to LabelService without verifying either belongs to the workspace.
File: src/praisonai-platform/praisonai_platform/services/label_service.py, lines 35-100; route handlers at src/praisonai-platform/praisonai_platform/api/routes/labels.py, lines 42-106.
Root cause: identical pattern to the agent / issue / project / comment IDORs in this codebase: the route's workspace_id is used as a membership predicate but never threaded through to the service layer. LabelService.get(label_id) runs session.get(IssueLabel, label_id) with no workspace filter; update/delete inherit the gap; add_to_issue(issue_id, label_id) and remove_from_issue(issue_id, label_id) write/delete association rows without verifying either ID belongs to the membership-checked workspace; list_for_issue(issue_id) reads them.
Affected Code
File 1: src/praisonai-platform/praisonai_platform/services/label_service.py, lines 35-100.
class LabelService:
...
async def get(self, label_id: str) -> Optional[IssueLabel]:
return await self._session.get(IssueLabel, label_id) # <-- BUG: no workspace_id predicate
async def update(
self,
label_id: str,
...
) -> Optional[IssueLabel]:
label = await self.get(label_id) # <-- inherits the gap
...
async def delete(self, label_id: str) -> bool:
label = await self.get(label_id) # <-- inherits the gap
...
async def add_to_issue(self, issue_id: str, label_id: str) -> None:
# writes a row in issue_label association table; no workspace check on either id
async def remove_from_issue(self, issue_id: str, label_id: str) -> None:
# deletes from association table; no workspace check on either id
async def list_for_issue(self, issue_id: str) -> list[IssueLabel]:
# reads from association table; no workspace check on issue_id
File 2: src/praisonai-platform/praisonai_platform/api/routes/labels.py, lines 42-106.
@router.patch("/labels/{label_id}", response_model=LabelResponse)
async def update_label(workspace_id: str, label_id: str, body: LabelUpdate, ...):
svc = LabelService(session)
label = await svc.update(label_id, body.name, body.color) # <-- writes any label in the DB
...
@router.delete("/labels/{label_id}", ...)
async def delete_label(workspace_id: str, label_id: str, ...):
deleted = await svc.delete(label_id) # <-- deletes any label in the DB
...
@router.post("/issues/{issue_id}/labels/{label_id}", ...)
async def add_label_to_issue(workspace_id: str, issue_id: str, label_id: str, ...):
await svc.add_to_issue(issue_id, label_id) # <-- attaches any label to any issue cross-workspace
@router.delete("/issues/{issue_id}/labels/{label_id}", ...)
async def remove_label_from_issue(workspace_id: str, issue_id: str, label_id: str, ...):
await svc.remove_from_issue(issue_id, label_id) # <-- detaches any label from any issue cross-workspace
@router.get("/issues/{issue_id}/labels", ...)
async def list_issue_labels(workspace_id: str, issue_id: str, ...):
labels = await svc.list_for_issue(issue_id) # <-- reads label assignments for any issue
Why it's wrong: the workspace_id URL segment is treated as a UI hint; the actual label_id and issue_id lookups query the database without a workspace constraint. The MemberService in this same…
- Record
GHSA-5jx9-w35f-vp65- CVSS 3.1
- 7.6 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:H/A:L - Weakness
- CWE-639
- Updated
- 2026-11-02
NVD
7.6
PraisonAI Platform is the platform layer for the PraisonAI multi-agent teams system. Versions prior to 0.1.4 have an Insecure Direct Object Reference. Five label endpoints — PATCH /workspaces/{workspace_id}/labels/{label_id}, DELETE .../labels/{label_id}, POST .../issues/{issue_id}/labels/{label_id}, DELETE .../issues/{issue_id}/labels/{label_id}, GET .../issues/{issue_id}/labels — gate access on require_workspace_member(workspace_id) only and pass URL-supplied label_id and issue_id straight through to LabelService without verifying either belongs to the workspace. PraisonAI Platform version 0.1.4 patches the issue.
- Record
CVE-2026-47414- CVSS 3.1
- 7.6 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:H/A:L - Weakness
- CWE-639
- Updated
- 2026-11-02
OSV
7.6
praisonai-platform: Label endpoints accept any label_id and any issue_id without workspace ownership check, cross-workspace label edit/delete and issue-label-link IDOR
PraisonAI Platform is the platform layer for the PraisonAI multi-agent teams system. Versions prior to 0.1.4 have an Insecure Direct Object Reference. Five label endpoints — PATCH /workspaces/{workspace_id}/labels/{label_id}, DELETE .../labels/{label_id}, POST .../issues/{issue_id}/labels/{label_id}, DELETE .../issues/{issue_id}/labels/{label_id}, GET .../issues/{issue_id}/labels — gate access on require_workspace_member(workspace_id) only and pass URL-supplied label_id and issue_id straight through to LabelService without verifying either belongs to the workspace. PraisonAI Platform version 0.1.4 patches the issue.
- Record
CVE-2026-47414- CVSS 3.1
- 7.6 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:H/A:L - Weakness
- CWE-639
- Updated
- 2026-11-02
OSVGHSA-5jx9-w35f-vp65
7.6
praisonai-platform: Label endpoints' unchecked label_id/issue_id enable cross-workspace label IDOR (edit, delete, link)
Summary
Type: Insecure Direct Object Reference. Five label endpoints — PATCH /workspaces/{workspace_id}/labels/{label_id}, DELETE .../labels/{label_id}, POST .../issues/{issue_id}/labels/{label_id}, DELETE .../issues/{issue_id}/labels/{label_id}, GET .../issues/{issue_id}/labels — gate access on require_workspace_member(workspace_id) only and pass URL-supplied label_id and issue_id straight through to LabelService without verifying either belongs to the workspace.
File: src/praisonai-platform/praisonai_platform/services/label_service.py, lines 35-100; route handlers at src/praisonai-platform/praisonai_platform/api/routes/labels.py, lines 42-106.
Root cause: identical pattern to the agent / issue / project / comment IDORs in this codebase: the route's workspace_id is used as a membership predicate but never threaded through to the service layer. LabelService.get(label_id) runs session.get(IssueLabel, label_id) with no workspace filter; update/delete inherit the gap; add_to_issue(issue_id, label_id) and remove_from_issue(issue_id, label_id) write/delete association rows without verifying either ID belongs to the membership-checked workspace; list_for_issue(issue_id) reads them.
Affected Code
File 1: src/praisonai-platform/praisonai_platform/services/label_service.py, lines 35-100.
class LabelService:
...
async def get(self, label_id: str) -> Optional[IssueLabel]:
return await self._session.get(IssueLabel, label_id) # <-- BUG: no workspace_id predicate
async def update(
self,
label_id: str,
...
) -> Optional[IssueLabel]:
label = await self.get(label_id) # <-- inherits the gap
...
async def delete(self, label_id: str) -> bool:
label = await self.get(label_id) # <-- inherits the gap
...
async def add_to_issue(self, issue_id: str, label_id: str) -> None:
# writes a row in issue_label association table; no workspace check on either id
async def remove_from_issue(self, issue_id: str, label_id: str) -> None:
# deletes from association table; no workspace check on either id
async def list_for_issue(self, issue_id: str) -> list[IssueLabel]:
# reads from association table; no workspace check on issue_id
File 2: src/praisonai-platform/praisonai_platform/api/routes/labels.py, lines 42-106.
@router.patch("/labels/{label_id}", response_model=LabelResponse)
async def update_label(workspace_id: str, label_id: str, body: LabelUpdate, ...):
svc = LabelService(session)
label = await svc.update(label_id, body.name, body.color) # <-- writes any label in the DB
...
@router.delete("/labels/{label_id}", ...)
async def delete_label(workspace_id: str, label_id: str, ...):
deleted = await svc.delete(label_id) # <-- deletes any label in the DB
...
@router.post("/issues/{issue_id}/labels/{label_id}", ...)
async def add_label_to_issue(workspace_id: str, issue_id: str, label_id: str, ...):
await svc.add_to_issue(issue_id, label_id) # <-- attaches any label to any issue cross-workspace
@router.delete("/issues/{issue_id}/labels/{label_id}", ...)
async def remove_label_from_issue(workspace_id: str, issue_id: str, label_id: str, ...):
await svc.remove_from_issue(issue_id, label_id) # <-- detaches any label from any issue cross-workspace
@router.get("/issues/{issue_id}/labels", ...)
async def list_issue_labels(workspace_id: str, issue_id: str, ...):
labels = await svc.list_for_issue(issue_id) # <-- reads label assignments for any issue
Why it's wrong: the workspace_id URL segment is treated as a UI hint; the actual label_id and issue_id lookups query the database without a workspace constraint. The MemberService in this same…
- Record
GHSA-5jx9-w35f-vp65- CVSS 3.1
- 7.6 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:H/A:L - Weakness
- CWE-639
- Updated
- 2026-11-02
OSVPYSEC-2026-2929
7.6
praisonai-platform: Label endpoints' unchecked label_id/issue_id enable cross-workspace label IDOR (edit, delete, link)
Summary
Type: Insecure Direct Object Reference. Five label endpoints — PATCH /workspaces/{workspace_id}/labels/{label_id}, DELETE .../labels/{label_id}, POST .../issues/{issue_id}/labels/{label_id}, DELETE .../issues/{issue_id}/labels/{label_id}, GET .../issues/{issue_id}/labels — gate access on require_workspace_member(workspace_id) only and pass URL-supplied label_id and issue_id straight through to LabelService without verifying either belongs to the workspace.
File: src/praisonai-platform/praisonai_platform/services/label_service.py, lines 35-100; route handlers at src/praisonai-platform/praisonai_platform/api/routes/labels.py, lines 42-106.
Root cause: identical pattern to the agent / issue / project / comment IDORs in this codebase: the route's workspace_id is used as a membership predicate but never threaded through to the service layer. LabelService.get(label_id) runs session.get(IssueLabel, label_id) with no workspace filter; update/delete inherit the gap; add_to_issue(issue_id, label_id) and remove_from_issue(issue_id, label_id) write/delete association rows without verifying either ID belongs to the membership-checked workspace; list_for_issue(issue_id) reads them.
Affected Code
File 1: src/praisonai-platform/praisonai_platform/services/label_service.py, lines 35-100.
class LabelService:
...
async def get(self, label_id: str) -> Optional[IssueLabel]:
return await self._session.get(IssueLabel, label_id) # <-- BUG: no workspace_id predicate
async def update(
self,
label_id: str,
...
) -> Optional[IssueLabel]:
label = await self.get(label_id) # <-- inherits the gap
...
async def delete(self, label_id: str) -> bool:
label = await self.get(label_id) # <-- inherits the gap
...
async def add_to_issue(self, issue_id: str, label_id: str) -> None:
# writes a row in issue_label association table; no workspace check on either id
async def remove_from_issue(self, issue_id: str, label_id: str) -> None:
# deletes from association table; no workspace check on either id
async def list_for_issue(self, issue_id: str) -> list[IssueLabel]:
# reads from association table; no workspace check on issue_id
File 2: src/praisonai-platform/praisonai_platform/api/routes/labels.py, lines 42-106.
@router.patch("/labels/{label_id}", response_model=LabelResponse)
async def update_label(workspace_id: str, label_id: str, body: LabelUpdate, ...):
svc = LabelService(session)
label = await svc.update(label_id, body.name, body.color) # <-- writes any label in the DB
...
@router.delete("/labels/{label_id}", ...)
async def delete_label(workspace_id: str, label_id: str, ...):
deleted = await svc.delete(label_id) # <-- deletes any label in the DB
...
@router.post("/issues/{issue_id}/labels/{label_id}", ...)
async def add_label_to_issue(workspace_id: str, issue_id: str, label_id: str, ...):
await svc.add_to_issue(issue_id, label_id) # <-- attaches any label to any issue cross-workspace
@router.delete("/issues/{issue_id}/labels/{label_id}", ...)
async def remove_label_from_issue(workspace_id: str, issue_id: str, label_id: str, ...):
await svc.remove_from_issue(issue_id, label_id) # <-- detaches any label from any issue cross-workspace
@router.get("/issues/{issue_id}/labels", ...)
async def list_issue_labels(workspace_id: str, issue_id: str, ...):
labels = await svc.list_for_issue(issue_id) # <-- reads label assignments for any issue
Why it's wrong: the workspace_id URL segment is treated as a UI hint; the actual label_id and issue_id lookups query the database without a workspace constraint. The MemberService in this same…
- Record
PYSEC-2026-2929- CVSS 3.1
- 7.6 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:H/A:L - 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-47414 is a high vulnerability (CVSS 7.6) 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.38%, the 32nd percentile. Fixed in praisonai-platform 0.1.4.
References
github.com/MervinPraison/PraisonAI/security/advisories/GHSA-5jx9-w35f-vp65
github.com/MervinPraison/PraisonAI/pull/1685
github.com/MervinPraison/PraisonAI/commit/ef79b7a0561796ad9807f0f09538c25cc78d3619
github.com/advisories/GHSA-5jx9-w35f-vp65
github.com/CVEProject/cvelistV5/tree/main/cves/2026/47xxx/CVE-2026-47414.json
nvd.nist.gov/vuln/detail/CVE-2026-47414
github.com/MervinPraison/PraisonAI
pypi.org/project/praisonai-platform
Left out here: the rest of 3 source descriptions. The JSON record has them.