From 2f6d86b7aadd0f8cddb567e2ac13e9c1b1019107 Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Thu, 21 Jan 2016 17:28:29 +0100 Subject: [PATCH] Fix OutOfBoundsExceptio in ResourceUrlEncodingFilter Prior to this change, the `ResourceUrlEncodingFilter` would try to lookup resources URLs as soon as the given URL would be longer than the expected context+servlet prefix path. This can lead to OutOfBoundsExceptions when the provided URL does not start with that prefix and still has the required length. This commit makes sure that all candidate URLs for resources lookup are prefixed with the cached servlet and context path. This underlines the fact that the `ResourceUrlEncodingFilter` does not support relative URLs for now and delegates to the native servlet implementation in that case. Issue: SPR-13861 --- .../resource/ResourceUrlEncodingFilter.java | 18 +++++++++------- .../ResourceUrlEncodingFilterTests.java | 21 ++++++++++++++++++- 2 files changed, 31 insertions(+), 8 deletions(-) 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 7299024880..471ae15461 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -37,6 +37,7 @@ import org.springframework.web.filter.OncePerRequestFilter; * @author Jeremy Grelle * @author Rossen Stoyanchev * @author Sam Brannen + * @author Brian Clozel * @since 4.1 */ public class ResourceUrlEncodingFilter extends OncePerRequestFilter { @@ -56,9 +57,11 @@ public class ResourceUrlEncodingFilter extends OncePerRequestFilter { private final HttpServletRequest request; - /* Cache the index of the path within the DispatcherServlet mapping */ + /* Cache the index and prefix of the path within the DispatcherServlet mapping */ private Integer indexLookupPath; + private String prefixLookupPath; + public ResourceUrlEncodingResponseWrapper(HttpServletRequest request, HttpServletResponse wrapped) { super(wrapped); this.request = request; @@ -72,15 +75,14 @@ public class ResourceUrlEncodingFilter extends OncePerRequestFilter { return super.encodeURL(url); } - initIndexLookupPath(resourceUrlProvider); - if (url.length() >= this.indexLookupPath) { - String prefix = url.substring(0, this.indexLookupPath); + initLookupPath(resourceUrlProvider); + if (url.startsWith(this.prefixLookupPath)) { int suffixIndex = getQueryParamsIndex(url); String suffix = url.substring(suffixIndex); String lookupPath = url.substring(this.indexLookupPath, suffixIndex); lookupPath = resourceUrlProvider.getForLookupPath(lookupPath); if (lookupPath != null) { - return super.encodeURL(prefix + lookupPath + suffix); + return super.encodeURL(this.prefixLookupPath + lookupPath + suffix); } } @@ -92,16 +94,18 @@ public class ResourceUrlEncodingFilter extends OncePerRequestFilter { ResourceUrlProviderExposingInterceptor.RESOURCE_URL_PROVIDER_ATTR); } - private void initIndexLookupPath(ResourceUrlProvider urlProvider) { + private void initLookupPath(ResourceUrlProvider urlProvider) { if (this.indexLookupPath == null) { String requestUri = urlProvider.getPathHelper().getRequestUri(this.request); String lookupPath = urlProvider.getPathHelper().getLookupPathForRequest(this.request); this.indexLookupPath = requestUri.lastIndexOf(lookupPath); + this.prefixLookupPath = requestUri.substring(0, this.indexLookupPath); if ("/".equals(lookupPath) && !"/".equals(requestUri)) { String contextPath = urlProvider.getPathHelper().getContextPath(this.request); if (requestUri.equals(contextPath)) { this.indexLookupPath = requestUri.length(); + this.prefixLookupPath = requestUri; } } } 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 964919484a..26dd6ae4ea 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-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -150,6 +150,25 @@ public class ResourceUrlEncodingFilterTests { }); } + // SPR-13847 + @Test + public void encodeUrlPreventStringOutOfBounds() throws Exception { + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/context-path/index"); + request.setContextPath("/context-path"); + request.setServletPath(""); + request.setAttribute(ResourceUrlProviderExposingInterceptor.RESOURCE_URL_PROVIDER_ATTR, this.resourceUrlProvider); + MockHttpServletResponse response = new MockHttpServletResponse(); + + this.filter.doFilterInternal(request, response, new FilterChain() { + @Override + public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { + String result = ((HttpServletResponse)response).encodeURL("index?key=value"); + assertEquals("index?key=value", result); + } + }); + } + + protected ResourceUrlProvider createResourceUrlProvider(List resolvers) { ResourceHttpRequestHandler handler = new ResourceHttpRequestHandler(); handler.setLocations(Arrays.asList(new ClassPathResource("test/", getClass())));