Root Cause Analysis
The vulnerability exists in the TeamController API due to a misconfiguration of the IsGranted security attribute in multiple methods responsible for team management. The vulnerable methods used #[IsGranted('edit_team')] or #[IsGranted('delete_team')], which only checks for a user's role-level permissions. It does not perform an object-level authorization check to verify if the user is actually a member or leader of the specific team they are trying to modify.
The Symfony TeamVoter is designed to perform this object-level check, but it only supports the attributes 'edit' and 'delete', not 'edit_team' or 'delete_team'. Consequently, the voter would abstain, and the authorization check would fall back to a simple role check, which is insufficient.
An attacker with the edit_team permission (which could be granted by an administrator to a non-admin role) could exploit this to modify the membership and permissions of any team in the system, not just the ones they manage. The patch corrects this by changing the attribute to #[IsGranted('edit', 'team')] or #[IsGranted('delete', 'team')], which correctly passes the team object to the TeamVoter for a proper ownership check.
Vulnerable functions
App\API\TeamController::deleteActionsrc/API/TeamController.php
The function was protected by `#[IsGranted('delete_team')]`. This attribute only checks for the role-level permission 'delete_team' and does not pass the 'team' object to the voter. As a result, the `TeamVoter` abstains, and no object-level authorization (ownership check) is performed. This allows any user with the 'delete_team' permission to delete any team, not just the ones they are authorized to manage. The patch corrects this by using `#[IsGranted('delete', 'team')]`, which properly invokes the `TeamVoter` to perform ownership checks.
App\API\TeamController::patchActionsrc/API/TeamController.php
The function was protected by `#[IsGranted('edit_team')]`. This attribute only checks for the role-level permission 'edit_team' and does not pass the 'team' object to the voter. As a result, the `TeamVoter` abstains, and no object-level authorization (ownership check) is performed. This allows any user with the 'edit_team' permission to modify any team, not just the ones they are authorized to manage. The patch corrects this by using `#[IsGranted('edit', 'team')]`, which properly invokes the `TeamVoter` to perform ownership checks.
App\API\TeamController::postMemberActionsrc/API/TeamController.php
The function was protected by `#[IsGranted('edit_team')]`. This attribute only checks for the role-level permission 'edit_team' and does not pass the 'team' object to the voter. As a result, the `TeamVoter` abstains, and no object-level authorization (ownership check) is performed. This allows any user with the 'edit_team' permission to add a member to any team, not just the ones they are authorized to manage. The patch corrects this by using `#[IsGranted('edit', 'team')]`, which properly invokes the `TeamVoter` to perform ownership checks.
App\API\TeamController::deleteMemberActionsrc/API/TeamController.php
The function was protected by `#[IsGranted('edit_team')]`. This attribute only checks for the role-level permission 'edit_team' and does not pass the 'team' object to the voter. As a result, the `TeamVoter` abstains, and no object-level authorization (ownership check) is performed. This allows any user with the 'edit_team' permission to remove a member from any team, not just the ones they are authorized to manage. The patch corrects this by using `#[IsGranted('edit', 'team')]`, which properly invokes the `TeamVoter` to perform ownership checks.
App\API\TeamController::postCustomerActionsrc/API/TeamController.php
The function was protected by `#[IsGranted('edit_team')]`. This attribute only checks for the role-level permission 'edit_team' and does not pass the 'team' object to the voter. As a result, the `TeamVoter` abstains, and no object-level authorization (ownership check) is performed. This allows any user with the 'edit_team' permission to grant customer access to any team, not just the ones they are authorized to manage. The patch corrects this by using `#[IsGranted('edit', 'team')]`, which properly invokes the `TeamVoter` to perform ownership checks.
App\API\TeamController::deleteCustomerActionsrc/API/TeamController.php
The function was protected by `#[IsGranted('edit_team')]`. This attribute only checks for the role-level permission 'edit_team' and does not pass the 'team' object to the voter. As a result, the `TeamVoter` abstains, and no object-level authorization (ownership check) is performed. This allows any user with the 'edit_team' permission to revoke customer access from any team, not just the ones they are authorized to manage. The patch corrects this by using `#[IsGranted('edit', 'team')]`, which properly invokes the `TeamVoter` to perform ownership checks.
App\API\TeamController::postProjectActionsrc/API/TeamController.php
The function was protected by `#[IsGranted('edit_team')]`. This attribute only checks for the role-level permission 'edit_team' and does not pass the 'team' object to the voter. As a result, the `TeamVoter` abstains, and no object-level authorization (ownership check) is performed. This allows any user with the 'edit_team' permission to grant project access to any team, not just the ones they are authorized to manage. The patch corrects this by using `#[IsGranted('edit', 'team')]`, which properly invokes the `TeamVoter` to perform ownership checks.
App\API\TeamController::deleteProjectActionsrc/API/TeamController.php
The function was protected by `#[IsGranted('edit_team')]`. This attribute only checks for the role-level permission 'edit_team' and does not pass the 'team' object to the voter. As a result, the `TeamVoter` abstains, and no object-level authorization (ownership check) is performed. This allows any user with the 'edit_team' permission to revoke project access from any team, not just the ones they are authorized to manage. The patch corrects this by using `#[IsGranted('edit', 'team')]`, which properly invokes the `TeamVoter` to perform ownership checks.
App\API\TeamController::postActivityActionsrc/API/TeamController.php
The function was protected by `#[IsGranted('edit_team')]`. This attribute only checks for the role-level permission 'edit_team' and does not pass the 'team' object to the voter. As a result, the `TeamVoter` abstains, and no object-level authorization (ownership check) is performed. This allows any user with the 'edit_team' permission to grant activity access to any team, not just the ones they are authorized to manage. The patch corrects this by using `#[IsGranted('edit', 'team')]`, which properly invokes the `TeamVoter` to perform ownership checks.
App\API\TeamController::deleteActivityActionsrc/API/TeamController.php
The function was protected by `#[IsGranted('edit_team')]`. This attribute only checks for the role-level permission 'edit_team' and does not pass the 'team' object to the voter. As a result, the `TeamVoter` abstains, and no object-level authorization (ownership check) is performed. This allows any user with the 'edit_team' permission to revoke activity access from any team, not just the ones they are authorized to manage. The patch corrects this by using `#[IsGranted('edit', 'team')]`, which properly invokes the `TeamVoter` to perform ownership checks.