Browse Source

Normalize static resource path early

Rather than leaving it to the Resource implementation, and
potentially normalizing twice, we apply it once as part of the
initial processPath checks.

Closes gh-33689
pull/33720/head
rstoyanchev 1 year ago
parent
commit
3bfbe30a78
  1. 23
      spring-webflux/src/main/java/org/springframework/web/reactive/function/server/PathResourceLookupFunction.java
  2. 20
      spring-webflux/src/main/java/org/springframework/web/reactive/resource/ResourceWebHandler.java
  3. 2
      spring-webflux/src/test/java/org/springframework/web/reactive/resource/ResourceWebHandlerTests.java
  4. 20
      spring-webmvc/src/main/java/org/springframework/web/servlet/function/PathResourceLookupFunction.java
  5. 20
      spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java
  6. 1
      spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandlerTests.java

23
spring-webflux/src/main/java/org/springframework/web/reactive/function/server/PathResourceLookupFunction.java

@ -104,7 +104,8 @@ class PathResourceLookupFunction implements Function<ServerRequest, Mono<Resourc @@ -104,7 +104,8 @@ class PathResourceLookupFunction implements Function<ServerRequest, Mono<Resourc
protected String processPath(String path) {
path = StringUtils.replace(path, "\\", "/");
path = cleanDuplicateSlashes(path);
return cleanLeadingSlash(path);
path = cleanLeadingSlash(path);
return normalizePath(path);
}
private String cleanDuplicateSlashes(String path) {
@ -146,6 +147,21 @@ class PathResourceLookupFunction implements Function<ServerRequest, Mono<Resourc @@ -146,6 +147,21 @@ class PathResourceLookupFunction implements Function<ServerRequest, Mono<Resourc
return (slash ? "/" : "");
}
private static String normalizePath(String path) {
if (path.contains("%")) {
try {
path = URLDecoder.decode(path, StandardCharsets.UTF_8);
}
catch (Exception ex) {
return "";
}
if (path.contains("../")) {
path = StringUtils.cleanPath(path);
}
}
return path;
}
private boolean isInvalidPath(String path) {
if (path.contains("WEB-INF") || path.contains("META-INF")) {
return true;
@ -156,10 +172,7 @@ class PathResourceLookupFunction implements Function<ServerRequest, Mono<Resourc @@ -156,10 +172,7 @@ class PathResourceLookupFunction implements Function<ServerRequest, Mono<Resourc
return true;
}
}
if (path.contains("..") && StringUtils.cleanPath(path).contains("../")) {
return true;
}
return false;
return path.contains("../");
}
/**

20
spring-webflux/src/main/java/org/springframework/web/reactive/resource/ResourceWebHandler.java

@ -523,7 +523,8 @@ public class ResourceWebHandler implements WebHandler, InitializingBean { @@ -523,7 +523,8 @@ public class ResourceWebHandler implements WebHandler, InitializingBean {
protected String processPath(String path) {
path = StringUtils.replace(path, "\\", "/");
path = cleanDuplicateSlashes(path);
return cleanLeadingSlash(path);
path = cleanLeadingSlash(path);
return normalizePath(path);
}
private String cleanDuplicateSlashes(String path) {
@ -565,6 +566,21 @@ public class ResourceWebHandler implements WebHandler, InitializingBean { @@ -565,6 +566,21 @@ public class ResourceWebHandler implements WebHandler, InitializingBean {
return (slash ? "/" : "");
}
private static String normalizePath(String path) {
if (path.contains("%")) {
try {
path = URLDecoder.decode(path, StandardCharsets.UTF_8);
}
catch (Exception ex) {
return "";
}
if (path.contains("../")) {
path = StringUtils.cleanPath(path);
}
}
return path;
}
/**
* Check whether the given path contains invalid escape sequences.
* @param path the path to validate
@ -623,7 +639,7 @@ public class ResourceWebHandler implements WebHandler, InitializingBean { @@ -623,7 +639,7 @@ public class ResourceWebHandler implements WebHandler, InitializingBean {
return true;
}
}
if (path.contains("..") && StringUtils.cleanPath(path).contains("../")) {
if (path.contains("../")) {
if (logger.isWarnEnabled()) {
logger.warn(LogFormatUtils.formatValue(
"Path contains \"../\" after call to StringUtils#cleanPath: [" + path + "]", -1, true));

2
spring-webflux/src/test/java/org/springframework/web/reactive/resource/ResourceWebHandlerTests.java

@ -670,7 +670,6 @@ class ResourceWebHandlerTests { @@ -670,7 +670,6 @@ class ResourceWebHandlerTests {
testInvalidPath("/../.." + secretPath, handler);
testInvalidPath("/%2E%2E/testsecret/secret.txt", handler);
testInvalidPath("/%2E%2E/testsecret/secret.txt", handler);
testInvalidPath("%2F%2F%2E%2E%2F%2F%2E%2E" + secretPath, handler);
}
private void testInvalidPath(String requestPath, ResourceWebHandler handler) {
@ -705,7 +704,6 @@ class ResourceWebHandlerTests { @@ -705,7 +704,6 @@ class ResourceWebHandlerTests {
testResolvePathWithTraversal(method, "/url:" + secretPath);
testResolvePathWithTraversal(method, "////../.." + secretPath);
testResolvePathWithTraversal(method, "/%2E%2E/testsecret/secret.txt");
testResolvePathWithTraversal(method, "%2F%2F%2E%2E%2F%2Ftestsecret/secret.txt");
testResolvePathWithTraversal(method, "url:" + secretPath);
// The following tests fail with a MalformedURLException on Windows

20
spring-webmvc/src/main/java/org/springframework/web/servlet/function/PathResourceLookupFunction.java

@ -105,7 +105,8 @@ class PathResourceLookupFunction implements Function<ServerRequest, Optional<Res @@ -105,7 +105,8 @@ class PathResourceLookupFunction implements Function<ServerRequest, Optional<Res
protected String processPath(String path) {
path = StringUtils.replace(path, "\\", "/");
path = cleanDuplicateSlashes(path);
return cleanLeadingSlash(path);
path = cleanLeadingSlash(path);
return normalizePath(path);
}
private String cleanDuplicateSlashes(String path) {
@ -147,6 +148,21 @@ class PathResourceLookupFunction implements Function<ServerRequest, Optional<Res @@ -147,6 +148,21 @@ class PathResourceLookupFunction implements Function<ServerRequest, Optional<Res
return (slash ? "/" : "");
}
private static String normalizePath(String path) {
if (path.contains("%")) {
try {
path = URLDecoder.decode(path, StandardCharsets.UTF_8);
}
catch (Exception ex) {
return "";
}
if (path.contains("../")) {
path = StringUtils.cleanPath(path);
}
}
return path;
}
private boolean isInvalidPath(String path) {
if (path.contains("WEB-INF") || path.contains("META-INF")) {
return true;
@ -157,7 +173,7 @@ class PathResourceLookupFunction implements Function<ServerRequest, Optional<Res @@ -157,7 +173,7 @@ class PathResourceLookupFunction implements Function<ServerRequest, Optional<Res
return true;
}
}
return path.contains("..") && StringUtils.cleanPath(path).contains("../");
return path.contains("../");
}
private boolean isInvalidEncodedInputPath(String path) {

20
spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java

@ -682,7 +682,8 @@ public class ResourceHttpRequestHandler extends WebContentGenerator @@ -682,7 +682,8 @@ public class ResourceHttpRequestHandler extends WebContentGenerator
protected String processPath(String path) {
path = StringUtils.replace(path, "\\", "/");
path = cleanDuplicateSlashes(path);
return cleanLeadingSlash(path);
path = cleanLeadingSlash(path);
return normalizePath(path);
}
private String cleanDuplicateSlashes(String path) {
@ -724,6 +725,21 @@ public class ResourceHttpRequestHandler extends WebContentGenerator @@ -724,6 +725,21 @@ public class ResourceHttpRequestHandler extends WebContentGenerator
return (slash ? "/" : "");
}
private static String normalizePath(String path) {
if (path.contains("%")) {
try {
path = URLDecoder.decode(path, StandardCharsets.UTF_8);
}
catch (Exception ex) {
return "";
}
if (path.contains("../")) {
path = StringUtils.cleanPath(path);
}
}
return path;
}
/**
* Check whether the given path contains invalid escape sequences.
* @param path the path to validate
@ -783,7 +799,7 @@ public class ResourceHttpRequestHandler extends WebContentGenerator @@ -783,7 +799,7 @@ public class ResourceHttpRequestHandler extends WebContentGenerator
return true;
}
}
if (path.contains("..") && StringUtils.cleanPath(path).contains("../")) {
if (path.contains("../")) {
if (logger.isWarnEnabled()) {
logger.warn(LogFormatUtils.formatValue(
"Path contains \"../\" after call to StringUtils#cleanPath: [" + path + "]", -1, true));

1
spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandlerTests.java

@ -656,7 +656,6 @@ class ResourceHttpRequestHandlerTests { @@ -656,7 +656,6 @@ class ResourceHttpRequestHandlerTests {
testInvalidPath("/../.." + secretPath);
testInvalidPath("/%2E%2E/testsecret/secret.txt");
testInvalidPath("/%2E%2E/testsecret/secret.txt");
testInvalidPath("%2F%2F%2E%2E%2F%2F%2E%2E" + secretPath);
}
private void testInvalidPath(String requestPath) {

Loading…
Cancel
Save