From e1ef42023ab66b5e722a8750e1bc5ba0d413a3ee Mon Sep 17 00:00:00 2001 From: Manish Gupta <59428681+mguptahub@users.noreply.github.com> Date: Thu, 9 Jul 2026 18:32:22 +0530 Subject: [PATCH] [WEB-7895] fix: scope UserProjectInvitationsViewset to workspace-validated project IDs (GHSA-45hc-q4mw-jhxm) (#9333) 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 c5dd0cbb65..45c997596f 100644 --- a/apps/api/plane/app/views/project/invite.py +++ b/apps/api/plane/app/views/project/invite.py @@ -148,10 +148,16 @@ class UserProjectInvitationsViewset(BaseViewSet): 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( [ @@ -162,7 +168,7 @@ class UserProjectInvitationsViewset(BaseViewSet): workspace=workspace, created_by=request.user, ) - for project_id in project_ids + for project_id in validated_project_ids ], ignore_conflicts=True, ) @@ -175,7 +181,7 @@ class UserProjectInvitationsViewset(BaseViewSet): workspace=workspace, created_by=request.user, ) - for project_id in project_ids + for project_id in validated_project_ids ], ignore_conflicts=True, )