diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/resource/CssLinkResourceTransformer.java b/spring-webflux/src/main/java/org/springframework/web/reactive/resource/CssLinkResourceTransformer.java index f612371233f..a8bb5872494 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/resource/CssLinkResourceTransformer.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/resource/CssLinkResourceTransformer.java @@ -176,9 +176,12 @@ public class CssLinkResourceTransformer extends ResourceTransformerSupport { return; } position += getKeyword().length(); - while (Character.isWhitespace(content.charAt(position))) { + while (position < content.length() && Character.isWhitespace(content.charAt(position))) { position++; } + if (position == content.length()) { + return; + } if (content.charAt(position) == '\'') { position = extractLink(position, '\'', content, result); } @@ -194,6 +197,12 @@ public class CssLinkResourceTransformer extends ResourceTransformerSupport { protected int extractLink(int index, char endChar, String content, Set result) { int start = index + 1; int end = content.indexOf(endChar, start); + if (end == -1) { + if (logger.isTraceEnabled()) { + logger.trace("Unterminated link at index " + start + ", no closing '" + endChar + "'"); + } + return content.length(); + } result.add(new ContentChunkInfo(start, end, true)); return end + 1; } diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/resource/CssLinkResourceTransformerTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/resource/CssLinkResourceTransformerTests.java index 8f56b3a0955..7fca7166b97 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/resource/CssLinkResourceTransformerTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/resource/CssLinkResourceTransformerTests.java @@ -183,6 +183,25 @@ class CssLinkResourceTransformerTests { .verify(); } + @Test + void transformUnclosedUrlFunction() { + MockServerWebExchange exchange = MockServerWebExchange.from(get("/static/unclosed_url_function.css")); + Resource css = getResource("unclosed_url_function.css"); + String expected = """ + body { background: url("/static/images/image-f448cd1d5dba82b774f3202c878230b3.png?#iefix") } + div { background: url(images/image.png + """; + + StepVerifier.create(this.transformerChain.transform(exchange, css) + .cast(TransformedResource.class)) + .consumeNextWith(transformedResource -> { + String result = new String(transformedResource.getByteArray(), UTF_8); + assertThat(result).isEqualToNormalizingNewlines(expected); + }) + .expectComplete() + .verify(); + } + private Resource getResource(String filePath) { return new ClassPathResource("test/" + filePath, getClass()); } diff --git a/spring-webflux/src/test/resources/org/springframework/web/reactive/resource/test/unclosed_url_function.css b/spring-webflux/src/test/resources/org/springframework/web/reactive/resource/test/unclosed_url_function.css new file mode 100644 index 00000000000..067404446bd --- /dev/null +++ b/spring-webflux/src/test/resources/org/springframework/web/reactive/resource/test/unclosed_url_function.css @@ -0,0 +1,2 @@ +body { background: url("images/image.png?#iefix") } +div { background: url(images/image.png 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 6b379ab9b05..136d8d2c619 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 @@ -140,9 +140,12 @@ public class CssLinkResourceTransformer extends ResourceTransformerSupport { return; } position += getKeyword().length(); - while (Character.isWhitespace(content.charAt(position))) { + while (position < content.length() && Character.isWhitespace(content.charAt(position))) { position++; } + if (position == content.length()) { + return; + } if (content.charAt(position) == '\'') { position = extractLink(position, "'", content, result); } @@ -158,6 +161,12 @@ public class CssLinkResourceTransformer extends ResourceTransformerSupport { protected int extractLink(int index, String endKey, String content, SortedSet linksToAdd) { int start = index + 1; int end = content.indexOf(endKey, start); + if (end == -1) { + if (logger.isTraceEnabled()) { + logger.trace("Unterminated link at index " + start + ", no closing \"" + endKey + "\""); + } + return content.length(); + } linksToAdd.add(new ContentChunkInfo(start, end)); return end + endKey.length(); } 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 021ef78e8c2..f5525a9a298 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 @@ -167,6 +167,20 @@ class CssLinkResourceTransformerTests { assertThat(result).isEqualToNormalizingNewlines(expected); } + @Test + void transformUnclosedUrlFunction() throws Exception { + this.request = new MockHttpServletRequest("GET", "/static/unclosed_url_function.css"); + Resource css = getResource("unclosed_url_function.css"); + String expected = """ + body { background: url("/static/images/image-f448cd1d5dba82b774f3202c878230b3.png?#iefix") } + div { background: url(images/image.png + """; + + TransformedResource actual = (TransformedResource) this.transformerChain.transform(this.request, css); + String result = new String(actual.getByteArray(), UTF_8); + assertThat(result).isEqualToNormalizingNewlines(expected); + } + private Resource getResource(String filePath) { return new ClassPathResource("test/" + filePath, getClass()); } diff --git a/spring-webmvc/src/test/resources/org/springframework/web/servlet/resource/test/unclosed_url_function.css b/spring-webmvc/src/test/resources/org/springframework/web/servlet/resource/test/unclosed_url_function.css new file mode 100644 index 00000000000..067404446bd --- /dev/null +++ b/spring-webmvc/src/test/resources/org/springframework/web/servlet/resource/test/unclosed_url_function.css @@ -0,0 +1,2 @@ +body { background: url("images/image.png?#iefix") } +div { background: url(images/image.png