Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add integration test to ensure ErrorPageSecurityFilter works with custom endpoint matchers #29564

Closed
jeffbswope opened this issue Jan 25, 2022 · 5 comments
Labels
status: superseded An issue that has been superseded by another type: task A general task

Comments

@jeffbswope
Copy link

Starting with 3.0.0-M1, a second WebSecurityConfigurer appears to be triggering "ServletContext must not be null" underneath the ErrorPageSecurityFilter when a request fails authentication.

We have a library which configures security for the actuator, then applications will configure their own security. This works fine so far up through 2.6.3, although I'm happy to hear if we are doing something unexpected.

Testing with 3.0.0-M1 all tests in this configuration which hit 401/403 errors are throwing this error from the actuator-specific matcher in RequestMatcherDelegatingWebInvocationPrivilegeEvaluator#delegates.

I know there's a lot going on with ErrorPageSecurityFilter recently but I did not see this problem in particular called out anywhere.

I was able to isolate this into minimal repro here, where the error can be found/eliminated based on enabling/disabling the second WebSecurityConfigurer:
https://github.com/jeffbswope/null-servletcontext-errorpagefilter

java.lang.IllegalArgumentException: ServletContext must not be null
	at org.springframework.util.Assert.notNull(Assert.java:201) ~[spring-core-6.0.0-M2.jar:6.0.0-M2]
	at org.springframework.web.context.support.WebApplicationContextUtils.getWebApplicationContext(WebApplicationContextUtils.java:112) ~[spring-web-6.0.0-M2.jar:6.0.0-M2]
	at org.springframework.web.context.support.WebApplicationContextUtils.getWebApplicationContext(WebApplicationContextUtils.java:101) ~[spring-web-6.0.0-M2.jar:6.0.0-M2]
	at org.springframework.web.context.support.WebApplicationContextUtils.getRequiredWebApplicationContext(WebApplicationContextUtils.java:83) ~[spring-web-6.0.0-M2.jar:6.0.0-M2]
	at org.springframework.boot.security.servlet.ApplicationContextRequestMatcher.matches(ApplicationContextRequestMatcher.java:58) ~[spring-boot-3.0.0-M1.jar:3.0.0-M1]
	at org.springframework.security.web.DefaultSecurityFilterChain.matches(DefaultSecurityFilterChain.java:67) ~[spring-security-web-6.0.0-M1.jar:6.0.0-M1]
	at org.springframework.security.web.access.RequestMatcherDelegatingWebInvocationPrivilegeEvaluator.getDelegate(RequestMatcherDelegatingWebInvocationPrivilegeEvaluator.java:115) ~[spring-security-web-6.0.0-M1.jar:6.0.0-M1]
	at org.springframework.security.web.access.RequestMatcherDelegatingWebInvocationPrivilegeEvaluator.isAllowed(RequestMatcherDelegatingWebInvocationPrivilegeEvaluator.java:66) ~[spring-security-web-6.0.0-M1.jar:6.0.0-M1]
	at org.springframework.boot.web.servlet.filter.ErrorPageSecurityFilter.isAllowed(ErrorPageSecurityFilter.java:83) ~[spring-boot-3.0.0-M1.jar:3.0.0-M1]
	at org.springframework.boot.web.servlet.filter.ErrorPageSecurityFilter.doFilter(ErrorPageSecurityFilter.java:71) ~[spring-boot-3.0.0-M1.jar:3.0.0-M1]
	at org.springframework.boot.web.servlet.filter.ErrorPageSecurityFilter.doFilter(ErrorPageSecurityFilter.java:65) ~[spring-boot-3.0.0-M1.jar:3.0.0-M1]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185) ~[tomcat-embed-core-10.0.16.jar:10.0.16]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158) ~[tomcat-embed-core-10.0.16.jar:10.0.16]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:327) ~[spring-security-web-6.0.0-M1.jar:6.0.0-M1]
	at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:106) ~[spring-security-web-6.0.0-M1.jar:6.0.0-M1]
	at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:81) ~[spring-security-web-6.0.0-M1.jar:6.0.0-M1]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-6.0.0-M1.jar:6.0.0-M1]
	at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:122) ~[spring-security-web-6.0.0-M1.jar:6.0.0-M1]
	at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:116) ~[spring-security-web-6.0.0-M1.jar:6.0.0-M1]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-6.0.0-M1.jar:6.0.0-M1]
	at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:87) ~[spring-security-web-6.0.0-M1.jar:6.0.0-M1]
	at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:81) ~[spring-security-web-6.0.0-M1.jar:6.0.0-M1]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-6.0.0-M1.jar:6.0.0-M1]
	at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:109) ~[spring-security-web-6.0.0-M1.jar:6.0.0-M1]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-6.0.0-M1.jar:6.0.0-M1]
	at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:149) ~[spring-security-web-6.0.0-M1.jar:6.0.0-M1]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-6.0.0-M1.jar:6.0.0-M1]
	at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) ~[spring-security-web-6.0.0-M1.jar:6.0.0-M1]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-6.0.0-M1.jar:6.0.0-M1]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101) ~[spring-web-6.0.0-M2.jar:6.0.0-M2]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-6.0.0-M1.jar:6.0.0-M1]
	at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:103) ~[spring-security-web-6.0.0-M1.jar:6.0.0-M1]
	at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:89) ~[spring-security-web-6.0.0-M1.jar:6.0.0-M1]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-6.0.0-M1.jar:6.0.0-M1]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101) ~[spring-web-6.0.0-M2.jar:6.0.0-M2]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-6.0.0-M1.jar:6.0.0-M1]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101) ~[spring-web-6.0.0-M2.jar:6.0.0-M2]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-6.0.0-M1.jar:6.0.0-M1]
	at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:110) ~[spring-security-web-6.0.0-M1.jar:6.0.0-M1]
	at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:80) ~[spring-security-web-6.0.0-M1.jar:6.0.0-M1]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-6.0.0-M1.jar:6.0.0-M1]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101) ~[spring-web-6.0.0-M2.jar:6.0.0-M2]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-6.0.0-M1.jar:6.0.0-M1]
	at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:211) ~[spring-security-web-6.0.0-M1.jar:6.0.0-M1]
	at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:183) ~[spring-security-web-6.0.0-M1.jar:6.0.0-M1]
	at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:351) ~[spring-web-6.0.0-M2.jar:6.0.0-M2]
	at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:267) ~[spring-web-6.0.0-M2.jar:6.0.0-M2]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185) ~[tomcat-embed-core-10.0.16.jar:10.0.16]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158) ~[tomcat-embed-core-10.0.16.jar:10.0.16]
	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-6.0.0-M2.jar:6.0.0-M2]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.0.0-M2.jar:6.0.0-M2]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185) ~[tomcat-embed-core-10.0.16.jar:10.0.16]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158) ~[tomcat-embed-core-10.0.16.jar:10.0.16]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101) ~[spring-web-6.0.0-M2.jar:6.0.0-M2]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185) ~[tomcat-embed-core-10.0.16.jar:10.0.16]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158) ~[tomcat-embed-core-10.0.16.jar:10.0.16]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101) ~[spring-web-6.0.0-M2.jar:6.0.0-M2]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185) ~[tomcat-embed-core-10.0.16.jar:10.0.16]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158) ~[tomcat-embed-core-10.0.16.jar:10.0.16]
	at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:691) ~[tomcat-embed-core-10.0.16.jar:10.0.16]
	at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:443) ~[tomcat-embed-core-10.0.16.jar:10.0.16]
	at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:367) ~[tomcat-embed-core-10.0.16.jar:10.0.16]
	at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:295) ~[tomcat-embed-core-10.0.16.jar:10.0.16]
	at org.apache.catalina.core.StandardHostValve.custom(StandardHostValve.java:387) ~[tomcat-embed-core-10.0.16.jar:10.0.16]
	at org.apache.catalina.core.StandardHostValve.status(StandardHostValve.java:233) ~[tomcat-embed-core-10.0.16.jar:10.0.16]
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:155) ~[tomcat-embed-core-10.0.16.jar:10.0.16]
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) ~[tomcat-embed-core-10.0.16.jar:10.0.16]
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78) ~[tomcat-embed-core-10.0.16.jar:10.0.16]
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:355) ~[tomcat-embed-core-10.0.16.jar:10.0.16]
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:399) ~[tomcat-embed-core-10.0.16.jar:10.0.16]
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) ~[tomcat-embed-core-10.0.16.jar:10.0.16]
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:866) ~[tomcat-embed-core-10.0.16.jar:10.0.16]
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1708) ~[tomcat-embed-core-10.0.16.jar:10.0.16]
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-10.0.16.jar:10.0.16]
	at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) ~[tomcat-embed-core-10.0.16.jar:10.0.16]
	at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) ~[tomcat-embed-core-10.0.16.jar:10.0.16]
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-10.0.16.jar:10.0.16]
	at java.base/java.lang.Thread.run(Thread.java:833) ~[na:na]
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Jan 25, 2022
@jeffbswope
Copy link
Author

One thing I hadn't bothered to notice before is that in that reproduction when running the application you do get the correct 401/403, but it comes with HTML (apparently from Tomcat) and the java.lang.IllegalArgumentException: ServletContext must not be null exception is spit out in the log anyway.

@philwebb
Copy link
Member

It looks like this is an issue that's been triggered by the fix for spring-projects/spring-security#10554. If I update the sample with snapshot repos, downgrade to Spring Boot 2.6.3 and add ext['spring-security.version'] = '5.6.2-SNAPSHOT' then I get the same exception.

It looks like RequestMatcherDelegatingWebInvocationPrivilegeEvaluator is creating a FilterInvocation that uses a DummyRequest which ultimately means our ApplicationContextRequestMatcher fails.

This feels similar to spring-projects/spring-security#10208. I'll open a Spring Security issue.

@philwebb
Copy link
Member

I've opened spring-projects/spring-security#10779 which you may want to subscribe to. Thanks for raising the issue.

@philwebb philwebb added for: external-project For an external project and not something we can fix and removed status: waiting-for-triage An issue we've not yet triaged labels Jan 25, 2022
@philwebb philwebb reopened this Jan 26, 2022
@philwebb philwebb changed the title ServletContext null under ErrorPageSecurityFilter in 3.0.0-M1 Add integration test to ensure ErrorPageSecurityFilter works with custom endpoint matchers Jan 26, 2022
@philwebb philwebb added status: blocked An issue that's blocked on an external project change type: task A general task and removed for: external-project For an external project and not something we can fix labels Jan 26, 2022
@philwebb philwebb added this to the 2.6.x milestone Jan 26, 2022
@philwebb
Copy link
Member

Reopening because it struck me that we didn't have any test in 3.0.x fail so it would be good to add one.

@wilkinsona
Copy link
Member

wilkinsona commented Apr 22, 2024

We since removed ErrorPageSecurityFilter so I don't this is needed any more.

@wilkinsona wilkinsona closed this as not planned Won't fix, can't repro, duplicate, stale Apr 22, 2024
@wilkinsona wilkinsona removed this from the 3.1.x milestone Apr 22, 2024
@wilkinsona wilkinsona added status: superseded An issue that has been superseded by another and removed status: blocked An issue that's blocked on an external project change labels Apr 22, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: superseded An issue that has been superseded by another type: task A general task
Projects
None yet
Development

No branches or pull requests

4 participants