From 7d0b33b7e77a8e8a0eaba77c992c365e661eb8ba Mon Sep 17 00:00:00 2001 From: Manish Gupta Date: Mon, 29 Jun 2026 17:34:55 +0530 Subject: [PATCH] [WEB-7895] fix: scope UserProjectInvitationsViewset to workspace-validated project IDs (GHSA-45hc-q4mw-jhxm) The `create` handler validated the network (SECRET/PUBLIC) check against a workspace-scoped queryset but then used the raw client-supplied `project_ids` list in the subsequent bulk_create and update calls. An attacker could include UUIDs of projects from other workspaces: those are absent from the validation queryset (no network check performed), yet get inserted as ProjectMember rows via bulk_create(ignore_conflicts=True), granting cross-workspace project access. Fix: derive `validated_project_ids` from the filtered queryset (projects already scoped to the requested workspace and passed the SECRET check), and use it exclusively for all subsequent DB writes. Co-authored-by: Plane AI --- apps/api/plane/app/views/project/invite.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/apps/api/plane/app/views/project/invite.py b/apps/api/plane/app/views/project/invite.py index 19d8c36bcf7..382a466c382 100644 --- a/apps/api/plane/app/views/project/invite.py +++ b/apps/api/plane/app/views/project/invite.py @@ -145,10 +145,16 @@ def create(self, request, slug): workspace_role = workspace_member.role workspace = workspace_member.workspace + # Use the workspace-scoped, network-validated project IDs only. + # Raw project_ids may contain UUIDs from other workspaces; those are + # absent from the `projects` queryset and therefore bypass the SECRET + # network check above (GHSA-45hc-q4mw-jhxm). + validated_project_ids = [str(p.id) for p in projects] + # If the user was already part of workspace - _ = ProjectMember.objects.filter(workspace__slug=slug, project_id__in=project_ids, member=request.user).update( - is_active=True - ) + _ = ProjectMember.objects.filter( + workspace__slug=slug, project_id__in=validated_project_ids, member=request.user + ).update(is_active=True) ProjectMember.objects.bulk_create( [ @@ -159,7 +165,7 @@ def create(self, request, slug): workspace=workspace, created_by=request.user, ) - for project_id in project_ids + for project_id in validated_project_ids ], ignore_conflicts=True, ) @@ -172,7 +178,7 @@ def create(self, request, slug): workspace=workspace, created_by=request.user, ) - for project_id in project_ids + for project_id in validated_project_ids ], ignore_conflicts=True, )