diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/resource/ResourceHandlerUtils.java b/spring-webflux/src/main/java/org/springframework/web/reactive/resource/ResourceHandlerUtils.java
index 2e95c58fd5b..e2687b2909f 100644
--- a/spring-webflux/src/main/java/org/springframework/web/reactive/resource/ResourceHandlerUtils.java
+++ b/spring-webflux/src/main/java/org/springframework/web/reactive/resource/ResourceHandlerUtils.java
@@ -19,7 +19,6 @@ package org.springframework.web.reactive.resource;
import java.io.IOException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
-import java.util.Locale;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -69,17 +68,15 @@ public abstract class ResourceHandlerUtils {
}
else if (location instanceof ClassPathResource classPathResource) {
path = classPathResource.getPath();
- if (path.isEmpty() || "/".equals(path)) {
- logger.warn("Resource location '" + location + "' is considered unsafe " +
- "and should not be used as it provides access to the entire classpath.");
- }
+ Assert.isTrue(!path.isEmpty() && !"/".equals(path),
+ () -> "Resource location '" + location + "' is considered unsafe " +
+ "and cannot be used as it provides access to the entire classpath.");
}
else if (location instanceof ContextResource contextResource) {
path = contextResource.getPathWithinContext();
- if ("/".equals(path)) {
- logger.warn("Resource location '" + location + "' is considered unsafe " +
- "and should not be used as it provides access to the root servlet context.");
- }
+ Assert.isTrue(!"/".equals(path),
+ () -> "Resource location '" + location + "' is considered unsafe " +
+ "and cannot be used as it provides access to the root servlet context.");
}
else if (location instanceof UrlResource) {
path = location.getURL().toExternalForm();
@@ -176,7 +173,6 @@ public abstract class ResourceHandlerUtils {
/**
* Checks for invalid resource input paths rejecting the following:
*
- * - Paths that contain "WEB-INF" or "META-INF"
*
- Paths that contain "../" after a call to
* {@link StringUtils#cleanPath}.
*
- Paths that represent a {@link ResourceUtils#isUrl
@@ -189,14 +185,6 @@ public abstract class ResourceHandlerUtils {
* @return {@code true} if the path is invalid, {@code false} otherwise
*/
public static boolean isInvalidPath(String path) {
- String pathLowerCase = path.toLowerCase(Locale.ROOT);
- if (pathLowerCase.contains("web-inf") || pathLowerCase.contains("meta-inf")) {
- if (logger.isWarnEnabled()) {
- logger.warn(LogFormatUtils.formatValue(
- "Path with \"WEB-INF\" or \"META-INF\": [" + path + "]", -1, true));
- }
- return true;
- }
if (path.contains(":/")) {
String relativePath = (path.charAt(0) == '/' ? path.substring(1) : path);
if (ResourceUtils.isUrl(relativePath) || relativePath.startsWith("url:")) {
diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/resource/ResourceHandlerUtilsTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/resource/ResourceHandlerUtilsTests.java
new file mode 100644
index 00000000000..3b3c0059c71
--- /dev/null
+++ b/spring-webflux/src/test/java/org/springframework/web/reactive/resource/ResourceHandlerUtilsTests.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2002-present 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.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.web.reactive.resource;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URL;
+
+import org.junit.jupiter.api.Test;
+
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.core.io.ContextResource;
+import org.springframework.core.io.FileSystemResource;
+import org.springframework.core.io.PathResource;
+import org.springframework.core.io.UrlResource;
+
+import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
+import static org.assertj.core.api.Assertions.assertThatNoException;
+
+/**
+ * Tests for {@link ResourceHandlerUtils}.
+ */
+class ResourceHandlerUtilsTests {
+
+ @Test
+ @SuppressWarnings("removal")
+ void assertResourceLocation() throws Exception {
+ assertThatNoException().isThrownBy(() ->
+ ResourceHandlerUtils.assertResourceLocation(new ClassPathResource("test/")));
+
+ assertThatNoException().isThrownBy(() ->
+ ResourceHandlerUtils.assertResourceLocation(new FileSystemResource("test/")));
+
+ assertThatNoException().isThrownBy(() ->
+ ResourceHandlerUtils.assertResourceLocation(new PathResource("test/")));
+
+ assertThatNoException().isThrownBy(() ->
+ ResourceHandlerUtils.assertResourceLocation(new UrlResource("file:/test/")));
+
+ assertThatNoException().isThrownBy(() ->
+ ResourceHandlerUtils.assertResourceLocation(new TestContextResource("/test/")));
+ }
+
+ @Test
+ void assertResourceLocationShouldRejectNull() {
+ assertThatIllegalArgumentException().isThrownBy(() ->
+ ResourceHandlerUtils.assertResourceLocation(null))
+ .withMessage("Resource location must not be null");
+ }
+
+ @Test
+ void assertResourceLocationShouldRejectNotEndWithSlash() {
+ assertThatIllegalArgumentException().isThrownBy(() ->
+ ResourceHandlerUtils.assertResourceLocation(new ClassPathResource("test")))
+ .withMessageContaining("Resource location does not end with slash");
+ }
+
+ @Test
+ void assertResourceLocationShouldRejectUnsafeClassPathResource() {
+ assertThatIllegalArgumentException().isThrownBy(() ->
+ ResourceHandlerUtils.assertResourceLocation(new ClassPathResource("")))
+ .withMessageContaining("is considered unsafe");
+
+ assertThatIllegalArgumentException().isThrownBy(() ->
+ ResourceHandlerUtils.assertResourceLocation(new ClassPathResource("/")))
+ .withMessageContaining("is considered unsafe");
+ }
+
+ @Test
+ void assertResourceLocationShouldRejectUnsafeContextResource() {
+ assertThatIllegalArgumentException().isThrownBy(() ->
+ ResourceHandlerUtils.assertResourceLocation(new TestContextResource("/")))
+ .withMessageContaining("is considered unsafe");
+ }
+
+ private static class TestContextResource implements ContextResource {
+
+ private final String path;
+
+ TestContextResource(String path) {
+ this.path = path;
+ }
+
+ @Override
+ public String getPathWithinContext() {
+ return this.path;
+ }
+
+ @Override
+ public boolean exists() {
+ return false;
+ }
+
+ @Override
+ public URL getURL() throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public URI getURI() throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public File getFile() throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public long contentLength() throws IOException {
+ return 0;
+ }
+
+ @Override
+ public long lastModified() throws IOException {
+ return 0;
+ }
+
+ @Override
+ public org.springframework.core.io.Resource createRelative(String relativePath) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getFilename() {
+ return null;
+ }
+
+ @Override
+ public String getDescription() {
+ return "TestContextResource";
+ }
+
+ @Override
+ public InputStream getInputStream() throws IOException {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+}
diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHandlerUtils.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHandlerUtils.java
index 5b1a133c8fa..214f80fc5f1 100644
--- a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHandlerUtils.java
+++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHandlerUtils.java
@@ -19,7 +19,6 @@ package org.springframework.web.servlet.resource;
import java.io.IOException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
-import java.util.Locale;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -70,17 +69,15 @@ public abstract class ResourceHandlerUtils {
}
else if (location instanceof ClassPathResource classPathResource) {
path = classPathResource.getPath();
- if (path.isEmpty() || "/".equals(path)) {
- logger.warn("Resource location '" + location + "' is considered unsafe " +
- "and should not be used as it provides access to the entire classpath.");
- }
+ Assert.isTrue(!path.isEmpty() && !"/".equals(path),
+ () -> "Resource location '" + location + "' is considered unsafe " +
+ "and cannot be used as it provides access to the entire classpath.");
}
else if (location instanceof ContextResource contextResource) {
path = contextResource.getPathWithinContext();
- if ("/".equals(path)) {
- logger.warn("Resource location '" + location + "' is considered unsafe " +
- "and should not be used as it provides access to the root servlet context.");
- }
+ Assert.isTrue(!"/".equals(path),
+ () -> "Resource location '" + location + "' is considered unsafe " +
+ "and cannot be used as it provides access to the root servlet context.");
}
else if (location instanceof UrlResource) {
path = location.getURL().toExternalForm();
@@ -177,7 +174,6 @@ public abstract class ResourceHandlerUtils {
/**
* Checks for invalid resource input paths rejecting the following:
*
- * - Paths that contain "WEB-INF" or "META-INF"
*
- Paths that contain "../" after a call to
* {@link org.springframework.util.StringUtils#cleanPath}.
*
- Paths that represent a {@link org.springframework.util.ResourceUtils#isUrl
@@ -190,14 +186,6 @@ public abstract class ResourceHandlerUtils {
* @return {@code true} if the path is invalid, {@code false} otherwise
*/
public static boolean isInvalidPath(String path) {
- String pathLowerCase = path.toLowerCase(Locale.ROOT);
- if (pathLowerCase.contains("web-inf") || pathLowerCase.contains("meta-inf")) {
- if (logger.isWarnEnabled()) {
- logger.warn(LogFormatUtils.formatValue(
- "Path with \"WEB-INF\" or \"META-INF\": [" + path + "]", -1, true));
- }
- return true;
- }
if (path.contains(":/")) {
String relativePath = (path.charAt(0) == '/' ? path.substring(1) : path);
if (ResourceUtils.isUrl(relativePath) || relativePath.startsWith("url:")) {
diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfigurationTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfigurationTests.java
index ce8be354416..019a7c0ba09 100644
--- a/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfigurationTests.java
+++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfigurationTests.java
@@ -239,7 +239,7 @@ class DelegatingWebMvcConfigurationTests {
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
- registry.addResourceHandler("/resources/**").addResourceLocations("/");
+ registry.addResourceHandler("/resources/**").addResourceLocations("/static");
}
};
@@ -304,7 +304,7 @@ class DelegatingWebMvcConfigurationTests {
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
- registry.addResourceHandler("/resources/**").addResourceLocations("/");
+ registry.addResourceHandler("/resources/**").addResourceLocations("/static");
}
};
diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceHandlerUtilsTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceHandlerUtilsTests.java
new file mode 100644
index 00000000000..84c72de129b
--- /dev/null
+++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceHandlerUtilsTests.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2002-present 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.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.web.servlet.resource;
+
+import org.junit.jupiter.api.Test;
+
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.core.io.FileSystemResource;
+import org.springframework.core.io.PathResource;
+import org.springframework.core.io.UrlResource;
+import org.springframework.web.context.support.ServletContextResource;
+import org.springframework.web.testfixture.servlet.MockServletContext;
+
+import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
+import static org.assertj.core.api.Assertions.assertThatNoException;
+
+/**
+ * Tests for {@link ResourceHandlerUtils}.
+ */
+class ResourceHandlerUtilsTests {
+
+ @Test
+ @SuppressWarnings("removal")
+ void assertResourceLocation() throws Exception {
+ assertThatNoException().isThrownBy(() ->
+ ResourceHandlerUtils.assertResourceLocation(new ClassPathResource("test/")));
+
+ assertThatNoException().isThrownBy(() ->
+ ResourceHandlerUtils.assertResourceLocation(new FileSystemResource("test/")));
+
+ assertThatNoException().isThrownBy(() ->
+ ResourceHandlerUtils.assertResourceLocation(new PathResource("test/")));
+
+ assertThatNoException().isThrownBy(() ->
+ ResourceHandlerUtils.assertResourceLocation(new UrlResource("file:/test/")));
+
+ assertThatNoException().isThrownBy(() ->
+ ResourceHandlerUtils.assertResourceLocation(new ServletContextResource(new MockServletContext(), "/test/")));
+ }
+
+ @Test
+ void assertResourceLocationShouldRejectNull() {
+ assertThatIllegalArgumentException().isThrownBy(() ->
+ ResourceHandlerUtils.assertResourceLocation(null))
+ .withMessage("Resource location must not be null");
+ }
+
+ @Test
+ void assertResourceLocationShouldRejectNotEndWithSlash() {
+ assertThatIllegalArgumentException().isThrownBy(() ->
+ ResourceHandlerUtils.assertResourceLocation(new ClassPathResource("test")))
+ .withMessageContaining("Resource location does not end with slash");
+ }
+
+ @Test
+ void assertResourceLocationShouldRejectUnsafeClassPathResource() {
+ assertThatIllegalArgumentException().isThrownBy(() ->
+ ResourceHandlerUtils.assertResourceLocation(new ClassPathResource("")))
+ .withMessageContaining("is considered unsafe");
+
+ assertThatIllegalArgumentException().isThrownBy(() ->
+ ResourceHandlerUtils.assertResourceLocation(new ClassPathResource("/")))
+ .withMessageContaining("is considered unsafe");
+ }
+
+ @Test
+ void assertResourceLocationShouldRejectUnsafeContextResource() {
+ assertThatIllegalArgumentException().isThrownBy(() ->
+ ResourceHandlerUtils.assertResourceLocation(new ServletContextResource(new MockServletContext(), "/")))
+ .withMessageContaining("is considered unsafe");
+ }
+
+}
diff --git a/spring-webmvc/src/test/resources/org/springframework/web/servlet/config/mvc-config-custom-pattern-parser.xml b/spring-webmvc/src/test/resources/org/springframework/web/servlet/config/mvc-config-custom-pattern-parser.xml
index 4e3e5bec51d..a95c8c72c00 100644
--- a/spring-webmvc/src/test/resources/org/springframework/web/servlet/config/mvc-config-custom-pattern-parser.xml
+++ b/spring-webmvc/src/test/resources/org/springframework/web/servlet/config/mvc-config-custom-pattern-parser.xml
@@ -12,6 +12,6 @@
-
+
diff --git a/spring-webmvc/src/test/resources/org/springframework/web/servlet/config/mvc-config-deprecated-path-matcher.xml b/spring-webmvc/src/test/resources/org/springframework/web/servlet/config/mvc-config-deprecated-path-matcher.xml
index 78afbe8cb6f..bbf133b14ea 100644
--- a/spring-webmvc/src/test/resources/org/springframework/web/servlet/config/mvc-config-deprecated-path-matcher.xml
+++ b/spring-webmvc/src/test/resources/org/springframework/web/servlet/config/mvc-config-deprecated-path-matcher.xml
@@ -12,6 +12,6 @@
-
+
diff --git a/spring-webmvc/src/test/resources/org/springframework/web/servlet/config/mvc-config-path-matching-mappings.xml b/spring-webmvc/src/test/resources/org/springframework/web/servlet/config/mvc-config-path-matching-mappings.xml
index 91d21e6695f..dfd73140dfe 100644
--- a/spring-webmvc/src/test/resources/org/springframework/web/servlet/config/mvc-config-path-matching-mappings.xml
+++ b/spring-webmvc/src/test/resources/org/springframework/web/servlet/config/mvc-config-path-matching-mappings.xml
@@ -5,7 +5,7 @@
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
-
+
-
+
diff --git a/spring-webmvc/src/test/resources/org/springframework/web/servlet/config/mvc-config-resources-chain.xml b/spring-webmvc/src/test/resources/org/springframework/web/servlet/config/mvc-config-resources-chain.xml
index 6ab67c7449f..5adf76a58fd 100644
--- a/spring-webmvc/src/test/resources/org/springframework/web/servlet/config/mvc-config-resources-chain.xml
+++ b/spring-webmvc/src/test/resources/org/springframework/web/servlet/config/mvc-config-resources-chain.xml
@@ -27,7 +27,7 @@
-
+
diff --git a/spring-webmvc/src/test/resources/org/springframework/web/servlet/config/mvc-config-resources-optional-attrs.xml b/spring-webmvc/src/test/resources/org/springframework/web/servlet/config/mvc-config-resources-optional-attrs.xml
index ddaa6ccd9b2..9145315afad 100644
--- a/spring-webmvc/src/test/resources/org/springframework/web/servlet/config/mvc-config-resources-optional-attrs.xml
+++ b/spring-webmvc/src/test/resources/org/springframework/web/servlet/config/mvc-config-resources-optional-attrs.xml
@@ -5,6 +5,6 @@
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
-
+
diff --git a/spring-webmvc/src/test/resources/org/springframework/web/servlet/config/mvc-config-resources.xml b/spring-webmvc/src/test/resources/org/springframework/web/servlet/config/mvc-config-resources.xml
index 8348ae99586..997b80d1bd5 100644
--- a/spring-webmvc/src/test/resources/org/springframework/web/servlet/config/mvc-config-resources.xml
+++ b/spring-webmvc/src/test/resources/org/springframework/web/servlet/config/mvc-config-resources.xml
@@ -6,6 +6,6 @@
http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
-
+