From 875cfb48dafb2c4c58cb436a6494c3240b4821d7 Mon Sep 17 00:00:00 2001 From: sriramveeraghanta Date: Tue, 31 Mar 2026 17:15:20 +0530 Subject: [PATCH] fix: prevent privilege escalation in project member role updates (GHSA-494h-3rcq-5g3c) Restrict role modification in ProjectMemberViewSet.partial_update to Admins only and enforce that requesters cannot modify or assign roles equal to or higher than their own. Previously, Guests could demote Admins by exploiting a missing lower-bound check on role changes. Co-Authored-By: Claude Opus 4.6 (1M context) --- apps/api/plane/app/views/project/member.py | 43 +++++++++++++++------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/apps/api/plane/app/views/project/member.py b/apps/api/plane/app/views/project/member.py index 1ad7639fb8b..7dfe7090013 100644 --- a/apps/api/plane/app/views/project/member.py +++ b/apps/api/plane/app/views/project/member.py @@ -226,21 +226,36 @@ def partial_update(self, request, slug, project_id, pk): is_active=True, ) - if workspace_role in [5] and int(request.data.get("role", project_member.role)) in [15, 20]: - return Response( - {"error": "You cannot add a user with role higher than the workspace role"}, - status=status.HTTP_400_BAD_REQUEST, - ) + if "role" in request.data: + # Only Admins can modify roles + if requested_project_member.role < ROLE.ADMIN.value and not is_workspace_admin: + return Response( + {"error": "You do not have permission to update roles"}, + status=status.HTTP_403_FORBIDDEN, + ) - if ( - "role" in request.data - and int(request.data.get("role", project_member.role)) > requested_project_member.role - and not is_workspace_admin - ): - return Response( - {"error": "You cannot update a role that is higher than your own role"}, - status=status.HTTP_400_BAD_REQUEST, - ) + # Cannot modify a member whose role is equal to or higher than your own + if project_member.role >= requested_project_member.role and not is_workspace_admin: + return Response( + {"error": "You cannot update the role of a member with a role equal to or higher than your own"}, + status=status.HTTP_403_FORBIDDEN, + ) + + new_role = int(request.data.get("role")) + + # Cannot assign a role equal to or higher than your own + if new_role >= requested_project_member.role and not is_workspace_admin: + return Response( + {"error": "You cannot assign a role equal to or higher than your own"}, + status=status.HTTP_403_FORBIDDEN, + ) + + # Cannot assign a role higher than the target's workspace role + if workspace_role in [5] and new_role in [15, 20]: + return Response( + {"error": "You cannot add a user with role higher than the workspace role"}, + status=status.HTTP_400_BAD_REQUEST, + ) serializer = ProjectMemberSerializer(project_member, data=request.data, partial=True)