Overview
The PraisonAI Platform exposes resources under `/api/v1/workspaces/{workspace_id}/...` and protects them with a `require_workspace_member(workspace_id)` FastAPI dependency. However, this dependency only checks if the caller is a member of the workspace specified in the URL prefix, without verifying the workspace ID of the inner resource. This oversight allows an attacker to access resources from other workspaces by manipulating the URL.
Technical Details
The vulnerability is caused by two main issues:
1. The auth dependency only sees the URL prefix and does not validate the inner resource ID.
2. The service-layer lookups are unscoped, allowing an attacker to access resources across workspaces.
The `require_workspace_member` function only checks if the user is a member of the workspace specified in the URL prefix, without validating the inner resource ID. This allows an attacker to put their own workspace in the URL prefix and access resources from other workspaces.
Additionally, the member-management routes (`add_member`, `update_member_role`, `remove_member`, `update_workspace`, `delete_workspace`) inherit the default `min_role="member"` from `require_workspace_member`. This allows any basic member to promote themselves to admin or owner, demote or remove other members, and delete the workspace.
Impact Analysis
The impact of this vulnerability is severe, with potential consequences including:
* Confidentiality: Any registered user can read every agent, issue, project, label, comment, and dependency across every workspace.
* Integrity: Any registered user can rewrite agent instructions to a malicious system prompt that exfiltrates conversations, mutates downstream behavior, or impersonates the original operator.
* Availability: Any registered user can delete every agent, issue, project, and dependency in every workspace, as well as delete entire workspaces.
* Account takeover: A user invited as a basic member to any workspace can promote themselves to admin, evict the original owner, and take full ownership of the workspace.
Mitigation
To fix this vulnerability, two changes are needed:
1. Re-scope every nested-resource lookup to the URL workspace by filtering at the service layer.
2. Enforce the role lattice on member-management routes by adding explicit `min_role` arguments where the operation is privileged.
Example of re-scoping every nested-resource lookup:
```python
async def get(self, agent_id: str, workspace_id: str) -> Optional[Agent]:
stmt = select(Agent).where(Agent.id == agent_id, Agent.workspace_id == workspace_id)
return (await self._session.execute(stmt)).scalar_one_or_none()
```
Example of enforcing the role lattice on member-management routes:
```python
async def update_member_role(
...,
user: AuthIdentity = Depends(lambda *a, **kw: require_workspace_member(*a, **kw, min_role="admin")),
):
...
```