CVE-2023-34035
Description
Spring Security versions 5.8 prior to 5.8.5, 6.0 prior to 6.0.5, and 6.1 prior to 6.1.2 could be susceptible to authorization rule misconfiguration if the application uses requestMatchers(String) and multiple servlets, one of them being Spring MVC’s DispatcherServlet. (DispatcherServlet is a Spring MVC component that maps HTTP endpoints to methods on @Controller-annotated classes.)
Specifically, an application is vulnerable when all of the following are true:
- Spring MVC is on the classpath
- Spring Security is securing more than one servlet in a single application (one of them being Spring MVC’s DispatcherServlet)
- The application uses requestMatchers(String) to refer to endpoints that are not Spring MVC endpoints
An application is not vulnerable if any of the following is true:
- The application does not have Spring MVC on the classpath
- The application secures no servlets other than Spring MVC’s DispatcherServlet
- The application uses requestMatchers(String) only for Spring MVC endpoints
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
org.springframework.security:spring-security-configMaven | >= 5.8.0, < 5.8.5 | 5.8.5 |
org.springframework.security:spring-security-configMaven | >= 6.0.0, < 6.0.5 | 6.0.5 |
org.springframework.security:spring-security-configMaven | >= 6.1.0, < 6.1.2 | 6.1.2 |
Affected products
1- Range: Spring Security 5.8.0 to 5.8.4, Spring Security 6.0.0 to 6.0.4, Spring Security 6.1.0 to 6.1.1
Patches
34e3bec904a54Specify MVC endpoint
1 file changed · +12 −3
servlet/java-configuration/authentication/preauth/src/main/java/example/SecurityConfiguration.java+12 −3 modified@@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,31 +17,40 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Scope; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.provisioning.InMemoryUserDetailsManager; import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher; +import org.springframework.web.servlet.handler.HandlerMappingIntrospector; @Configuration @EnableWebSecurity public class SecurityConfiguration { @Bean - public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + public SecurityFilterChain securityFilterChain(HttpSecurity http, MvcRequestMatcher.Builder mvc) throws Exception { // @formatter:off http .authorizeHttpRequests((authorize) -> authorize - .requestMatchers("/login", "/resources/**").permitAll() + .requestMatchers(mvc.pattern("/login"), mvc.pattern("/resources/**")).permitAll() .anyRequest().authenticated() ) .jee((jee) -> jee.mappableRoles("USER", "ADMIN")); // @formatter:on return http.build(); } + @Scope("prototype") + @Bean + MvcRequestMatcher.Builder mvc(HandlerMappingIntrospector introspector) { + return new MvcRequestMatcher.Builder(introspector); + } + // @formatter:off @Bean public UserDetailsService userDetailsService() {
bb46a5427005Add DispatcherServlet to Tests
6 files changed · +150 −68
config/src/test/java/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistryTests.java+2 −29 modified@@ -18,22 +18,18 @@ import java.lang.reflect.Field; import java.lang.reflect.Modifier; -import java.util.LinkedHashMap; import java.util.List; -import java.util.Map; import javax.servlet.DispatcherType; import javax.servlet.Servlet; -import javax.servlet.ServletContext; -import javax.servlet.ServletRegistration; -import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.context.ApplicationContext; import org.springframework.http.HttpMethod; +import org.springframework.security.config.MockServletContext; import org.springframework.security.config.annotation.ObjectPostProcessor; import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; @@ -70,10 +66,8 @@ public <O> O postProcess(O object) { public void setUp() { this.matcherRegistry = new TestRequestMatcherRegistry(); this.context = mock(WebApplicationContext.class); - ServletContext servletContext = new MockServletContext(); - servletContext.addServlet("dispatcherServlet", DispatcherServlet.class); given(this.context.getBean(ObjectPostProcessor.class)).willReturn(NO_OP_OBJECT_POST_PROCESSOR); - given(this.context.getServletContext()).willReturn(servletContext); + given(this.context.getServletContext()).willReturn(MockServletContext.mvc()); this.matcherRegistry.setApplicationContext(this.context); } @@ -256,25 +250,4 @@ protected List<RequestMatcher> chainRequestMatchers(List<RequestMatcher> request } - private static class MockServletContext extends org.springframework.mock.web.MockServletContext { - - private final Map<String, ServletRegistration> registrations = new LinkedHashMap<>(); - - @NotNull - @Override - public ServletRegistration.Dynamic addServlet(@NotNull String servletName, Class<? extends Servlet> clazz) { - ServletRegistration.Dynamic dynamic = mock(ServletRegistration.Dynamic.class); - given(dynamic.getClassName()).willReturn(clazz.getName()); - this.registrations.put(servletName, dynamic); - return dynamic; - } - - @NotNull - @Override - public Map<String, ? extends ServletRegistration> getServletRegistrations() { - return this.registrations; - } - - } - }
config/src/test/java/org/springframework/security/config/annotation/web/configurers/AuthorizeRequestsTests.java+2 −2 modified@@ -29,10 +29,10 @@ import org.springframework.mock.web.MockFilterChain; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; -import org.springframework.mock.web.MockServletContext; import org.springframework.security.access.hierarchicalroles.RoleHierarchy; import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.config.MockServletContext; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; @@ -75,7 +75,7 @@ public class AuthorizeRequestsTests { @BeforeEach public void setup() { - this.servletContext = spy(new MockServletContext()); + this.servletContext = spy(MockServletContext.mvc()); this.request = new MockHttpServletRequest("GET", ""); this.request.setMethod("GET"); this.response = new MockHttpServletResponse();
config/src/test/java/org/springframework/security/config/annotation/web/configurers/HttpSecuritySecurityMatchersTests.java+2 −32 modified@@ -18,14 +18,9 @@ import java.lang.reflect.Field; import java.lang.reflect.Modifier; -import java.util.LinkedHashMap; -import java.util.Map; -import javax.servlet.Servlet; -import javax.servlet.ServletRegistration; import javax.servlet.http.HttpServletResponse; -import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -39,6 +34,7 @@ import org.springframework.mock.web.MockFilterChain; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.security.config.MockServletContext; import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; @@ -52,15 +48,12 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; -import org.springframework.web.servlet.DispatcherServlet; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.PathMatchConfigurer; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.handler.HandlerMappingIntrospector; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; import static org.springframework.security.config.Customizer.withDefaults; /** @@ -240,9 +233,7 @@ public void securityMatchersWhenMultiMvcMatcherThenAllPathsAreDenied() throws Ex public void loadConfig(Class<?>... configs) { this.context = new AnnotationConfigWebApplicationContext(); this.context.register(configs); - MockServletContext servletContext = new MockServletContext(); - servletContext.addServlet("dispatcherServlet", DispatcherServlet.class); - this.context.setServletContext(servletContext); + this.context.setServletContext(MockServletContext.mvc()); this.context.refresh(); this.context.getAutowireCapableBeanFactory().autowireBean(this); } @@ -573,25 +564,4 @@ public void configurePathMatch(PathMatchConfigurer configurer) { } - private static class MockServletContext extends org.springframework.mock.web.MockServletContext { - - private final Map<String, ServletRegistration> registrations = new LinkedHashMap<>(); - - @NotNull - @Override - public ServletRegistration.Dynamic addServlet(@NotNull String servletName, Class<? extends Servlet> clazz) { - ServletRegistration.Dynamic dynamic = mock(ServletRegistration.Dynamic.class); - given(dynamic.getClassName()).willReturn(clazz.getName()); - this.registrations.put(servletName, dynamic); - return dynamic; - } - - @NotNull - @Override - public Map<String, ? extends ServletRegistration> getServletRegistrations() { - return this.registrations; - } - - } - }
config/src/test/java/org/springframework/security/config/annotation/web/configurers/UrlAuthorizationConfigurerTests.java+2 −2 modified@@ -31,8 +31,8 @@ import org.springframework.mock.web.MockFilterChain; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; -import org.springframework.mock.web.MockServletContext; import org.springframework.security.config.Customizer; +import org.springframework.security.config.MockServletContext; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; @@ -167,7 +167,7 @@ public void multiMvcMatchersConfig() throws Exception { public void loadConfig(Class<?>... configs) { this.context = new AnnotationConfigWebApplicationContext(); this.context.register(configs); - this.context.setServletContext(new MockServletContext()); + this.context.setServletContext(MockServletContext.mvc()); this.context.refresh(); this.context.getAutowireCapableBeanFactory().autowireBean(this); }
config/src/test/java/org/springframework/security/config/MockServletContext.java+139 −0 added@@ -0,0 +1,139 @@ +/* + * Copyright 2002-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.security.config; + +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; + +import javax.servlet.MultipartConfigElement; +import javax.servlet.Servlet; +import javax.servlet.ServletRegistration; +import javax.servlet.ServletSecurityElement; + +import org.springframework.lang.NonNull; +import org.springframework.web.servlet.DispatcherServlet; + +public class MockServletContext extends org.springframework.mock.web.MockServletContext { + + private final Map<String, ServletRegistration> registrations = new LinkedHashMap<>(); + + public static MockServletContext mvc() { + MockServletContext servletContext = new MockServletContext(); + servletContext.addServlet("dispatcherServlet", DispatcherServlet.class); + return servletContext; + } + + @NonNull + @Override + public ServletRegistration.Dynamic addServlet(@NonNull String servletName, Class<? extends Servlet> clazz) { + ServletRegistration.Dynamic dynamic = new MockServletRegistration(servletName, clazz); + this.registrations.put(servletName, dynamic); + return dynamic; + } + + @NonNull + @Override + public Map<String, ? extends ServletRegistration> getServletRegistrations() { + return this.registrations; + } + + private static class MockServletRegistration implements ServletRegistration.Dynamic { + + private final String name; + + private final Class<?> clazz; + + MockServletRegistration(String name, Class<?> clazz) { + this.name = name; + this.clazz = clazz; + } + + @Override + public void setLoadOnStartup(int loadOnStartup) { + + } + + @Override + public Set<String> setServletSecurity(ServletSecurityElement constraint) { + return null; + } + + @Override + public void setMultipartConfig(MultipartConfigElement multipartConfig) { + + } + + @Override + public void setRunAsRole(String roleName) { + + } + + @Override + public void setAsyncSupported(boolean isAsyncSupported) { + + } + + @Override + public Set<String> addMapping(String... urlPatterns) { + return null; + } + + @Override + public Collection<String> getMappings() { + return null; + } + + @Override + public String getRunAsRole() { + return null; + } + + @Override + public String getName() { + return this.name; + } + + @Override + public String getClassName() { + return this.clazz.getName(); + } + + @Override + public boolean setInitParameter(String name, String value) { + return false; + } + + @Override + public String getInitParameter(String name) { + return null; + } + + @Override + public Set<String> setInitParameters(Map<String, String> initParameters) { + return null; + } + + @Override + public Map<String, String> getInitParameters() { + return null; + } + + } + +}
config/src/test/java/org/springframework/security/config/test/SpringTestContext.java+3 −3 modified@@ -28,8 +28,8 @@ import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor; import org.springframework.mock.web.MockServletConfig; -import org.springframework.mock.web.MockServletContext; import org.springframework.security.config.BeanIds; +import org.springframework.security.config.MockServletContext; import org.springframework.security.config.util.InMemoryXmlWebApplicationContext; import org.springframework.test.context.web.GenericXmlWebContextLoader; import org.springframework.test.web.servlet.MockMvc; @@ -129,15 +129,15 @@ private SpringTestContext addFilter(Filter filter) { public ConfigurableWebApplicationContext getContext() { if (!this.context.isRunning()) { - this.context.setServletContext(new MockServletContext()); + this.context.setServletContext(MockServletContext.mvc()); this.context.setServletConfig(new MockServletConfig()); this.context.refresh(); } return this.context; } public void autowire() { - this.context.setServletContext(new MockServletContext()); + this.context.setServletContext(MockServletContext.mvc()); this.context.setServletConfig(new MockServletConfig()); for (Consumer<ConfigurableWebApplicationContext> postProcessor : this.postProcessors) { postProcessor.accept(this.context);
df239b6448ccImprove RequestMatcher Validation
3 files changed · +148 −17
config/src/main/java/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistry.java+53 −12 modified@@ -19,8 +19,11 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Map; import javax.servlet.DispatcherType; +import javax.servlet.ServletContext; +import javax.servlet.ServletRegistration; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.context.ApplicationContext; @@ -36,6 +39,7 @@ import org.springframework.security.web.util.matcher.RequestMatcher; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; +import org.springframework.web.context.WebApplicationContext; import org.springframework.web.servlet.handler.HandlerMappingIntrospector; /** @@ -297,14 +301,47 @@ public C requestMatchers(RequestMatcher... requestMatchers) { * @since 5.8 */ public C requestMatchers(HttpMethod method, String... patterns) { - List<RequestMatcher> matchers = new ArrayList<>(); - if (mvcPresent) { - matchers.addAll(createMvcMatchers(method, patterns)); + if (!mvcPresent) { + return requestMatchers(RequestMatchers.antMatchersAsArray(method, patterns)); + } + if (!(this.context instanceof WebApplicationContext)) { + return requestMatchers(RequestMatchers.antMatchersAsArray(method, patterns)); + } + WebApplicationContext context = (WebApplicationContext) this.context; + ServletContext servletContext = context.getServletContext(); + if (servletContext == null) { + return requestMatchers(RequestMatchers.antMatchersAsArray(method, patterns)); + } + Map<String, ? extends ServletRegistration> registrations = servletContext.getServletRegistrations(); + if (registrations == null) { + return requestMatchers(RequestMatchers.antMatchersAsArray(method, patterns)); + } + if (!hasDispatcherServlet(registrations)) { + return requestMatchers(RequestMatchers.antMatchersAsArray(method, patterns)); } - else { - matchers.addAll(RequestMatchers.antMatchers(method, patterns)); + Assert.isTrue(registrations.size() == 1, + "This method cannot decide whether these patterns are Spring MVC patterns or not. If this endpoint is a Spring MVC endpoint, please use requestMatchers(MvcRequestMatcher); otherwise, please use requestMatchers(AntPathRequestMatcher)."); + return requestMatchers(createMvcMatchers(method, patterns).toArray(new RequestMatcher[0])); + } + + private boolean hasDispatcherServlet(Map<String, ? extends ServletRegistration> registrations) { + if (registrations == null) { + return false; + } + Class<?> dispatcherServlet = ClassUtils.resolveClassName("org.springframework.web.servlet.DispatcherServlet", + null); + for (ServletRegistration registration : registrations.values()) { + try { + Class<?> clazz = Class.forName(registration.getClassName()); + if (dispatcherServlet.isAssignableFrom(clazz)) { + return true; + } + } + catch (ClassNotFoundException ex) { + return false; + } } - return requestMatchers(matchers.toArray(new RequestMatcher[0])); + return false; } /** @@ -380,12 +417,7 @@ private RequestMatchers() { * @return a {@link List} of {@link AntPathRequestMatcher} instances */ static List<RequestMatcher> antMatchers(HttpMethod httpMethod, String... antPatterns) { - String method = (httpMethod != null) ? httpMethod.toString() : null; - List<RequestMatcher> matchers = new ArrayList<>(); - for (String pattern : antPatterns) { - matchers.add(new AntPathRequestMatcher(pattern, method)); - } - return matchers; + return Arrays.asList(antMatchersAsArray(httpMethod, antPatterns)); } /** @@ -399,6 +431,15 @@ static List<RequestMatcher> antMatchers(String... antPatterns) { return antMatchers(null, antPatterns); } + static RequestMatcher[] antMatchersAsArray(HttpMethod httpMethod, String... antPatterns) { + String method = (httpMethod != null) ? httpMethod.toString() : null; + RequestMatcher[] matchers = new RequestMatcher[antPatterns.length]; + for (int index = 0; index < antPatterns.length; index++) { + matchers[index] = new AntPathRequestMatcher(antPatterns[index], method); + } + return matchers; + } + /** * Create a {@link List} of {@link RegexRequestMatcher} instances. * @param httpMethod the {@link HttpMethod} to use or {@code null} for any
config/src/test/java/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistryTests.java+63 −3 modified@@ -18,10 +18,16 @@ import java.lang.reflect.Field; import java.lang.reflect.Modifier; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import javax.servlet.DispatcherType; +import javax.servlet.Servlet; +import javax.servlet.ServletContext; +import javax.servlet.ServletRegistration; +import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -34,6 +40,8 @@ import org.springframework.security.web.util.matcher.DispatcherTypeRequestMatcher; import org.springframework.security.web.util.matcher.RegexRequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.servlet.DispatcherServlet; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; @@ -56,12 +64,17 @@ public <O> O postProcess(O object) { private TestRequestMatcherRegistry matcherRegistry; + private WebApplicationContext context; + @BeforeEach public void setUp() { this.matcherRegistry = new TestRequestMatcherRegistry(); - ApplicationContext context = mock(ApplicationContext.class); - given(context.getBean(ObjectPostProcessor.class)).willReturn(NO_OP_OBJECT_POST_PROCESSOR); - this.matcherRegistry.setApplicationContext(context); + this.context = mock(WebApplicationContext.class); + ServletContext servletContext = new MockServletContext(); + servletContext.addServlet("dispatcherServlet", DispatcherServlet.class); + given(this.context.getBean(ObjectPostProcessor.class)).willReturn(NO_OP_OBJECT_POST_PROCESSOR); + given(this.context.getServletContext()).willReturn(servletContext); + this.matcherRegistry.setApplicationContext(this.context); } @Test @@ -184,6 +197,32 @@ public void requestMatchersWhenMvcPresentInClassPathAndMvcIntrospectorBeanNotAva "Please ensure Spring Security & Spring MVC are configured in a shared ApplicationContext"); } + @Test + public void requestMatchersWhenNoDispatcherServletThenAntPathRequestMatcherType() { + MockServletContext servletContext = new MockServletContext(); + given(this.context.getServletContext()).willReturn(servletContext); + List<RequestMatcher> requestMatchers = this.matcherRegistry.requestMatchers("/**"); + assertThat(requestMatchers).isNotEmpty(); + assertThat(requestMatchers).hasSize(1); + assertThat(requestMatchers.get(0)).isExactlyInstanceOf(AntPathRequestMatcher.class); + servletContext.addServlet("servletOne", Servlet.class); + servletContext.addServlet("servletTwo", Servlet.class); + requestMatchers = this.matcherRegistry.requestMatchers("/**"); + assertThat(requestMatchers).isNotEmpty(); + assertThat(requestMatchers).hasSize(1); + assertThat(requestMatchers.get(0)).isExactlyInstanceOf(AntPathRequestMatcher.class); + } + + @Test + public void requestMatchersWhenAmbiguousServletsThenException() { + MockServletContext servletContext = new MockServletContext(); + given(this.context.getServletContext()).willReturn(servletContext); + servletContext.addServlet("dispatcherServlet", DispatcherServlet.class); + servletContext.addServlet("servletTwo", Servlet.class); + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> this.matcherRegistry.requestMatchers("/**")); + } + private void mockMvcIntrospector(boolean isPresent) { ApplicationContext context = this.matcherRegistry.getApplicationContext(); given(context.containsBean("mvcHandlerMappingIntrospector")).willReturn(isPresent); @@ -217,4 +256,25 @@ protected List<RequestMatcher> chainRequestMatchers(List<RequestMatcher> request } + private static class MockServletContext extends org.springframework.mock.web.MockServletContext { + + private final Map<String, ServletRegistration> registrations = new LinkedHashMap<>(); + + @NotNull + @Override + public ServletRegistration.Dynamic addServlet(@NotNull String servletName, Class<? extends Servlet> clazz) { + ServletRegistration.Dynamic dynamic = mock(ServletRegistration.Dynamic.class); + given(dynamic.getClassName()).willReturn(clazz.getName()); + this.registrations.put(servletName, dynamic); + return dynamic; + } + + @NotNull + @Override + public Map<String, ? extends ServletRegistration> getServletRegistrations() { + return this.registrations; + } + + } + }
config/src/test/java/org/springframework/security/config/annotation/web/configurers/HttpSecuritySecurityMatchersTests.java+32 −2 modified@@ -18,9 +18,14 @@ import java.lang.reflect.Field; import java.lang.reflect.Modifier; +import java.util.LinkedHashMap; +import java.util.Map; +import javax.servlet.Servlet; +import javax.servlet.ServletRegistration; import javax.servlet.http.HttpServletResponse; +import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -34,7 +39,6 @@ import org.springframework.mock.web.MockFilterChain; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; -import org.springframework.mock.web.MockServletContext; import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; @@ -48,12 +52,15 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; +import org.springframework.web.servlet.DispatcherServlet; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.PathMatchConfigurer; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.handler.HandlerMappingIntrospector; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; import static org.springframework.security.config.Customizer.withDefaults; /** @@ -233,7 +240,9 @@ public void securityMatchersWhenMultiMvcMatcherThenAllPathsAreDenied() throws Ex public void loadConfig(Class<?>... configs) { this.context = new AnnotationConfigWebApplicationContext(); this.context.register(configs); - this.context.setServletContext(new MockServletContext()); + MockServletContext servletContext = new MockServletContext(); + servletContext.addServlet("dispatcherServlet", DispatcherServlet.class); + this.context.setServletContext(servletContext); this.context.refresh(); this.context.getAutowireCapableBeanFactory().autowireBean(this); } @@ -564,4 +573,25 @@ public void configurePathMatch(PathMatchConfigurer configurer) { } + private static class MockServletContext extends org.springframework.mock.web.MockServletContext { + + private final Map<String, ServletRegistration> registrations = new LinkedHashMap<>(); + + @NotNull + @Override + public ServletRegistration.Dynamic addServlet(@NotNull String servletName, Class<? extends Servlet> clazz) { + ServletRegistration.Dynamic dynamic = mock(ServletRegistration.Dynamic.class); + given(dynamic.getClassName()).willReturn(clazz.getName()); + this.registrations.put(servletName, dynamic); + return dynamic; + } + + @NotNull + @Override + public Map<String, ? extends ServletRegistration> getServletRegistrations() { + return this.registrations; + } + + } + }
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
7- github.com/advisories/GHSA-4vpr-xfrp-cj64ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2023-34035ghsaADVISORY
- github.com/spring-projects/spring-security-samples/commit/4e3bec904a5467db28ea33e25ac9d90524b53d66ghsaWEB
- github.com/spring-projects/spring-security-samples/tree/main/servlet/java-configuration/authentication/preauthghsaWEB
- github.com/spring-projects/spring-security/commit/bb46a5427005e33e637b15948de8adae244ce547ghsaWEB
- github.com/spring-projects/spring-security/commit/df239b6448ccf138b0c95b5575a88f33ac35cd9aghsaWEB
- spring.io/security/cve-2023-34035ghsaWEB
News mentions
0No linked articles in our index yet.