VYPR
High severityNVD Advisory· Published Dec 18, 2023· Updated Nov 7, 2025

Infinispan: non-admins should not be able to get cache config via rest api

CVE-2023-3629

Description

Infinispan REST cache retrieval endpoints missing admin permission checks allow authenticated users to access unauthorized cache configuration data.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Infinispan REST cache retrieval endpoints missing admin permission checks allow authenticated users to access unauthorized cache configuration data.

Vulnerability

Overview

CVE-2023-3629 is a flaw in Infinispan's REST API where cache retrieval endpoints fail to properly evaluate administrative permissions. The root cause is that operations intended only for users with admin roles, such as retrieving cache configuration details, are incorrectly exposed to any authenticated user through the REST interface. The official description states that the endpoints "do not properly evaluate the necessary admin permissions for the operation" [1][2].

Exploitation

An attacker needs only to be an authenticated user of the Infinispan cluster; no special admin privileges are required. By sending requests to the affected REST endpoints (e.g., cache/{name}/configuration), an authenticated user can retrieve configuration data and other cache details that should be restricted to administrators. The fix commits show that the test code was modified to use an adminClient object instead of a generic client when calling these endpoints, confirming the access control gap [3][4].

Impact

Successful exploitation allows an authenticated user to access cache configuration information outside of their intended permissions. This could expose sensitive settings, cache schemas, or operational details that aid further attacks. The vulnerability does not require any special network position beyond API access.

Mitigation

Red Hat has acknowledged the issue, and the fix is included in Infinispan releases starting with versions that incorporate the commits referenced [3][4]. Users should update to a patched version. There is no indication that this CVE is listed in CISA's Known Exploited Vulnerabilities (KEV) catalog.

AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
org.infinispan:infinispan-server-restMaven
>= 15.0.0.Dev01, < 15.0.0.Dev0415.0.0.Dev04
org.infinispan:infinispan-server-restMaven
< 14.0.18.Final14.0.18.Final

Affected products

15

Patches

2
11b3cb0f7ba6

ISPN-14986 CVE-2023-3629 Non-admins should not be able to retrieve cache configuration

https://github.com/infinispan/infinispanTristan TarrantAug 9, 2023via ghsa
3 files changed · +60 20
  • server/rest/src/main/java/org/infinispan/rest/resources/CacheResourceV2.java+23 3 modified
    @@ -115,6 +115,7 @@
     import org.infinispan.rest.stream.CacheKeyStreamProcessor;
     import org.infinispan.rest.tracing.RestTelemetryService;
     import org.infinispan.security.AuditContext;
    +import org.infinispan.security.AuthorizationManager;
     import org.infinispan.security.AuthorizationPermission;
     import org.infinispan.security.actions.SecurityActions;
     import org.infinispan.stats.Stats;
    @@ -686,6 +687,8 @@ private CompletionStage<RestResponse> getAllDetails(RestRequest request) {
        }
     
        private RestResponse getDetailResponse(RestRequest request, Cache<?, ?> cache, boolean pretty) {
    +      // We escalate privileges to obtain various items of the configuration, but we need to take care about
    +      // the details included in the response
           Configuration configuration = SecurityActions.getCacheConfiguration(cache.getAdvancedCache());
           EmbeddedCacheManager cacheManager = invocationHelper.getRestCacheManager().getInstance();
           GlobalConfiguration globalConfiguration = SecurityActions.getCacheManagerConfiguration(cacheManager);
    @@ -736,7 +739,16 @@ private RestResponse getDetailResponse(RestRequest request, Cache<?, ?> cache, b
           try (ConfigurationWriter w = ConfigurationWriter.to(sw).withType(APPLICATION_JSON).prettyPrint(pretty).build()) {
              invocationHelper.getParserRegistry().serialize(w, cache.getName(), configuration);
           }
    -      fullDetail.configuration = sw.toString();
    +      // Only include the full configuration if ADMIN
    +      AuthorizationManager authorizationManager = SecurityActions.getCacheAuthorizationManager(cache.getAdvancedCache());
    +      if (authorizationManager == null || authorizationManager.isPermissive()) {
    +         // Cache is not secured, use the global authz
    +         if (invocationHelper.getRestCacheManager().getAuthorizer().getPermissions(null, request.getSubject()).contains(AuthorizationPermission.ADMIN)) {
    +            fullDetail.configuration = sw.toString();
    +         }
    +      } else {
    +         authorizationManager.doIf(request.getSubject(), AuthorizationPermission.ADMIN, () -> fullDetail.configuration = sw.toString());
    +      }
           fullDetail.size = size;
           fullDetail.rehashInProgress = rehashInProgress;
           fullDetail.indexingInProgress = indexingInProgress;
    @@ -779,7 +791,12 @@ private CompletionStage<RestResponse> getCacheConfig(RestRequest request) {
           if (cache == null)
              return invocationHelper.newResponse(request, NOT_FOUND).toFuture();
     
    -      Configuration cacheConfiguration = SecurityActions.getCacheConfiguration(cache.getAdvancedCache());
    +      AuthorizationManager authorizationManager = SecurityActions.getCacheAuthorizationManager(cache.getAdvancedCache());
    +      if (authorizationManager == null || authorizationManager.isPermissive()) {
    +         // Cache is not secured, use the global authz
    +         invocationHelper.getRestCacheManager().getAuthorizer().checkPermission(AuthorizationPermission.ADMIN);
    +      }
    +      Configuration cacheConfiguration = cache.getCacheConfiguration();
     
           ByteArrayOutputStream entity = new ByteArrayOutputStream();
           try (ConfigurationWriter writer = ConfigurationWriter.to(entity).withType(accept).prettyPrint(pretty).build()) {
    @@ -1053,8 +1070,11 @@ public Json toJson() {
                 json.set("rebalancing_enabled", rebalancingEnabled);
              }
     
    +         if (configuration != null) {
    +            json.set("configuration", Json.factory().raw(configuration));
    +         }
    +
              return json
    -               .set("configuration", Json.factory().raw(configuration))
                    .set("bounded", bounded)
                    .set("indexed", indexed)
                    .set("persistent", persistent)
    
  • server/rest/src/test/java/org/infinispan/rest/resources/CacheResourceV2Test.java+15 17 modified
    @@ -361,7 +361,7 @@ public void testCreateAndAlterCache() {
           response = cacheClient.updateWithConfiguration(RestEntity.create(APPLICATION_JSON, cacheConfigAlter));
           assertThat(response).isOk();
     
    -      response = cacheClient.configuration();
    +      response = adminClient.cache("mutable").configuration();
           assertThat(response).isOk();
           String configFromServer = join(response).getBody();
     
    @@ -474,17 +474,17 @@ public void testCacheV2LifeCycle() throws Exception {
           assertPersistence("cache2", true);
     
           String mediaList = "application/json,text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
    -      response = client.cache("cache1").configuration(mediaList);
    +      response = adminClient.cache("cache1").configuration(mediaList);
           assertThat(response).isOk();
           String cache1Cfg = join(response).getBody();
     
    -      response = client.cache("cache2").configuration();
    +      response = adminClient.cache("cache2").configuration();
           assertThat(response).isOk();
           String cache2Cfg = join(response).getBody();
     
           assertEquals(cache1Cfg, cache2Cfg.replace("cache2", "cache1"));
     
    -      response = client.cache("cache1").configuration("application/xml");
    +      response = adminClient.cache("cache1").configuration("application/xml");
           assertThat(response).isOk();
           String cache1Xml = join(response).getBody();
     
    @@ -493,7 +493,7 @@ public void testCacheV2LifeCycle() throws Exception {
           assertEquals(1200000, xmlConfig.clustering().l1().lifespan());
           assertEquals(60500, xmlConfig.clustering().stateTransfer().timeout());
     
    -      response = client.cache("cache1").configuration("application/xml; q=0.9");
    +      response = adminClient.cache("cache1").configuration("application/xml; q=0.9");
           assertThat(response).isOk();
        }
     
    @@ -680,7 +680,7 @@ public void testCacheSize() {
     
        @Test
        public void testCacheFullDetail() {
    -      RestResponse response = join(client.cache("default").details());
    +      RestResponse response = join(adminClient.cache("default").details());
           Json document = Json.read(response.getBody());
           assertThat(response).isOk();
           assertThat(document.at("stats")).isNotNull();
    @@ -698,6 +698,14 @@ public void testCacheFullDetail() {
           assertThat(document.at("key_storage").asString()).isEqualTo("application/unknown");
           assertThat(document.at("value_storage").asString()).isEqualTo("application/unknown");
     
    +      // non admins should have an empty config
    +      if (security) {
    +         response = join(client.cache("default").details());
    +         document = Json.read(response.getBody());
    +         assertThat(response).isOk();
    +         assertThat(document.at("configuration")).isNull();
    +      }
    +
           response = join(client.cache("proto").details());
           document = Json.read(response.getBody());
           assertThat(document.at("key_storage").asString()).isEqualTo("application/x-protostream");
    @@ -1292,12 +1300,6 @@ public void testVersionMetadata() {
           }
        }
     
    -   @Test
    -   public void testGetProtoCacheConfig() {
    -      testGetProtoCacheConfig(APPLICATION_XML_TYPE);
    -      testGetProtoCacheConfig(APPLICATION_JSON_TYPE);
    -   }
    -
        @Test
        public void testRebalancingActions() {
           String cacheName = "default";
    @@ -1326,12 +1328,8 @@ private void assertRebalancingStatus(String cacheName, boolean enabled) {
           }
        }
     
    -   private void testGetProtoCacheConfig(String accept) {
    -      getCacheConfig(accept, PROTOBUF_METADATA_CACHE_NAME);
    -   }
    -
        private String getCacheConfig(String accept, String name) {
    -      RestResponse response = join(client.cache(name).configuration(accept));
    +      RestResponse response = join(adminClient.cache(name).configuration(accept));
           assertThat(response).isOk();
           return response.getBody();
        }
    
  • server/tests/src/test/java/org/infinispan/server/security/authorization/RESTAuthorizationTest.java+22 0 modified
    @@ -14,6 +14,8 @@
     import static org.infinispan.server.test.core.Common.sync;
     import static org.junit.jupiter.api.Assertions.assertEquals;
     import static org.junit.jupiter.api.Assertions.assertFalse;
    +import static org.junit.jupiter.api.Assertions.assertNotNull;
    +import static org.junit.jupiter.api.Assertions.assertNull;
     import static org.junit.jupiter.api.Assertions.assertTrue;
     
     import java.io.Closeable;
    @@ -189,6 +191,26 @@ public void testRestNonAdminsMustNotCreateCache() {
           }
        }
     
    +   @Test
    +   public void testRestNonAdminsMustNotReadCacheConfig() {
    +      restCreateAuthzCache();
    +      String name = ext.getMethodName();
    +      for (TestUser user : EnumSet.of(TestUser.ADMIN)) {
    +         RestClient client = ext.rest().withClientConfiguration(restBuilders.get(user)).get();
    +         assertStatus(OK, client.cache(name).configuration());
    +         String details = assertStatus(OK, client.cache(name).details());
    +         Json json = Json.read(details);
    +         assertNotNull(json.asJsonMap().get("configuration"));
    +      }
    +      for (TestUser user : EnumSet.of(TestUser.APPLICATION, TestUser.OBSERVER, TestUser.MONITOR)) {
    +         RestClient client = ext.rest().withClientConfiguration(restBuilders.get(user)).get();
    +         assertStatus(FORBIDDEN, client.cache(name).configuration());
    +         String details = assertStatus(OK, client.cache(name).details());
    +         Json json = Json.read(details);
    +         assertNull(json.asJsonMap().get("configuration"));
    +      }
    +   }
    +
        @Test
        public void testRestWriterCannotReadImplicit() {
           testRestWriterCannotRead();
    
1e3cc542336d

ISPN-14986 CVE-2023-3629 Non-admins should not be able to retrieve cache configuration

https://github.com/infinispan/infinispanTristan TarrantAug 9, 2023via ghsa
4 files changed · +65 20
  • server/rest/src/main/java/org/infinispan/rest/resources/CacheResourceV2.java+23 3 modified
    @@ -114,6 +114,7 @@
     import org.infinispan.rest.stream.CacheKeyStreamProcessor;
     import org.infinispan.rest.tracing.RestTelemetryService;
     import org.infinispan.security.AuditContext;
    +import org.infinispan.security.AuthorizationManager;
     import org.infinispan.security.AuthorizationPermission;
     import org.infinispan.stats.Stats;
     import org.infinispan.topology.LocalTopologyManager;
    @@ -657,6 +658,8 @@ private CompletionStage<RestResponse> getAllDetails(RestRequest request) {
        }
     
        private RestResponse getDetailResponse(RestRequest request, Cache<?, ?> cache, boolean pretty) {
    +      // We escalate privileges to obtain various items of the configuration, but we need to take care about
    +      // the details included in the response
           Configuration configuration = SecurityActions.getCacheConfiguration(cache.getAdvancedCache());
           EmbeddedCacheManager cacheManager = invocationHelper.getRestCacheManager().getInstance();
           GlobalConfiguration globalConfiguration = SecurityActions.getCacheManagerConfiguration(cacheManager);
    @@ -707,7 +710,16 @@ private RestResponse getDetailResponse(RestRequest request, Cache<?, ?> cache, b
           try (ConfigurationWriter w = ConfigurationWriter.to(sw).withType(APPLICATION_JSON).prettyPrint(pretty).build()) {
              invocationHelper.getParserRegistry().serialize(w, cache.getName(), configuration);
           }
    -      fullDetail.configuration = sw.toString();
    +      // Only include the full configuration if ADMIN
    +      AuthorizationManager authorizationManager = SecurityActions.getCacheAuthorizationManager(cache.getAdvancedCache());
    +      if (authorizationManager == null || authorizationManager.isPermissive()) {
    +         // Cache is not secured, use the global authz
    +         if (invocationHelper.getRestCacheManager().getAuthorizer().getPermissions(null, request.getSubject()).contains(AuthorizationPermission.ADMIN)) {
    +            fullDetail.configuration = sw.toString();
    +         }
    +      } else {
    +         authorizationManager.doIf(request.getSubject(), AuthorizationPermission.ADMIN, () -> fullDetail.configuration = sw.toString());
    +      }
           fullDetail.size = size;
           fullDetail.rehashInProgress = rehashInProgress;
           fullDetail.indexingInProgress = indexingInProgress;
    @@ -750,7 +762,12 @@ private CompletionStage<RestResponse> getCacheConfig(RestRequest request) {
           if (cache == null)
              return invocationHelper.newResponse(request, NOT_FOUND).toFuture();
     
    -      Configuration cacheConfiguration = SecurityActions.getCacheConfiguration(cache.getAdvancedCache());
    +      AuthorizationManager authorizationManager = SecurityActions.getCacheAuthorizationManager(cache.getAdvancedCache());
    +      if (authorizationManager == null || authorizationManager.isPermissive()) {
    +         // Cache is not secured, use the global authz
    +         invocationHelper.getRestCacheManager().getAuthorizer().checkPermission(AuthorizationPermission.ADMIN);
    +      }
    +      Configuration cacheConfiguration = cache.getCacheConfiguration();
     
           ByteArrayOutputStream entity = new ByteArrayOutputStream();
           try (ConfigurationWriter writer = ConfigurationWriter.to(entity).withType(accept).prettyPrint(pretty).build()) {
    @@ -965,8 +982,11 @@ public Json toJson() {
                 json.set("rebalancing_enabled", rebalancingEnabled);
              }
     
    +         if (configuration != null) {
    +            json.set("configuration", Json.factory().raw(configuration));
    +         }
    +
              return json
    -               .set("configuration", Json.factory().raw(configuration))
                    .set("bounded", bounded)
                    .set("indexed", indexed)
                    .set("persistent", persistent)
    
  • server/rest/src/main/java/org/infinispan/rest/resources/SecurityActions.java+6 0 modified
    @@ -15,10 +15,12 @@
     import org.infinispan.persistence.manager.PersistenceManager;
     import org.infinispan.rest.InvocationHelper;
     import org.infinispan.rest.framework.RestRequest;
    +import org.infinispan.security.AuthorizationManager;
     import org.infinispan.security.AuthorizationPermission;
     import org.infinispan.security.Security;
     import org.infinispan.security.actions.AddCacheManagerListenerAsyncAction;
     import org.infinispan.security.actions.AddLoggerListenerAsyncAction;
    +import org.infinispan.security.actions.GetCacheAuthorizationManagerAction;
     import org.infinispan.security.actions.GetCacheComponentRegistryAction;
     import org.infinispan.security.actions.GetCacheConfigurationAction;
     import org.infinispan.security.actions.GetCacheConfigurationFromManagerAction;
    @@ -94,4 +96,8 @@ static void checkPermission(InvocationHelper invocationHelper, RestRequest reque
        static ClusterExecutor getClusterExecutor(EmbeddedCacheManager cacheManager) {
           return doPrivileged(cacheManager::executor);
        }
    +
    +   static AuthorizationManager getCacheAuthorizationManager(AdvancedCache<?,?> advancedCache) {
    +      return doPrivileged(new GetCacheAuthorizationManagerAction(advancedCache));
    +   }
     }
    
  • server/rest/src/test/java/org/infinispan/rest/resources/CacheResourceV2Test.java+15 17 modified
    @@ -335,7 +335,7 @@ public void testCreateAndAlterCache() {
           response = cacheClient.updateWithConfiguration(RestEntity.create(APPLICATION_JSON, cacheConfigAlter));
           assertThat(response).isOk();
     
    -      response = cacheClient.configuration();
    +      response = adminClient.cache("mutable").configuration();
           assertThat(response).isOk();
           String configFromServer = join(response).getBody();
     
    @@ -448,17 +448,17 @@ public void testCacheV2LifeCycle() throws Exception {
           assertPersistence("cache2", true);
     
           String mediaList = "application/json,text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
    -      response = client.cache("cache1").configuration(mediaList);
    +      response = adminClient.cache("cache1").configuration(mediaList);
           assertThat(response).isOk();
           String cache1Cfg = join(response).getBody();
     
    -      response = client.cache("cache2").configuration();
    +      response = adminClient.cache("cache2").configuration();
           assertThat(response).isOk();
           String cache2Cfg = join(response).getBody();
     
           assertEquals(cache1Cfg, cache2Cfg.replace("cache2", "cache1"));
     
    -      response = client.cache("cache1").configuration("application/xml");
    +      response = adminClient.cache("cache1").configuration("application/xml");
           assertThat(response).isOk();
           String cache1Xml = join(response).getBody();
     
    @@ -467,7 +467,7 @@ public void testCacheV2LifeCycle() throws Exception {
           assertEquals(1200000, xmlConfig.clustering().l1().lifespan());
           assertEquals(60500, xmlConfig.clustering().stateTransfer().timeout());
     
    -      response = client.cache("cache1").configuration("application/xml; q=0.9");
    +      response = adminClient.cache("cache1").configuration("application/xml; q=0.9");
           assertThat(response).isOk();
        }
     
    @@ -612,7 +612,7 @@ public void testCacheSize() {
     
        @Test
        public void testCacheFullDetail() {
    -      RestResponse response = join(client.cache("default").details());
    +      RestResponse response = join(adminClient.cache("default").details());
           Json document = Json.read(response.getBody());
           assertThat(response).isOk();
           assertThat(document.at("stats")).isNotNull();
    @@ -630,6 +630,14 @@ public void testCacheFullDetail() {
           assertThat(document.at("key_storage").asString()).isEqualTo("application/unknown");
           assertThat(document.at("value_storage").asString()).isEqualTo("application/unknown");
     
    +      // non admins should have an empty config
    +      if (security) {
    +         response = join(client.cache("default").details());
    +         document = Json.read(response.getBody());
    +         assertThat(response).isOk();
    +         assertThat(document.at("configuration")).isNull();
    +      }
    +
           response = join(client.cache("proto").details());
           document = Json.read(response.getBody());
           assertThat(document.at("key_storage").asString()).isEqualTo("application/x-protostream");
    @@ -1101,12 +1109,6 @@ public void testProtobufMetadataManipulation() {
           assertEquals(3, keys.size());
        }
     
    -   @Test
    -   public void testGetProtoCacheConfig() {
    -      testGetProtoCacheConfig(APPLICATION_XML_TYPE);
    -      testGetProtoCacheConfig(APPLICATION_JSON_TYPE);
    -   }
    -
        @Test
        public void testRebalancingActions() {
           String cacheName = "default";
    @@ -1135,12 +1137,8 @@ private void assertRebalancingStatus(String cacheName, boolean enabled) {
           }
        }
     
    -   private void testGetProtoCacheConfig(String accept) {
    -      getCacheConfig(accept, PROTOBUF_METADATA_CACHE_NAME);
    -   }
    -
        private String getCacheConfig(String accept, String name) {
    -      RestResponse response = join(client.cache(name).configuration(accept));
    +      RestResponse response = join(adminClient.cache(name).configuration(accept));
           assertThat(response).isOk();
           return response.getBody();
        }
    
  • server/tests/src/test/java/org/infinispan/server/security/authorization/AbstractAuthorization.java+21 0 modified
    @@ -15,6 +15,7 @@
     import static org.junit.Assert.assertEquals;
     import static org.junit.Assert.assertFalse;
     import static org.junit.Assert.assertNotNull;
    +import static org.junit.Assert.assertNull;
     import static org.junit.Assert.assertTrue;
     
     import java.io.Closeable;
    @@ -199,6 +200,26 @@ public void testRestNonAdminsMustNotCreateCache() {
           }
        }
     
    +   @Test
    +   public void testRestNonAdminsMustNotReadCacheConfig() {
    +      restCreateAuthzCache();
    +      String name = getServerTest().getMethodName();
    +      for (TestUser user : EnumSet.of(TestUser.ADMIN)) {
    +         RestClient client = getServerTest().rest().withClientConfiguration(restBuilders.get(user)).get();
    +         assertStatus(OK, client.cache(name).configuration());
    +         String details = assertStatus(OK, client.cache(name).details());
    +         Json json = Json.read(details);
    +         assertNotNull(json.asJsonMap().get("configuration"));
    +      }
    +      for (TestUser user : EnumSet.of(TestUser.APPLICATION, TestUser.OBSERVER, TestUser.MONITOR)) {
    +         RestClient client = getServerTest().rest().withClientConfiguration(restBuilders.get(user)).get();
    +         assertStatus(FORBIDDEN, client.cache(name).configuration());
    +         String details = assertStatus(OK, client.cache(name).details());
    +         Json json = Json.read(details);
    +         assertNull(json.asJsonMap().get("configuration"));
    +      }
    +   }
    +
        @Test
        public void testHotRodWriterCannotReadImplicit() {
           testHotRodWriterCannotRead();
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

8

News mentions

0

No linked articles in our index yet.