.agents/skills/sentry-security/references/privilege-escalation.md
Sentry's organization roles from lowest to highest:
member → admin → manager → owner
Team roles:
contributor → admin
Rule: A user can only modify roles at or below their own level.
Contributors could downgrade org admins' team roles. Low-privilege members could downgrade other members' roles.
Pattern to check: Any endpoint that modifies a user's role must verify:
# WRONG: No role comparison
def update_member_role(self, request, member):
member.role = request.data["role"]
member.save()
# RIGHT: Verify requesting user has sufficient role
def update_member_role(self, request, member):
new_role = request.data["role"]
requesting_member = OrganizationMember.objects.get(
user_id=request.user.id, organization_id=member.organization_id
)
# Can't set a role higher than your own
if roles.get(new_role).priority > roles.get(requesting_member.role).priority:
raise PermissionDenied()
# Can't modify someone with a higher role than yours
if roles.get(member.role).priority > roles.get(requesting_member.role).priority:
raise PermissionDenied()
member.role = new_role
member.save()
Replay endpoints checked for is_superuser instead of is_active_staff(), which doesn't verify the superuser session is active.
Correct patterns:
from sentry.auth.superuser import is_active_superuser
from sentry.auth.staff import is_active_staff
# WRONG: Checks the flag, not the active session
if request.user.is_superuser:
...
# RIGHT: Checks active superuser session
if is_active_superuser(request):
...
# RIGHT: Checks active staff session
if is_active_staff(request):
...
Staff could change superuser/staff privileges without verifying the target user was a member of the default org.
Pattern to check: Privilege escalation operations (granting superuser, staff) must verify org membership and other preconditions.
□ Role modification compares requesting user's role to target's current role
□ Role modification compares requesting user's role to the new role being set
□ Superuser checks use is_active_superuser(request), not request.user.is_superuser
□ Staff checks use is_active_staff(request)
□ Privilege changes verify org membership preconditions
□ Team role changes respect org-level role hierarchy