From 72baa9bf2741b599ed1d848521e167e644ab7fb5 Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Thu, 12 Jun 2014 10:54:41 +0200 Subject: [PATCH] Ignore external resources in CssLinkResourceTransormer Prior to this commit, the CssLinkResourceTransformer would transform "external resources", i.e. resources not served by the web application. This commit only allows transformation for resources which path don't contain scheme such as "file://" or "http://". Only relative and absolute paths for resources served by the webapp are valid. Issue: SPR-11860 --- .../resource/CssLinkResourceTransformer.java | 10 +++- .../CssLinkResourceTransformerTests.java | 27 ++++++++++- .../resource/PathResourceResolverTests.java | 46 +++++++++++++++++++ .../web/servlet/resource/test/external.css | 2 + .../web/servlet/resource/test/main.css | 2 - 5 files changed, 82 insertions(+), 5 deletions(-) create mode 100644 spring-webmvc/src/test/java/org/springframework/web/servlet/resource/PathResourceResolverTests.java create mode 100644 spring-webmvc/src/test/resources/org/springframework/web/servlet/resource/test/external.css diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/CssLinkResourceTransformer.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/CssLinkResourceTransformer.java index 35db96db7a..2dac50810d 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/CssLinkResourceTransformer.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/CssLinkResourceTransformer.java @@ -101,7 +101,10 @@ public class CssLinkResourceTransformer implements ResourceTransformer { for (CssLinkInfo info : sortedInfos) { writer.write(content.substring(index, info.getStart())); String link = content.substring(info.getStart(), info.getEnd()); - String newLink = transformerChain.getResolverChain().resolveUrlPath(link, Arrays.asList(resource)); + String newLink = null; + if(!hasScheme(link)) { + newLink = transformerChain.getResolverChain().resolveUrlPath(link, Arrays.asList(resource)); + } if (logger.isTraceEnabled()) { if (newLink != null && !link.equals(newLink)) { logger.trace("Link modified: " + newLink + " (original: " + link + ")"); @@ -118,6 +121,11 @@ public class CssLinkResourceTransformer implements ResourceTransformer { return new TransformedResource(resource, writer.toString().getBytes(DEFAULT_CHARSET)); } + private boolean hasScheme(String link) { + int schemeIndex = link.indexOf(":"); + return schemeIndex > 0 && !link.substring(0, schemeIndex).contains("/"); + } + protected static interface CssLinkParser { diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/CssLinkResourceTransformerTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/CssLinkResourceTransformerTests.java index 6afc078836..a37c7e52da 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/CssLinkResourceTransformerTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/CssLinkResourceTransformerTests.java @@ -17,10 +17,12 @@ package org.springframework.web.servlet.resource; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import org.junit.Before; import org.junit.Test; +import org.mockito.Mockito; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; @@ -77,8 +79,7 @@ public class CssLinkResourceTransformerTests { "@import url(bar-11e16cf79faee7ac698c805cf28248d2.css);\n\n" + "@import \"foo-e36d2e05253c6c7085a91522ce43a0b4.css\";\n" + "@import 'foo-e36d2e05253c6c7085a91522ce43a0b4.css';\n\n" + - "body { background: url(\"images/image-f448cd1d5dba82b774f3202c878230b3.png\") }\n\n" + - "li { list-style: url(http://www.example.com/redball.png) disc }\n"; + "body { background: url(\"images/image-f448cd1d5dba82b774f3202c878230b3.png\") }\n"; String result = new String(transformedResource.getByteArray(), "UTF-8"); result = StringUtils.deleteAny(result, "\r"); @@ -92,4 +93,26 @@ public class CssLinkResourceTransformerTests { assertSame(expected, actual); } + @Test + public void transformExtLinksNotAllowed() throws Exception { + ResourceResolverChain resolverChain = Mockito.mock(DefaultResourceResolverChain.class); + ResourceTransformerChain transformerChain = new DefaultResourceTransformerChain(resolverChain, + Arrays.asList(new CssLinkResourceTransformer())); + + Resource externalCss = new ClassPathResource("test/external.css", getClass()); + Resource resource = transformerChain.transform(this.request, externalCss); + TransformedResource transformedResource = (TransformedResource) resource; + + String expected = "@import url(\"http://example.org/fonts/css\");\n" + + "body { background: url(\"file:///home/spring/image.png\") }"; + String result = new String(transformedResource.getByteArray(), "UTF-8"); + result = StringUtils.deleteAny(result, "\r"); + assertEquals(expected, result); + + Mockito.verify(resolverChain, Mockito.never()) + .resolveUrlPath("http://example.org/fonts/css", Arrays.asList(externalCss)); + Mockito.verify(resolverChain, Mockito.never()) + .resolveUrlPath("file:///home/spring/image.png", Arrays.asList(externalCss)); + } + } diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/PathResourceResolverTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/PathResourceResolverTests.java new file mode 100644 index 0000000000..e119fde68b --- /dev/null +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/PathResourceResolverTests.java @@ -0,0 +1,46 @@ +package org.springframework.web.servlet.resource; + +import static org.junit.Assert.*; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Before; +import org.junit.Test; + +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.Resource; + +/** + * Unit tests for + * {@link org.springframework.web.servlet.resource.PathResourceResolver}. + * + * @author Brian Clozel + */ +public class PathResourceResolverTests { + + private ResourceResolverChain chain; + + private List locations; + + @Before + public void setup() { + + List resolvers = new ArrayList<>(); + resolvers.add(new PathResourceResolver()); + this.chain = new DefaultResourceResolverChain(resolvers); + + this.locations = new ArrayList<>(); + this.locations.add(new ClassPathResource("test/", getClass())); + } + + @Test + public void resolveResourceInternal() { + String file = "bar.css"; + Resource expected = new ClassPathResource("test/" + file, getClass()); + Resource actual = this.chain.resolveResource(null, file, this.locations); + + assertEquals(expected, actual); + } + +} diff --git a/spring-webmvc/src/test/resources/org/springframework/web/servlet/resource/test/external.css b/spring-webmvc/src/test/resources/org/springframework/web/servlet/resource/test/external.css new file mode 100644 index 0000000000..aa0afb2bbc --- /dev/null +++ b/spring-webmvc/src/test/resources/org/springframework/web/servlet/resource/test/external.css @@ -0,0 +1,2 @@ +@import url("http://example.org/fonts/css"); +body { background: url("file:///home/spring/image.png") } \ No newline at end of file diff --git a/spring-webmvc/src/test/resources/org/springframework/web/servlet/resource/test/main.css b/spring-webmvc/src/test/resources/org/springframework/web/servlet/resource/test/main.css index ac14294bf3..a6265ea7ac 100644 --- a/spring-webmvc/src/test/resources/org/springframework/web/servlet/resource/test/main.css +++ b/spring-webmvc/src/test/resources/org/springframework/web/servlet/resource/test/main.css @@ -7,5 +7,3 @@ @import 'foo.css'; body { background: url("images/image.png") } - -li { list-style: url(http://www.example.com/redball.png) disc }