Sanitize request URL in ResourceUrlEncodingFilter

Prior to this change, ResourceUrlEncodingFilter and ResourceUrlProvider
would try to resolve the resource path using the full request URL (i.e.
request path and request parameters), whereas the request path is the
only information to consider.
This would lead to StringIndexOutOfBoundsExceptions when the path +
request params information was given to the AntPathMatcher.

This commit makes the appropriate change to both
ResourceUrlEncodingFilter and ResourceUrlProvider, in order to only
select the request path.

Issue: SPR-13374
This commit is contained in:
Brian Clozel 2015-08-25 13:47:30 +02:00
parent 04cff89eb7
commit 0b9c3de320
4 changed files with 51 additions and 7 deletions

View File

@ -74,10 +74,12 @@ public class ResourceUrlEncodingFilter extends OncePerRequestFilter {
initIndexLookupPath(resourceUrlProvider);
if (url.length() >= this.indexLookupPath) {
String prefix = url.substring(0, this.indexLookupPath);
String lookupPath = url.substring(this.indexLookupPath);
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);
return super.encodeURL(prefix + lookupPath + suffix);
}
}
return super.encodeURL(url);
@ -95,6 +97,11 @@ public class ResourceUrlEncodingFilter extends OncePerRequestFilter {
this.indexLookupPath = requestUri.lastIndexOf(lookupPath);
}
}
private int getQueryParamsIndex(String url) {
int index = url.indexOf("?");
return index > 0 ? index : url.length();
}
}
}

View File

@ -172,11 +172,13 @@ public class ResourceUrlProvider implements ApplicationListener<ContextRefreshed
if (logger.isTraceEnabled()) {
logger.trace("Getting resource URL for requestURL=" + requestUrl);
}
int index = getLookupPathIndex(request);
String prefix = requestUrl.substring(0, index);
String lookupPath = requestUrl.substring(index);
int prefixIndex = getLookupPathIndex(request);
int suffixIndex = getQueryParamsIndex(requestUrl);
String prefix = requestUrl.substring(0, prefixIndex);
String suffix = requestUrl.substring(suffixIndex);
String lookupPath = requestUrl.substring(prefixIndex, suffixIndex);
String resolvedLookupPath = getForLookupPath(lookupPath);
return (resolvedLookupPath != null) ? prefix + resolvedLookupPath : null;
return (resolvedLookupPath != null) ? prefix + resolvedLookupPath + suffix : null;
}
private int getLookupPathIndex(HttpServletRequest request) {
@ -185,6 +187,11 @@ public class ResourceUrlProvider implements ApplicationListener<ContextRefreshed
return requestUri.indexOf(lookupPath);
}
private int getQueryParamsIndex(String lookupPath) {
int index = lookupPath.indexOf("?");
return index > 0 ? index : lookupPath.length();
}
/**
* Compare the given path against configured resource handler mappings and
* if a match is found use the {@code ResourceResolver} chain of the matched

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2015 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.
@ -106,6 +106,23 @@ public class ResourceUrlEncodingFilterTests {
});
}
// SPR-13374
@Test
public void encodeURLWithRequestParams() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo");
request.setContextPath("/");
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("/resources/bar.css?foo=bar&url=http://example.org");
assertEquals("/resources/bar-11e16cf79faee7ac698c805cf28248d2.css?foo=bar&url=http://example.org", result);
}
});
}
protected ResourceUrlProvider createResourceUrlProvider(List<ResourceResolver> resolvers) {
ResourceHttpRequestHandler handler = new ResourceHttpRequestHandler();
handler.setLocations(Arrays.asList(new ClassPathResource("test/", getClass())));

View File

@ -29,6 +29,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.mock.web.test.MockHttpServletRequest;
import org.springframework.mock.web.test.MockServletContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
@ -74,6 +75,18 @@ public class ResourceUrlProviderTests {
assertEquals("/resources/foo.css", url);
}
// SPR-13374
@Test
public void getStaticResourceUrlRequestWithRequestParams() {
initTranslator();
MockHttpServletRequest request = new MockHttpServletRequest();
request.setContextPath("/");
request.setRequestURI("/");
String url = this.translator.getForRequestUrl(request, "/resources/foo.css?foo=bar&url=http://example.org");
assertEquals("/resources/foo.css?foo=bar&url=http://example.org", url);
}
@Test
public void getFingerprintedResourceUrl() {
Map<String, VersionStrategy> versionStrategyMap = new HashMap<>();