PT-2026-45484 · Pypi · Praisonai-Platform
Published
2026-06-01
·
Updated
2026-06-01
·
CVE-2026-47411
CVSS v3.1
6.5
Medium
| Vector | AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N |
Summary
Type: Authorization bypass enabling workspace metadata + settings tampering. The
PATCH /workspaces/{workspace id} endpoint is gated only by require workspace member(workspace id) (default min role="member"). Any member can rewrite the workspace's name, description, and the settings JSON blob. The settings field is a free-form JSON object — depending on which downstream code reads it, this becomes a configuration-injection primitive for any setting the platform exposes there.
File: src/praisonai-platform/praisonai platform/api/routes/workspaces.py, lines 63-74; services/workspace service.py's update() method.
Root cause: Depends(require workspace member) resolves to default min role="member". WorkspaceService.update(workspace id, name, description, settings) writes the new fields to the workspace row without any caller-permission check. The role hierarchy (MemberService.has role) is never consulted.Affected Code
File:
src/praisonai-platform/praisonai platform/api/routes/workspaces.py, lines 63-74.@router.patch("/{workspace id}", response model=WorkspaceResponse)
async def update workspace(
workspace id: str,
body: WorkspaceUpdate,
user: AuthIdentity = Depends(require workspace member), # <-- BUG: defaults to min role="member"
session: AsyncSession = Depends(get db),
):
ws svc = WorkspaceService(session)
ws = await ws svc.update(workspace id, body.name, body.description, body.settings) # <-- writes any value
if ws is None:
raise HTTPException(status code=404, detail="Workspace not found")
return WorkspaceResponse.model validate(ws)
Why it's wrong: workspace name and settings are owner-tier fields. Renaming the workspace to a profanity is a low-impact griefing vector; rewriting the JSON
settings blob is potentially a much higher-impact configuration injection (depending on what fields downstream code reads from settings, the attacker may flip feature flags, redirect webhook URLs, change LLM provider keys for shared configs, disable audit logging, etc.). The require workspace member(min role) parameter is implemented and unused. This endpoint should require owner.Exploit Chain
- Attacker is a member of workspace
Wwith role "member". State: attacker holds JWT. - Attacker sends
PATCH /workspaces/WwithAuthorization: Bearer <attacker jwt>and body{"name": "Compromised", "description": "Owned by attacker", "settings": {"allow public invite": true, "ai provider url": "https://attacker.example/v1"}}. State: control flow entersupdate workspace. require workspace member(W, attacker)passes.WorkspaceService.update(W, ...)writes the three fields. State: workspaceWnow has attacker-chosen name, description, and settings.- The settings JSON is read by any downstream code that consults workspace settings (LLM proxying, invite flows, webhook routing). If the deployment uses settings-keyed configuration overrides, those overrides now point at attacker-controlled endpoints.
- Final state: with one member-level token plus one PATCH, the attacker rewrites the workspace's metadata and settings, with effects ranging from cosmetic (rename) to substantive (settings-keyed config injection).
Security Impact
Severity: sec-moderate. CVSS 6.5: network attack, low complexity, low privileges, no user interaction, scope unchanged, no confidentiality directly (though settings rewrites may enable indirect data exfiltration via attacker-pointed integration URLs), high integrity, no availability claim.
Attacker capability: rewrite any workspace's name, description, and settings JSON. The actual blast radius depends on what fields the deployment reads from
settings — but that field is documented as a free-form JSON blob, so any future configuration the platform adds there becomes attacker-tunable.
Preconditions: praisonai-platform is deployed multi-tenant; attacker has any membership token in the target workspace.
Differential: source-inspection-verified. With the suggested fix below, member-tier tokens fail the gate and the metadata rewrite is rejected with 403.Suggested Fix
--- a/src/praisonai-platform/praisonai platform/api/routes/workspaces.py
+++ b/src/praisonai-platform/praisonai platform/api/routes/workspaces.py
@@ -63,11 +63,11 @@
@router.patch("/{workspace id}", response model=WorkspaceResponse)
async def update workspace(
workspace id: str,
body: WorkspaceUpdate,
- user: AuthIdentity = Depends(require workspace member),
+ user: AuthIdentity = Depends( require workspace owner), # see member-update-role advisory for helper
session: AsyncSession = Depends(get db),
):
ws svc = WorkspaceService(session)
ws = await ws svc.update(workspace id, body.name, body.description, body.settings)
if ws is None:
raise HTTPException(status code=404, detail="Workspace not found")
return WorkspaceResponse.model validate(ws)
Defence-in-depth: validate the keys allowed in
body.settings against an allowlist so the field cannot become an arbitrary config-injection primitive even for owners. The four companion workspace-mutation endpoints (add member, update member role, remove member, delete workspace) exhibit the same default-min-role gap and are filed as their own advisories.Fix
Improper Privilege Management
Missing Authorization
Found an issue in the description? Have something to add? Feel free to write us 👾
Related Identifiers
Affected Products
Praisonai-Platform