Low severity2.7GHSA Advisory· Published Mar 11, 2026· Updated May 7, 2026
CVE-2026-3911
CVE-2026-3911
Description
A flaw was found in Keycloak. An authenticated user with the view-users role could exploit a vulnerability in the UserResource component. By accessing a specific administrative endpoint, this user could improperly retrieve user attributes that were configured to be hidden. This unauthorized information disclosure could expose sensitive user data.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
org.keycloak:keycloak-servicesMaven | <= 26.5.5 | — |
Affected products
1Patches
1215bc1e27230Do not return managed attribute as unmanaged if admin has no view permission
3 files changed · +51 −10
services/src/main/java/org/keycloak/services/resources/admin/UserResource.java+1 −10 modified@@ -1252,18 +1252,9 @@ public void joinGroup(@PathParam("groupId") String groupId) { public Map<String, List<String>> getUnmanagedAttributes() { auth.users().requireView(user); UserProfileProvider provider = session.getProvider(UserProfileProvider.class); - UserProfile profile = provider.create(USER_API, user); - Map<String, List<String>> managedAttributes = profile.getAttributes().getReadable(); - Map<String, List<String>> unmanagedAttributes = profile.getAttributes().getUnmanagedAttributes(); - managedAttributes.entrySet().removeAll(unmanagedAttributes.entrySet()); - Map<String, List<String>> attributes = new HashMap<>(user.getAttributes()); - attributes.entrySet().removeAll(managedAttributes.entrySet()); - - attributes.remove(UserModel.USERNAME); - attributes.remove(UserModel.EMAIL); - return attributes.entrySet().stream() + return profile.getAttributes().getUnmanagedAttributes().entrySet().stream() .filter(entry -> ofNullable(entry.getValue()).orElse(emptyList()).stream().anyMatch(StringUtil::isNotBlank)) .collect(Collectors.toMap(Entry::getKey, Entry::getValue)); }
tests/base/src/test/java/org/keycloak/tests/admin/user/UserProfileTest.java+45 −0 modified@@ -18,8 +18,11 @@ package org.keycloak.tests.admin.user; import java.util.List; +import java.util.Map; import java.util.Set; +import jakarta.ws.rs.core.Response; + import org.keycloak.admin.client.resource.UserProfileResource; import org.keycloak.admin.client.resource.UsersResource; import org.keycloak.models.UserModel; @@ -29,11 +32,13 @@ import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.representations.userprofile.config.UPAttributePermissions; import org.keycloak.representations.userprofile.config.UPConfig; +import org.keycloak.representations.userprofile.config.UPConfig.UnmanagedAttributePolicy; import org.keycloak.testframework.annotations.InjectRealm; import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.injection.LifeCycle; import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testframework.realm.UserConfigBuilder; +import org.keycloak.testframework.util.ApiUtil; import org.hamcrest.Matchers; import org.junit.jupiter.api.Test; @@ -176,4 +181,44 @@ public void defaultMaxResultsBrief() { upResource.update(upConfig); } } + + @Test + public void testGetUnmanagedAttributes() { + UserProfileResource upResource = managedRealm.admin().users().userProfile(); + UPConfig upConfig = upResource.getConfiguration(); + upConfig.addOrReplaceAttribute(createAttributeMetadata("aName")); + upResource.update(upConfig); + + try { + UsersResource users = managedRealm.admin().users(); + UserRepresentation user = UserConfigBuilder.create().username("test-user").attribute("aName", "aValue").build(); + try (Response response = users.create(user)) { + user.setId(ApiUtil.getCreatedId(response)); + } + Map<String, List<String>> unmanagedAttributes = users.get(user.getId()).getUnmanagedAttributes(); + assertTrue(unmanagedAttributes.isEmpty()); + + upConfig.setUnmanagedAttributePolicy(UnmanagedAttributePolicy.ENABLED); + upResource.update(upConfig); + user.getAttributes().put("unmanaged", List.of("value")); + users.get(user.getId()).update(user); + unmanagedAttributes = users.get(user.getId()).getUnmanagedAttributes(); + assertThat(unmanagedAttributes.keySet(), hasSize(1)); + upConfig.removeAttribute("aName"); + upResource.update(upConfig); + unmanagedAttributes = users.get(user.getId()).getUnmanagedAttributes(); + assertThat(unmanagedAttributes.keySet(), hasSize(2)); + upConfig.addOrReplaceAttribute(createAttributeMetadata("aName")); + upResource.update(upConfig); + unmanagedAttributes = users.get(user.getId()).getUnmanagedAttributes(); + assertThat(unmanagedAttributes.keySet(), hasSize(1)); + upConfig.getAttribute("aName").setPermissions(new UPAttributePermissions(Set.of("user"), Set.of("user"))); + upResource.update(upConfig); + unmanagedAttributes = users.get(user.getId()).getUnmanagedAttributes(); + assertThat(unmanagedAttributes.keySet(), hasSize(1)); + } finally { + upConfig.removeAttribute("aName"); + upResource.update(upConfig); + } + } }
tests/base/src/test/java/org/keycloak/tests/workflow/WorkflowScheduleTest.java+5 −0 modified@@ -6,6 +6,8 @@ import org.keycloak.admin.client.resource.UserResource; import org.keycloak.models.workflow.SetUserAttributeStepProviderFactory; +import org.keycloak.representations.userprofile.config.UPConfig; +import org.keycloak.representations.userprofile.config.UPConfig.UnmanagedAttributePolicy; import org.keycloak.representations.workflows.WorkflowRepresentation; import org.keycloak.representations.workflows.WorkflowScheduleRepresentation; import org.keycloak.representations.workflows.WorkflowStepRepresentation; @@ -32,6 +34,9 @@ public class WorkflowScheduleTest extends AbstractWorkflowTest { @Test public void testSchedule() { + UPConfig upConfig = managedRealm.admin().users().userProfile().getConfiguration(); + upConfig.setUnmanagedAttributePolicy(UnmanagedAttributePolicy.ADMIN_VIEW); + managedRealm.admin().users().userProfile().update(upConfig); WorkflowRepresentation expectedWorkflow = WorkflowRepresentation.withName("myworkflow") .schedule(WorkflowScheduleRepresentation.create().after("1s").batchSize(10).build()) .withSteps(
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
9- access.redhat.com/errata/RHSA-2026:6477nvdVendor AdvisoryWEB
- access.redhat.com/errata/RHSA-2026:6478nvdVendor AdvisoryWEB
- access.redhat.com/security/cve/CVE-2026-3911nvdVendor AdvisoryWEB
- github.com/advisories/GHSA-xh32-c9wx-phrpghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2026-3911ghsaADVISORY
- bugzilla.redhat.com/show_bug.cginvdIssue TrackingWEB
- github.com/keycloak/keycloak/commit/215bc1e27230f2a66670ed70262248b5f5254eb9ghsaWEB
- github.com/keycloak/keycloak/issues/46922ghsaWEB
- github.com/keycloak/keycloak/pull/46923ghsaWEB
News mentions
0No linked articles in our index yet.