The vulnerability exists because the application's service layer does not perform authorization checks to ensure that a requested object belongs to the workspace the user is authorized for. The API routes are scoped by workspace, and the initial authorization check (require_workspace_member) correctly verifies that the user is a member of the specified workspace. However, the subsequent database lookups for objects like agents, projects, and issues are done using only the global UUID of the object (agent_id, project_id, issue_id). This allows an authenticated user who is a member of any workspace to access, modify, or delete objects in any other workspace, simply by knowing the object's UUID. The vulnerability is systemic and affects multiple object types across the platform. The provided vulnerability description was detailed enough to identify the vulnerable functions and patterns without needing the exact patch commit.
Vulnerable functions
get_agent
praisonai_platform/api/routes/agents.py
The function gets the agent by `agent_id` without verifying if it belongs to the `workspace_id`. This allows an attacker to access agents from other workspaces.
AgentService.get
praisonai_platform/services/agent_service.py
The function retrieves an agent from the database using only the `agent_id`, without any workspace validation. This is the root cause of the vulnerability for agent retrieval.
AgentService.update
praisonai_platform/services/agent_service.py
The function updates an agent using only the `agent_id`, which allows an attacker to update agents in other workspaces.
Therefore, if an attacker is a valid member of any workspace, they can pass their own workspace ID in the URL while supplying an object ID from another workspace.
The same architectural pattern appears in other workspace-scoped object routes, including projects, issues, and comments:
This indicates a systemic object-level access control issue: routes are workspace-scoped, but service-layer object lookups are not workspace-bound.
PoC
The following local PoC creates a real PraisonAI Platform FastAPI app backed by an in-memory SQLite database, then uses only HTTP requests against the real API routes.
The PoC demonstrates the following chain:
An attacker account creates workspace_attacker.
A victim account creates workspace_victim.
The victim creates an agent in workspace_victim.
The attacker sends:
GET /api/v1/workspaces/{workspace_attacker}/agents/{victim_agent_id}
The server returns the victim agent from workspace_victim.
The attacker updates the victim agent through the attacker workspace path.
The victim observes the attacker-controlled modification.
The attacker deletes the victim agent through the attacker workspace path.
This confirms that an authenticated user from one workspace can read, modify, and delete an object belonging to another workspace by using the victim object's UUID through the attacker's own workspace-scoped route.
Impact
Any authenticated workspace member who knows or obtains object UUIDs from another workspace may be able to:
read other workspaces' agents;
read agent instructions and metadata;
modify victim agents;
delete victim agents;
potentially read, modify, or delete projects and issues that follow the same object lookup pattern;
enumerate comments for issues by raw issue_id;
corrupt activity data, project state, and issue state across workspace boundaries.
This breaks the platform's tenant-isolation boundary. The impact is especially serious in multi-tenant deployments where separate users or teams rely on workspaces as an authorization boundary.
The demonstrated PoC confirms read, update, and delete access against agents. The same root-cause pattern appears in other workspace-scoped object routes and should be audited across the platform.
Suggested remediation
Recommended fixes:
Require every object fetch, update, and delete method to take both workspace_id and object_id.
Enforce object ownership in the service layer. For example:
agent = await self._session.get(Agent, agent_id)
if not agent or agent.workspace_id != workspace_id:
return None
Avoid service methods that resolve workspace-owned objects by global UUID alone.
Apply the same object-level ownership checks to agents, projects, issues, comments, dependencies, and any other workspace-owned resources.
For comment and dependency helpers that pivot from raw issue_id, validate that the parent issue belongs to the authorized workspace before returning or modifying child records.
Add regression tests for negative cross-workspace access cases, including:
workspace A member cannot read workspace B object
workspace A member cannot update workspace B object
workspace A member cannot delete workspace B object
workspace A member cannot list comments for workspace B issue
Return 404 Not Found or 403 Forbidden consistently when an object does not belong to the authorized workspace.
Security boundary
This report concerns a workspace tenant-isolation failure. The caller is authenticated, but authentication alone is insufficient. The server must also verify that the requested object belongs to the workspace for which the caller has authorization.