CVE-2026-47417
praisonai-platform: Comment endpoints accept any issue_id without workspace ownership check, cross-workspace comment read and post 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. The comment endpoints (POST /workspaces/{workspace_id}/issues/{issue_id}/comments and GET .../comments) gate access on require_workspace_member(workspace_id) only, then call CommentService.create(issue_id=issue_id, ...) and CommentService.list_for_issue(issue_id) without verifying that issue_id belongs to workspace_id. A user who is a member of any workspace W1 can read every comment on, and post new comments to, any issue in any other workspace W2. 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
- High
- Integrity
- High
- Availability
- None
Exploit likelihood
What each source reports
CVE List Score used
8.1
praisonai-platform: Comment endpoints accept any issue_id without workspace ownership check, cross-workspace comment read and post 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. The comment endpoints (POST /workspaces/{workspace_id}/issues/{issue_id}/comments and GET .../comments) gate access on require_workspace_member(workspace_id) only, then call CommentService.create(issue_id=issue_id, ...) and CommentService.list_for_issue(issue_id) without verifying that issue_id belongs to workspace_id. A user who is a member of any workspace W1 can read every comment on, and post new comments to, any issue in any other workspace W2. PraisonAI Platform version 0.1.4 patches the issue.
- Record
CVE-2026-47417- CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N - Weakness
- CWE-639
- Updated
- 2026-11-02
EUVDEUVD-2026-46335
8.1
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. The comment endpoints (POST /workspaces/{workspace_id}/issues/{issue_id}/comments and GET .../comments) gate access on require_workspace_member(workspace_id) only, then call CommentService.create(issue_id=issue_id, ...) and CommentService.list_for_issue(issue_id) without verifying that issue_id belongs to workspace_id. A user who is a member of any workspace W1 can read every comment on, and post new comments to, any issue in any other workspace W2. PraisonAI Platform version 0.1.4 patches the issue.
- Record
EUVD-2026-46335- CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-cp4f-5m9r-5jc2
8.1
praisonai-platform: Comment endpoints accept any issue_id without workspace ownership check, cross-workspace comment read and post IDOR
Summary
Type: Insecure Direct Object Reference. The comment endpoints (POST /workspaces/{workspace_id}/issues/{issue_id}/comments and GET .../comments) gate access on require_workspace_member(workspace_id) only, then call CommentService.create(issue_id=issue_id, ...) and CommentService.list_for_issue(issue_id) without verifying that issue_id belongs to workspace_id. A user who is a member of any workspace W1 can read every comment on, and post new comments to, any issue in any other workspace W2.
File: src/praisonai-platform/praisonai_platform/api/routes/issues.py, lines 143-171; src/praisonai-platform/praisonai_platform/services/comment_service.py, lines 19-53.
Root cause: the route extracts workspace_id from the URL path and uses it solely for the membership gate, then passes the URL-supplied issue_id straight into CommentService without confirming that this issue exists in workspace_id. CommentService.list_for_issue(issue_id) runs SELECT * FROM comments WHERE issue_id = :issue_id with no workspace join. CommentService.create(issue_id=issue_id, ...) blindly writes a row with that issue_id. Both flows trust the URL-supplied issue ID as authoritative even though the membership check guarantees nothing about it.
Affected Code
File 1: src/praisonai-platform/praisonai_platform/api/routes/issues.py, lines 143-171.
@router.post("/{issue_id}/comments", response_model=CommentResponse, status_code=status.HTTP_201_CREATED)
async def add_comment(
workspace_id: str,
issue_id: str,
body: CommentCreate,
user: AuthIdentity = Depends(require_workspace_member), # only checks attacker is in workspace_id
session: AsyncSession = Depends(get_db),
):
svc = CommentService(session)
comment = await svc.create(
issue_id=issue_id, # <-- BUG: no validation that issue_id is in workspace_id
author_id=user.id,
content=body.content,
author_type="member" if user.is_user else "agent",
parent_id=body.parent_id,
)
return CommentResponse.model_validate(comment)
@router.get("/{issue_id}/comments", response_model=List[CommentResponse])
async def list_comments(
workspace_id: str,
issue_id: str,
user: AuthIdentity = Depends(require_workspace_member),
session: AsyncSession = Depends(get_db),
):
svc = CommentService(session)
comments = await svc.list_for_issue(issue_id) # <-- BUG: returns comments on any issue
return [CommentResponse.model_validate(c) for c in comments]
File 2: src/praisonai-platform/praisonai_platform/services/comment_service.py, lines 19-53.
class CommentService:
...
async def create(
self,
issue_id: str,
author_id: str,
content: str,
author_type: str = "member",
comment_type: str = "comment",
parent_id: Optional[str] = None,
) -> Comment:
comment = Comment(
issue_id=issue_id, # <-- accepts any issue_id; no workspace verify
author_type=author_type,
author_id=author_id,
...
)
self._session.add(comment)
await self._session.flush()
return comment
async def list_for_issue(self, issue_id: str) -> list[Comment]:
stmt = (
select(Comment)
.where(Comment.issue_id == issue_id) # <-- no JOIN against issues for workspace constraint
.order_by(Comment.created_at)
)
result = await self._session.execute(stmt)
return list(result.scalars().all())
Why it's wrong: the service trusts the caller-supplied issue_id as authoritative, but the route layer never verified that this issue belongs to the workspace the membership check covers. The standard FastAPI/SQLAlchemy fix is to first resolve the issue scoped to…
- Record
GHSA-cp4f-5m9r-5jc2- CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N - Weakness
- CWE-639
- Updated
- 2026-11-02
NVD
8.1
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. The comment endpoints (POST /workspaces/{workspace_id}/issues/{issue_id}/comments and GET .../comments) gate access on require_workspace_member(workspace_id) only, then call CommentService.create(issue_id=issue_id, ...) and CommentService.list_for_issue(issue_id) without verifying that issue_id belongs to workspace_id. A user who is a member of any workspace W1 can read every comment on, and post new comments to, any issue in any other workspace W2. PraisonAI Platform version 0.1.4 patches the issue.
- Record
CVE-2026-47417- CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N - Weakness
- CWE-639
- Updated
- 2026-11-02
OSV
8.1
praisonai-platform: Comment endpoints accept any issue_id without workspace ownership check, cross-workspace comment read and post 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. The comment endpoints (POST /workspaces/{workspace_id}/issues/{issue_id}/comments and GET .../comments) gate access on require_workspace_member(workspace_id) only, then call CommentService.create(issue_id=issue_id, ...) and CommentService.list_for_issue(issue_id) without verifying that issue_id belongs to workspace_id. A user who is a member of any workspace W1 can read every comment on, and post new comments to, any issue in any other workspace W2. PraisonAI Platform version 0.1.4 patches the issue.
- Record
CVE-2026-47417- CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N - Weakness
- CWE-639
- Updated
- 2026-11-02
OSVGHSA-cp4f-5m9r-5jc2
8.1
praisonai-platform: Comment endpoints accept any issue_id without workspace ownership check, cross-workspace comment read and post IDOR
Summary
Type: Insecure Direct Object Reference. The comment endpoints (POST /workspaces/{workspace_id}/issues/{issue_id}/comments and GET .../comments) gate access on require_workspace_member(workspace_id) only, then call CommentService.create(issue_id=issue_id, ...) and CommentService.list_for_issue(issue_id) without verifying that issue_id belongs to workspace_id. A user who is a member of any workspace W1 can read every comment on, and post new comments to, any issue in any other workspace W2.
File: src/praisonai-platform/praisonai_platform/api/routes/issues.py, lines 143-171; src/praisonai-platform/praisonai_platform/services/comment_service.py, lines 19-53.
Root cause: the route extracts workspace_id from the URL path and uses it solely for the membership gate, then passes the URL-supplied issue_id straight into CommentService without confirming that this issue exists in workspace_id. CommentService.list_for_issue(issue_id) runs SELECT * FROM comments WHERE issue_id = :issue_id with no workspace join. CommentService.create(issue_id=issue_id, ...) blindly writes a row with that issue_id. Both flows trust the URL-supplied issue ID as authoritative even though the membership check guarantees nothing about it.
Affected Code
File 1: src/praisonai-platform/praisonai_platform/api/routes/issues.py, lines 143-171.
@router.post("/{issue_id}/comments", response_model=CommentResponse, status_code=status.HTTP_201_CREATED)
async def add_comment(
workspace_id: str,
issue_id: str,
body: CommentCreate,
user: AuthIdentity = Depends(require_workspace_member), # only checks attacker is in workspace_id
session: AsyncSession = Depends(get_db),
):
svc = CommentService(session)
comment = await svc.create(
issue_id=issue_id, # <-- BUG: no validation that issue_id is in workspace_id
author_id=user.id,
content=body.content,
author_type="member" if user.is_user else "agent",
parent_id=body.parent_id,
)
return CommentResponse.model_validate(comment)
@router.get("/{issue_id}/comments", response_model=List[CommentResponse])
async def list_comments(
workspace_id: str,
issue_id: str,
user: AuthIdentity = Depends(require_workspace_member),
session: AsyncSession = Depends(get_db),
):
svc = CommentService(session)
comments = await svc.list_for_issue(issue_id) # <-- BUG: returns comments on any issue
return [CommentResponse.model_validate(c) for c in comments]
File 2: src/praisonai-platform/praisonai_platform/services/comment_service.py, lines 19-53.
class CommentService:
...
async def create(
self,
issue_id: str,
author_id: str,
content: str,
author_type: str = "member",
comment_type: str = "comment",
parent_id: Optional[str] = None,
) -> Comment:
comment = Comment(
issue_id=issue_id, # <-- accepts any issue_id; no workspace verify
author_type=author_type,
author_id=author_id,
...
)
self._session.add(comment)
await self._session.flush()
return comment
async def list_for_issue(self, issue_id: str) -> list[Comment]:
stmt = (
select(Comment)
.where(Comment.issue_id == issue_id) # <-- no JOIN against issues for workspace constraint
.order_by(Comment.created_at)
)
result = await self._session.execute(stmt)
return list(result.scalars().all())
Why it's wrong: the service trusts the caller-supplied issue_id as authoritative, but the route layer never verified that this issue belongs to the workspace the membership check covers. The standard FastAPI/SQLAlchemy fix is to first resolve the issue scoped to…
- Record
GHSA-cp4f-5m9r-5jc2- CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N - Weakness
- CWE-639
- Updated
- 2026-11-02
OSVPYSEC-2026-2933
8.1
praisonai-platform: Comment endpoints accept any issue_id without workspace ownership check, cross-workspace comment read and post IDOR
Summary
Type: Insecure Direct Object Reference. The comment endpoints (POST /workspaces/{workspace_id}/issues/{issue_id}/comments and GET .../comments) gate access on require_workspace_member(workspace_id) only, then call CommentService.create(issue_id=issue_id, ...) and CommentService.list_for_issue(issue_id) without verifying that issue_id belongs to workspace_id. A user who is a member of any workspace W1 can read every comment on, and post new comments to, any issue in any other workspace W2.
File: src/praisonai-platform/praisonai_platform/api/routes/issues.py, lines 143-171; src/praisonai-platform/praisonai_platform/services/comment_service.py, lines 19-53.
Root cause: the route extracts workspace_id from the URL path and uses it solely for the membership gate, then passes the URL-supplied issue_id straight into CommentService without confirming that this issue exists in workspace_id. CommentService.list_for_issue(issue_id) runs SELECT * FROM comments WHERE issue_id = :issue_id with no workspace join. CommentService.create(issue_id=issue_id, ...) blindly writes a row with that issue_id. Both flows trust the URL-supplied issue ID as authoritative even though the membership check guarantees nothing about it.
Affected Code
File 1: src/praisonai-platform/praisonai_platform/api/routes/issues.py, lines 143-171.
@router.post("/{issue_id}/comments", response_model=CommentResponse, status_code=status.HTTP_201_CREATED)
async def add_comment(
workspace_id: str,
issue_id: str,
body: CommentCreate,
user: AuthIdentity = Depends(require_workspace_member), # only checks attacker is in workspace_id
session: AsyncSession = Depends(get_db),
):
svc = CommentService(session)
comment = await svc.create(
issue_id=issue_id, # <-- BUG: no validation that issue_id is in workspace_id
author_id=user.id,
content=body.content,
author_type="member" if user.is_user else "agent",
parent_id=body.parent_id,
)
return CommentResponse.model_validate(comment)
@router.get("/{issue_id}/comments", response_model=List[CommentResponse])
async def list_comments(
workspace_id: str,
issue_id: str,
user: AuthIdentity = Depends(require_workspace_member),
session: AsyncSession = Depends(get_db),
):
svc = CommentService(session)
comments = await svc.list_for_issue(issue_id) # <-- BUG: returns comments on any issue
return [CommentResponse.model_validate(c) for c in comments]
File 2: src/praisonai-platform/praisonai_platform/services/comment_service.py, lines 19-53.
class CommentService:
...
async def create(
self,
issue_id: str,
author_id: str,
content: str,
author_type: str = "member",
comment_type: str = "comment",
parent_id: Optional[str] = None,
) -> Comment:
comment = Comment(
issue_id=issue_id, # <-- accepts any issue_id; no workspace verify
author_type=author_type,
author_id=author_id,
...
)
self._session.add(comment)
await self._session.flush()
return comment
async def list_for_issue(self, issue_id: str) -> list[Comment]:
stmt = (
select(Comment)
.where(Comment.issue_id == issue_id) # <-- no JOIN against issues for workspace constraint
.order_by(Comment.created_at)
)
result = await self._session.execute(stmt)
return list(result.scalars().all())
Why it's wrong: the service trusts the caller-supplied issue_id as authoritative, but the route layer never verified that this issue belongs to the workspace the membership check covers. The standard FastAPI/SQLAlchemy fix is to first resolve the issue scoped to…
- Record
PYSEC-2026-2933- CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N - Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
praisonai-platformPyPI |
before 0.1.4 | 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-47417 is a high vulnerability (CVSS 8.1) in Mervinpraison Praisonai-platform, published June 1, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.36%, the 30th percentile. Fixed in praisonai-platform 0.1.4.
References
github.com/MervinPraison/PraisonAI/security/advisories/GHSA-cp4f-5m9r-5jc2
github.com/MervinPraison/PraisonAI/commit/ef79b7a0561796ad9807f0f09538c25cc78d3619
github.com/advisories/GHSA-cp4f-5m9r-5jc2
github.com/CVEProject/cvelistV5/tree/main/cves/2026/47xxx/CVE-2026-47417.json
nvd.nist.gov/vuln/detail/CVE-2026-47417
github.com/MervinPraison/PraisonAI
pypi.org/project/praisonai-platform
Left out here: the rest of 3 source descriptions. The JSON record has them.