The Keycloak Upgrade Ledger

Every upgrade we have rehearsed, with the environment stated and the clock running.

2026-09-06 · run 2026-09-06-cve-2026-14613-role-groups-disclosure

CVE-2026-14613: role→groups endpoint filters hidden groups under FGAP v2

Condition
CVE-2026-14613 (FGAP v2: an admin who can see a role can read all groups assigned to it — the per-group view check was missing)

Question

Does 26.7.3 enforce the CVE-2026-14613 fix — i.e. does GET /admin/realms/{realm}/roles/{role}/groups (and the client-role variant) now filter the returned groups by the caller's per-group view permission, instead of returning every group that has the role?

Method

Built the FGAP-v2 "delegated admin" fixture in a fresh realm (adminPermissionsEnabled=true) and exercised the endpoint as that admin. The delegated admin is a service-account (client-credentials) subject, not a password user: ROPC against a hand-rolled realm hits the undiagnosed "Account is not fully set up" quirk we have hit repeatedly in this lab, and a service-account token evaluates FGAP v2 identically.

Fixture (reproducible via bin/fgap-rbac-cves.sh):

  1. Realm fgap-cves, adminPermissionsEnabled=true.
  2. Client fgap-delegate (confidential, service account); its service-account user is the delegated admin.
  3. Grant the service-account user view-realm + query-groups (realm-management client roles) — view-realm to reach realm-level role endpoints, query-groups so getGroupsInRole's requireList() gate passes and the per-group filter is what is actually exercised.
  4. Realm role visible-realm-role; groups visible-group and hidden-group; the role mapped to both groups.
  5. FGAP permission: a user policy on the admin-permissions client referencing the service-account user, and a scope permission granting view on visible-group only (resource type Groups, resource = visible-group id).

Then, as the delegated admin:

Result

CallResponse
GET .../groups/{hidden-group}HTTP 403
GET .../groups/{visible-group}HTTP 200
GET .../roles/visible-realm-role/groups200[{"name":"visible-group",...}] only; hidden-group absent

Finding

26.7.3 filters the role→groups result by per-group view — the caller sees visible-group and not hidden-group. The fix is two-part and both are present:

Primary source: commit dc0f68bd235d816fc2e7a3e690a3c6fd237ec7b1 "Fine-Grained Admin Permissions Bypass via Role Groups Endpoint" (closes #50616).

Verification

ClaimPrimary sourceChecked
Fix adds requireList() to getGroupsInRolecommit dc0f68bd diff, RoleContainerResource.java
Fix filters getGroupsByRoleStream by group authorizationcommit dc0f68bd diff, JpaRealmProvider.java (applyAuthorizationFilters(GROUPS))
Hidden group excluded, visible group returnedlive GET /roles/visible-realm-role/groups response quoted above
Direct read of hidden group is already 403 (per-group view)live GET /groups/{hidden-group} → 403

← Back to the Ledger