diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceUrlEncodingFilter.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceUrlEncodingFilter.java index 77b71215b4c..c2a4486a3fd 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceUrlEncodingFilter.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceUrlEncodingFilter.java @@ -96,10 +96,7 @@ public class ResourceUrlEncodingFilter extends GenericFilterBean { String lookupPath = pathHelper.getLookupPathForRequest(this); this.indexLookupPath = requestUri.lastIndexOf(lookupPath); if (this.indexLookupPath == -1) { - throw new IllegalStateException( - "Failed to find lookupPath '" + lookupPath + "' within requestUri '" + requestUri + "'. " + - "Does the path have invalid encoded characters for characterEncoding '" + - getRequest().getCharacterEncoding() + "'?"); + throw new LookupPathIndexException(lookupPath, requestUri); } this.prefixLookupPath = requestUri.substring(0, this.indexLookupPath); if ("/".equals(lookupPath) && !"/".equals(requestUri)) { @@ -164,4 +161,14 @@ public class ResourceUrlEncodingFilter extends GenericFilterBean { } } + + @SuppressWarnings("serial") + static class LookupPathIndexException extends IllegalArgumentException { + + LookupPathIndexException(String lookupPath, String requestUri) { + super("Failed to find lookupPath '" + lookupPath + "' within requestUri '" + requestUri + "'. " + + "This could be because the path has invalid encoded characters or isn't normalized."); + } + } + } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceUrlProviderExposingInterceptor.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceUrlProviderExposingInterceptor.java index 9572843c431..49ec8e25d8b 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceUrlProviderExposingInterceptor.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceUrlProviderExposingInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2019 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. @@ -20,6 +20,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.util.Assert; +import org.springframework.web.bind.ServletRequestBindingException; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; /** @@ -48,7 +49,12 @@ public class ResourceUrlProviderExposingInterceptor extends HandlerInterceptorAd public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { - request.setAttribute(RESOURCE_URL_PROVIDER_ATTR, this.resourceUrlProvider); + try { + request.setAttribute(RESOURCE_URL_PROVIDER_ATTR, this.resourceUrlProvider); + } + catch (ResourceUrlEncodingFilter.LookupPathIndexException ex) { + throw new ServletRequestBindingException(ex.getMessage(), ex); + } return true; } diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceUrlEncodingFilterTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceUrlEncodingFilterTests.java index 6fa6f7294ae..0bc6fcd92f6 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceUrlEncodingFilterTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceUrlEncodingFilterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 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. @@ -19,7 +19,9 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import javax.servlet.FilterChain; import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.junit.Before; @@ -28,6 +30,7 @@ import org.junit.Test; import org.springframework.core.io.ClassPathResource; import org.springframework.mock.web.test.MockHttpServletRequest; import org.springframework.mock.web.test.MockHttpServletResponse; +import org.springframework.web.bind.ServletRequestBindingException; import static org.junit.Assert.*; @@ -155,14 +158,36 @@ public class ResourceUrlEncodingFilterTests { "/resources/bar-11e16cf79faee7ac698c805cf28248d2.css?foo=bar&url=https://example.org#something"); } + @Test // gh-23508 + public void invalidLookupPath() throws Exception { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setRequestURI("/a/b/../logo.png"); + request.setServletPath("/a/logo.png"); + + this.filter.doFilter(request, new MockHttpServletResponse(), (req, res) -> { + try { + ResourceUrlProviderExposingInterceptor interceptor = + new ResourceUrlProviderExposingInterceptor(this.urlProvider); + + interceptor.preHandle((HttpServletRequest) req, (HttpServletResponse) res, new Object()); + fail(); + } + catch (Exception ex) { + assertEquals(ServletRequestBindingException.class, ex.getClass()); + } + }); + } + private void testEncodeUrl(MockHttpServletRequest request, String url, String expected) throws ServletException, IOException { - this.filter.doFilter(request, new MockHttpServletResponse(), (req, res) -> { + FilterChain chain = (req, res) -> { req.setAttribute(ResourceUrlProviderExposingInterceptor.RESOURCE_URL_PROVIDER_ATTR, this.urlProvider); String result = ((HttpServletResponse) res).encodeURL(url); assertEquals(expected, result); - }); + }; + + this.filter.doFilter(request, new MockHttpServletResponse(), chain); } }