GHSA-2fjj-qqg8-fg7x: praisonai-platform: cross-tenant IDOR poisons project stats

GHSA-2fjj-qqg8-fg7x MEDIUM
Published June 18, 2026
CISO Take

praisonai-platform versions below 0.1.8 allow any authenticated user in one workspace to inject issues referencing a project they have no access to in another workspace, corrupting that project's statistics visible to the victim organization. Although the bug is confined to statistics integrity rather than data disclosure, the attacker needs only a valid account in any workspace and knowledge of a target project UUID to execute the attack repeatedly — a trivially low bar requiring no specialized skill. EPSS data is unavailable and the vulnerability is not in CISA KEV, but the 104 prior CVEs in this same package signal a pattern of insufficient authorization hardening worth treating seriously in multi-tenant deployments. Teams running the PraisonAI platform module should upgrade to 0.1.8 immediately; no architectural workaround exists short of blocking write access to the issues endpoint entirely.

Sources: GitHub Advisory ATLAS

What is the risk?

Medium risk overall, but elevated for multi-tenant deployments where workspace isolation is a trust boundary. CVSS 4.3 correctly reflects no confidentiality impact and scoped integrity breach limited to statistics. However, exploitability is trivial — a single authenticated HTTP POST with a crafted project_id is sufficient, requiring no elevated privileges or specialized AI/ML knowledge. The 104 prior CVEs in the same package indicate a historically under-hardened authorization model. Risk escalates in compliance-sensitive environments where project metrics feed audit evidence or automated SLA reporting.

How does the attack unfold?

Reconnaissance
Attacker enumerates target workspace project UUIDs through their own workspace's API responses, shared platform UI elements, or systematic UUID probing of the issues endpoint.
AML.T0006
Initial Access
Attacker authenticates to the platform using valid credentials for their own workspace (W_B), requiring no privilege escalation, credential theft, or special permissions.
AML.T0012
Exploitation
Attacker sends repeated POST /workspaces/W_B/issues requests with a foreign project_id from victim workspace W_A, bypassing workspace ownership validation on the write path — each returns 201 with no authorization error.
AML.T0049
Impact
Victim's project statistics at GET /workspaces/W_A/projects/P_A/stats reflect attacker-injected data, corrupting dashboards, SLA reporting, sprint velocity metrics, and any compliance evidence derived from project aggregates.
AML.T0031

What systems are affected?

Package Ecosystem Vulnerable Range Patched
PraisonAI pip No patch
1 dependents 89% patched ~0d to patch Full package profile →
PraisonAI pip < 0.1.8 0.1.8
1 dependents 89% patched ~0d to patch Full package profile →

How severe is it?

CVSS 3.1
4.3 / 10
EPSS
N/A
Exploitation Status
No known exploitation
Sophistication
Trivial

What is the attack surface?

AV AC PR UI S C I A
AV Network
AC Low
PR Low
UI None
S Unchanged
C None
I Low
A None

What should I do?

5 steps
  1. Upgrade praisonai-platform to 0.1.8 or later immediately — this is the only complete fix.

  2. If upgrade is not immediately possible, block or rate-limit the POST /workspaces/{id}/issues endpoint via WAF or API gateway until patched.

  3. Audit the existing issues table for integrity violations: query for rows where the workspace_id on the issue does not match the workspace_id on the referenced project — these represent historical cross-tenant pollution.

  4. Apply defense-in-depth per the vendor's own suggested fix: scope get_stats() to count only issues whose workspace_id matches the project's workspace.

  5. Validate parent_issue_id and assignee_id body fields against the URL workspace using the existing require_issue_in_workspace helper pattern.

How is it classified?

Which compliance frameworks are affected?

This CVE is relevant to:

EU AI Act
Art. 9 - Risk management system
ISO 42001
6.1.2 - AI risk assessment
NIST AI RMF
GOVERN 6.1 - Policies and processes for AI risk
OWASP LLM Top 10
LLM08 - Excessive Agency

Frequently Asked Questions

What is GHSA-2fjj-qqg8-fg7x?

praisonai-platform versions below 0.1.8 allow any authenticated user in one workspace to inject issues referencing a project they have no access to in another workspace, corrupting that project's statistics visible to the victim organization. Although the bug is confined to statistics integrity rather than data disclosure, the attacker needs only a valid account in any workspace and knowledge of a target project UUID to execute the attack repeatedly — a trivially low bar requiring no specialized skill. EPSS data is unavailable and the vulnerability is not in CISA KEV, but the 104 prior CVEs in this same package signal a pattern of insufficient authorization hardening worth treating seriously in multi-tenant deployments. Teams running the PraisonAI platform module should upgrade to 0.1.8 immediately; no architectural workaround exists short of blocking write access to the issues endpoint entirely.

Is GHSA-2fjj-qqg8-fg7x actively exploited?

No confirmed active exploitation of GHSA-2fjj-qqg8-fg7x has been reported, but organizations should still patch proactively.

How to fix GHSA-2fjj-qqg8-fg7x?

1. Upgrade praisonai-platform to 0.1.8 or later immediately — this is the only complete fix. 2. If upgrade is not immediately possible, block or rate-limit the POST /workspaces/{id}/issues endpoint via WAF or API gateway until patched. 3. Audit the existing issues table for integrity violations: query for rows where the workspace_id on the issue does not match the workspace_id on the referenced project — these represent historical cross-tenant pollution. 4. Apply defense-in-depth per the vendor's own suggested fix: scope get_stats() to count only issues whose workspace_id matches the project's workspace. 5. Validate parent_issue_id and assignee_id body fields against the URL workspace using the existing require_issue_in_workspace helper pattern.

What systems are affected by GHSA-2fjj-qqg8-fg7x?

This vulnerability affects the following AI/ML architecture patterns: AI agent orchestration platforms, Multi-tenant SaaS AI infrastructure, Agent project management APIs.

What is the CVSS score for GHSA-2fjj-qqg8-fg7x?

GHSA-2fjj-qqg8-fg7x has a CVSS v3.1 base score of 4.3 (MEDIUM).

What is the AI security impact?

Affected AI Architectures

AI agent orchestration platformsMulti-tenant SaaS AI infrastructureAgent project management APIs

MITRE ATLAS Techniques

AML.T0012 Valid Accounts
AML.T0031 Erode AI Model Integrity
AML.T0049 Exploit Public-Facing Application

Compliance Controls Affected

EU AI Act: Art. 9
ISO 42001: 6.1.2
NIST AI RMF: GOVERN 6.1
OWASP LLM Top 10: LLM08

What are the technical details?

Original Advisory

## Summary The issue create and update endpoints in `praisonai-platform` accept a `project_id` in the request body and persist it without validating that the project belongs to the URL workspace. A user who is a member of workspace `W_B` (and has no access to workspace `W_A`) can create issues that reference a project owned by `W_A`. Because `ProjectService.get_stats()` aggregates issues by `project_id` with no workspace constraint, those foreign issues are then counted in the victim's own legitimate view of their project statistics. This is a cross-tenant integrity violation reachable by an outsider. This is distinct from the path-parameter IDOR family fixed in 0.1.4 (CVE-2026-47415, CVE-2026-47418, CVE-2026-47419). Those fixes scoped object references supplied in the URL path. This report concerns an object reference supplied in the request body at write time, which the 0.1.4 fixes did not cover. Version 0.1.4 fixed a set of path-parameter IDORs by threading `workspace_id` into the service-layer lookups (`get` / `update` / `delete`) and by adding the helpers `ensure_resource_in_workspace()` and `require_issue_in_workspace()` in `api/deps.py`. Those helpers are applied to object references that arrive in the URL path. They are not applied to object references that arrive in the request body on create or update. ## Details `api/routes/issues.py`, `create_issue` passes the body's `project_id` straight through with no workspace validation: ```python @router.post("/", response_model=IssueResponse, status_code=201) async def create_issue(workspace_id: str, body: IssueCreate, user=Depends(require_workspace_member), session=Depends(get_db)): svc = IssueService(session) issue = await svc.create( workspace_id=workspace_id, title=body.title, creator_id=user.id, project_id=body.project_id, # attacker-controlled, not validated against workspace_id ... ) ``` `services/issue_service.py`, `create` persists it as-is: ```python issue = Issue( workspace_id=workspace_id, project_id=project_id, # no check that project_id belongs to workspace_id ... ) ``` `services/issue_service.py`, `update` has the identical gap on the update path: ```python if project_id is not None: issue.project_id = project_id # re-parent to any project, no workspace check ``` `services/project_service.py`, `get_stats` aggregates by `project_id` only: ```python async def get_stats(self, project_id: str) -> dict: stmt = ( select(Issue.status, func.count(Issue.id)) .where(Issue.project_id == project_id) # no workspace_id constraint .group_by(Issue.status) ) ... ``` Note that the read path is not directly vulnerable. The stats route scopes the project first, so a cross-workspace stats read returns 404: ```python @router.get("/{project_id}/stats") async def project_stats(workspace_id, project_id, user=Depends(require_workspace_member), ...): project = await svc.get(project_id, workspace_id=workspace_id) # 404 for a foreign project if project is None: raise HTTPException(404, "Project not found") return await svc.get_stats(project_id) ``` The pollution therefore enters through the write side (issue create/update accepting a foreign `project_id`) and surfaces in the victim's own legitimate read of their project statistics. ## Proof of concept Two unrelated users: - Alice, member of workspace `W_A`, owns project `P_A`. - Bob, member of workspace `W_B` only. Bob has no access to `W_A` (every direct call to `W_A` resources returns 403). Steps: 1. Alice's project `P_A` has one in-progress issue. `GET /workspaces/W_A/projects/P_A/stats` returns `{"total": 1, "by_status": {"in_progress": 1}}`. 2. Bob creates issues in his own workspace that reference Alice's project. Repeat 7 times: ```http POST /workspaces/W_B/issues Authorization: Bearer <Bob's token> Content-Type: application/json {"title": "x", "project_id": "P_A", "status": "done"} ``` Each returns 201. Each issue is stored with `workspace_id = W_B` and `project_id = P_A`. 3. Alice reads her own project stats: `GET /workspaces/W_A/projects/P_A/stats` now returns `{"total": 8, "by_status": {"done": 7, "in_progress": 1}}`. Bob is not a member of `W_A`, yet data he wrote appears in Alice's project dashboard. ## Impact An unauthorized outsider can inflate or skew the issue counts shown in any workspace's project-statistics view, given only the target `project_id` (a UUID that can be harvested or guessed). The effect is limited to the statistics aggregation; it does not expose the victim's issue contents to the attacker and does not appear in the victim's workspace-scoped issue list. The same unvalidated write path also accepts cross-workspace `parent_issue_id` and `assignee_id` values, which have no aggregation read endpoint today but represent the same dangling cross-workspace reference class and should be fixed together. ## Suggested fix On both issue create and update, validate that any body-supplied object reference resolves within the URL workspace before persisting, reusing the existing pattern: ```python if body.project_id is not None: project = await ProjectService(session).get(body.project_id, workspace_id=workspace_id) if project is None: raise HTTPException(404, "Project not found") ``` Apply the same check to `parent_issue_id` (via `require_issue_in_workspace`) and to `assignee_id`. As defense in depth, scope `get_stats` so it only counts issues whose `workspace_id` matches the project's workspace.

Exploitation Scenario

An adversary with a trial account on a shared praisonai-platform instance (workspace W_B) targets a competitor organization in workspace W_A. They enumerate project UUIDs via their own workspace's API responses, shared UI hints, or brute-force of UUID space. The adversary scripts 500 POST /workspaces/W_B/issues requests — each with status 'done' and project_id set to the victim's P_A — all returning 201 with no authorization error. The victim's CISO dashboard now shows P_A with 500 falsely completed issues, collapsing the real in-progress ratio and potentially triggering false-positive SLA compliance alerts or corrupting sprint velocity metrics used by the organization's AI project governance reports.

Weaknesses (CWE)

CWE-639 — Authorization Bypass Through User-Controlled Key: The system's authorization functionality does not prevent one user from gaining access to another user's data or record by modifying the key value identifying the data.

  • [Architecture and Design] For each and every data access, ensure that the user has sufficient privilege to access the record that is being requested.
  • [Architecture and Design, Implementation] Make sure that the key that is used in the lookup of a specific user's record is not controllable externally by the user or that any tampering can be detected.

Source: MITRE CWE corpus.

CVSS Vector

CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:N

Timeline

Published
June 18, 2026
Last Modified
June 18, 2026
First Seen
June 18, 2026

Related Vulnerabilities