Duplicate of GHSA-85h2-mhcc-xfmw.
This report describes the same underlying bug: WorkspaceFileAssetEndpoint.get scopes file lookups to the workspace instead of the project, letting any workspace member read another project's files. The v1.4 test file cites this report together with GHSA-cjph-cgm5-8pw8; GHSA-85h2-mhcc-xfmw reported the same handler three weeks earlier. See GHSA-85h2-mhcc-xfmw for the authoritative report and remediation details. This advisory is being withdrawn to avoid duplicate tracking; the reporter's credit for this finding has been carried over to the surviving advisory.
Summary
The fix for GHSA-qw87-v5w3-6vxx (patched in v1.3.1) added @allow_permission(level="WORKSPACE") to WorkspaceFileAssetEndpoint, which blocks cross-workspace access. However, the fix is incomplete: the handler still does not check project membership for project-scoped assets (e.g., ISSUE_ATTACHMENT). Any workspace member — even a GUEST who belongs to a different project — can download confidential issue attachments from projects they are not a member of.
Details
Vulnerable code — apps/api/plane/app/views/asset/v2.py line 413:
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST], level="WORKSPACE")
def get(self, request, slug, asset_id):
asset = FileAsset.objects.get(id=asset_id, workspace__slug=slug)
# ← NO project membership check
# ← NO entity_type-based access control
signed_url = storage.generate_presigned_url(...)
return HttpResponseRedirect(signed_url)
The query filters only by workspace__slug — any workspace member passes. The project-scoped twin endpoint (ProjectFileAssetEndpoint) correctly adds project_id= to the filter, but this workspace-level endpoint does not.
Note: StaticFileAssetEndpoint explicitly refuses non-cover entity types (only allows USER_AVATAR, USER_COVER, WORKSPACE_LOGO, PROJECT_COVER), confirming issue attachments are NOT meant to be publicly accessible.
Endpoint: GET /api/assets/v2/workspaces/<slug>/<asset_id>/
PoC
Self-contained, idempotent, stdlib-only Python script:
BASE=http://localhost:8000 python3 poc_asset_bola.py
Requires two files: poc_asset_bola.py + plane_poc.py (shared library). Both attached as a comment.
Output from live run:
[base ] owner GET asset -> HTTP 302
[ctrl ] guest project-members/me on A's project -> HTTP 404 (outsider to the project)
[attack] guest GET A's issue attachment -> HTTP 302
[ctrl ] non-workspace outsider GET asset -> HTTP 403 (denied as expected)
PASS — workspace guest read a confidential attachment from a project they are not in
Impact
- Confidentiality (High): Any workspace member (even a GUEST in a different project) can read confidential attachments from projects they have no access to — internal documents, screenshots, sensitive files attached to private issues.
-
- CVSS 3.1: 6.5 (Medium) —
AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N
-
- CWE-639 (Authorization Bypass Through User-Controlled Key)
Suggested Fix
Add project membership verification for project-scoped entity types:
if asset.entity_type in PROJECT_SCOPED_ENTITY_TYPES:
if not ProjectMember.objects.filter(
project_id=asset.project_id, member=request.user, is_active=True
).exists():
return Response(status=status.HTTP_403_FORBIDDEN)
Summary
The fix for GHSA-qw87-v5w3-6vxx (patched in v1.3.1) added
@allow_permission(level="WORKSPACE")toWorkspaceFileAssetEndpoint, which blocks cross-workspace access. However, the fix is incomplete: the handler still does not check project membership for project-scoped assets (e.g.,ISSUE_ATTACHMENT). Any workspace member — even a GUEST who belongs to a different project — can download confidential issue attachments from projects they are not a member of.Details
Vulnerable code —
apps/api/plane/app/views/asset/v2.pyline 413:The query filters only by
workspace__slug— any workspace member passes. The project-scoped twin endpoint (ProjectFileAssetEndpoint) correctly addsproject_id=to the filter, but this workspace-level endpoint does not.Note:
StaticFileAssetEndpointexplicitly refuses non-cover entity types (only allowsUSER_AVATAR,USER_COVER,WORKSPACE_LOGO,PROJECT_COVER), confirming issue attachments are NOT meant to be publicly accessible.Endpoint:
GET /api/assets/v2/workspaces/<slug>/<asset_id>/PoC
Self-contained, idempotent, stdlib-only Python script:
Requires two files:
poc_asset_bola.py+plane_poc.py(shared library). Both attached as a comment.Output from live run:
Impact
AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:NSuggested Fix
Add project membership verification for project-scoped entity types: