Skip to content

Prevent privilege escalation through role creation/update/assignment - #26914

Draft
dennisoelkers wants to merge 11 commits into
masterfrom
security/block-privilege-escalation-via-roles
Draft

Prevent privilege escalation through role creation/update/assignment#26914
dennisoelkers wants to merge 11 commits into
masterfrom
security/block-privilege-escalation-via-roles

Conversation

@dennisoelkers

Copy link
Copy Markdown
Member

Note: This is based on #26883, which needs to be merged before.
Note: This needs a backport to previous, supported versions.

Description

Motivation and Context

This PR is extending role creation/updating/assignment by a check that verifies that the current user holds (at least) the same permissions that the role that is created/updated/assigned contains. This is supposed to prevent privilege escalation through creating a role with more permissions that the current user has and assigning it to the current or another user. Same for updating an existing role with elevated privileges, which is already assigned to the current/other users.

For this, we reuse the PermissionsValidator in RolesResource so that a user cannot grant permissions they do not hold themselves:

  • POST /roles validates the permissions of the new role.
  • PUT /roles/{rolename} validates the permissions of the updated role.
  • PUT /roles/{rolename}/members/{username} validates the permissions granted by the role before assigning it.

Previously roles:create/roles:edit/roles:assign were sufficient to mint or assign a role granting arbitrary permissions, which bypassed the equivalent checks already added to the user endpoints.

How Has This Been Tested?

Screenshots (if appropriate):

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Refactoring (non-breaking change)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist:

  • My code follows the code style of this project.
  • My change requires a change to the documentation.
  • I have requested a documentation update.
  • I have read the CONTRIBUTING document.
  • I have added tests to cover my changes.

dennisoelkers and others added 7 commits August 4, 2026 17:06
A user holding only `users:create` was able to create another user with
more permissions than they hold themselves, either by supplying the
permissions directly or by assigning a role that grants them.

`UsersResource#create` now collects the permissions requested for the new
user, resolves the permissions granted by the requested roles, and refuses
the request with a `BadRequestException` if the current user is missing at
least one of them. Reading the requested roles additionally requires
`roles:read` on each role.
Cover `UsersResource#changeUser` with the two escalation cases that were
only tested for user creation so far: assigning a permission the editing
user does not hold, and assigning a role that grants such a permission.

In both cases the editing user is granted `users:permissionsedit` /
`users:rolesedit` on the target, so the submitted fields would actually be
applied and the rejection can only come from the escalation check.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
dennisoelkers added a commit that referenced this pull request Aug 6, 2026
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
dennisoelkers and others added 4 commits August 6, 2026 14:28
Reuse PermissionsValidator in RolesResource so that a user cannot grant
permissions they do not hold themselves:

- `POST /roles` validates the permissions of the new role.
- `PUT /roles/{rolename}` validates the permissions of the updated role.
- `PUT /roles/{rolename}/members/{username}` validates the permissions
  granted by the role before assigning it.

Previously `roles:create`/`roles:edit`/`roles:assign` were sufficient to
mint or assign a role granting arbitrary permissions, which bypassed the
equivalent checks already added to the user endpoints.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Covers all three validated endpoints in RolesResource, for both the
allowed and the rejected case, plus the wildcard edge cases:

- holding `streams:*` allows granting `streams:read`
- holding `streams:read:12345` does NOT allow granting `streams:read`
- removing a member stays unvalidated, since un-assigning a role
  reduces privileges

The tests use a real PermissionsValidator so they exercise the actual
Shiro permission resolution rather than asserting on a mock.

Verified by mutation: commenting out the three validatePermissions calls
fails 7 of these tests.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@dennisoelkers
dennisoelkers force-pushed the security/block-privilege-escalation-via-roles branch from 439c921 to bf81255 Compare August 6, 2026 12:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant