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
This commit is contained in:
parent
32962894a0
commit
3bfbe30a78
|
@ -104,7 +104,8 @@ class PathResourceLookupFunction implements Function<ServerRequest, Mono<Resourc
|
||||||
protected String processPath(String path) {
|
protected String processPath(String path) {
|
||||||
path = StringUtils.replace(path, "\\", "/");
|
path = StringUtils.replace(path, "\\", "/");
|
||||||
path = cleanDuplicateSlashes(path);
|
path = cleanDuplicateSlashes(path);
|
||||||
return cleanLeadingSlash(path);
|
path = cleanLeadingSlash(path);
|
||||||
|
return normalizePath(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String cleanDuplicateSlashes(String path) {
|
private String cleanDuplicateSlashes(String path) {
|
||||||
|
@ -146,6 +147,21 @@ class PathResourceLookupFunction implements Function<ServerRequest, Mono<Resourc
|
||||||
return (slash ? "/" : "");
|
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) {
|
private boolean isInvalidPath(String path) {
|
||||||
if (path.contains("WEB-INF") || path.contains("META-INF")) {
|
if (path.contains("WEB-INF") || path.contains("META-INF")) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -156,10 +172,7 @@ class PathResourceLookupFunction implements Function<ServerRequest, Mono<Resourc
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (path.contains("..") && StringUtils.cleanPath(path).contains("../")) {
|
return path.contains("../");
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -523,7 +523,8 @@ public class ResourceWebHandler implements WebHandler, InitializingBean {
|
||||||
protected String processPath(String path) {
|
protected String processPath(String path) {
|
||||||
path = StringUtils.replace(path, "\\", "/");
|
path = StringUtils.replace(path, "\\", "/");
|
||||||
path = cleanDuplicateSlashes(path);
|
path = cleanDuplicateSlashes(path);
|
||||||
return cleanLeadingSlash(path);
|
path = cleanLeadingSlash(path);
|
||||||
|
return normalizePath(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String cleanDuplicateSlashes(String path) {
|
private String cleanDuplicateSlashes(String path) {
|
||||||
|
@ -565,6 +566,21 @@ public class ResourceWebHandler implements WebHandler, InitializingBean {
|
||||||
return (slash ? "/" : "");
|
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.
|
* Check whether the given path contains invalid escape sequences.
|
||||||
* @param path the path to validate
|
* @param path the path to validate
|
||||||
|
@ -623,7 +639,7 @@ public class ResourceWebHandler implements WebHandler, InitializingBean {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (path.contains("..") && StringUtils.cleanPath(path).contains("../")) {
|
if (path.contains("../")) {
|
||||||
if (logger.isWarnEnabled()) {
|
if (logger.isWarnEnabled()) {
|
||||||
logger.warn(LogFormatUtils.formatValue(
|
logger.warn(LogFormatUtils.formatValue(
|
||||||
"Path contains \"../\" after call to StringUtils#cleanPath: [" + path + "]", -1, true));
|
"Path contains \"../\" after call to StringUtils#cleanPath: [" + path + "]", -1, true));
|
||||||
|
|
|
@ -670,7 +670,6 @@ class ResourceWebHandlerTests {
|
||||||
testInvalidPath("/../.." + secretPath, handler);
|
testInvalidPath("/../.." + secretPath, handler);
|
||||||
testInvalidPath("/%2E%2E/testsecret/secret.txt", handler);
|
testInvalidPath("/%2E%2E/testsecret/secret.txt", 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) {
|
private void testInvalidPath(String requestPath, ResourceWebHandler handler) {
|
||||||
|
@ -705,7 +704,6 @@ class ResourceWebHandlerTests {
|
||||||
testResolvePathWithTraversal(method, "/url:" + secretPath);
|
testResolvePathWithTraversal(method, "/url:" + secretPath);
|
||||||
testResolvePathWithTraversal(method, "////../.." + secretPath);
|
testResolvePathWithTraversal(method, "////../.." + secretPath);
|
||||||
testResolvePathWithTraversal(method, "/%2E%2E/testsecret/secret.txt");
|
testResolvePathWithTraversal(method, "/%2E%2E/testsecret/secret.txt");
|
||||||
testResolvePathWithTraversal(method, "%2F%2F%2E%2E%2F%2Ftestsecret/secret.txt");
|
|
||||||
testResolvePathWithTraversal(method, "url:" + secretPath);
|
testResolvePathWithTraversal(method, "url:" + secretPath);
|
||||||
|
|
||||||
// The following tests fail with a MalformedURLException on Windows
|
// The following tests fail with a MalformedURLException on Windows
|
||||||
|
|
|
@ -105,7 +105,8 @@ class PathResourceLookupFunction implements Function<ServerRequest, Optional<Res
|
||||||
protected String processPath(String path) {
|
protected String processPath(String path) {
|
||||||
path = StringUtils.replace(path, "\\", "/");
|
path = StringUtils.replace(path, "\\", "/");
|
||||||
path = cleanDuplicateSlashes(path);
|
path = cleanDuplicateSlashes(path);
|
||||||
return cleanLeadingSlash(path);
|
path = cleanLeadingSlash(path);
|
||||||
|
return normalizePath(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String cleanDuplicateSlashes(String path) {
|
private String cleanDuplicateSlashes(String path) {
|
||||||
|
@ -147,6 +148,21 @@ class PathResourceLookupFunction implements Function<ServerRequest, Optional<Res
|
||||||
return (slash ? "/" : "");
|
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) {
|
private boolean isInvalidPath(String path) {
|
||||||
if (path.contains("WEB-INF") || path.contains("META-INF")) {
|
if (path.contains("WEB-INF") || path.contains("META-INF")) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -157,7 +173,7 @@ class PathResourceLookupFunction implements Function<ServerRequest, Optional<Res
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return path.contains("..") && StringUtils.cleanPath(path).contains("../");
|
return path.contains("../");
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isInvalidEncodedInputPath(String path) {
|
private boolean isInvalidEncodedInputPath(String path) {
|
||||||
|
|
|
@ -682,7 +682,8 @@ public class ResourceHttpRequestHandler extends WebContentGenerator
|
||||||
protected String processPath(String path) {
|
protected String processPath(String path) {
|
||||||
path = StringUtils.replace(path, "\\", "/");
|
path = StringUtils.replace(path, "\\", "/");
|
||||||
path = cleanDuplicateSlashes(path);
|
path = cleanDuplicateSlashes(path);
|
||||||
return cleanLeadingSlash(path);
|
path = cleanLeadingSlash(path);
|
||||||
|
return normalizePath(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String cleanDuplicateSlashes(String path) {
|
private String cleanDuplicateSlashes(String path) {
|
||||||
|
@ -724,6 +725,21 @@ public class ResourceHttpRequestHandler extends WebContentGenerator
|
||||||
return (slash ? "/" : "");
|
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.
|
* Check whether the given path contains invalid escape sequences.
|
||||||
* @param path the path to validate
|
* @param path the path to validate
|
||||||
|
@ -783,7 +799,7 @@ public class ResourceHttpRequestHandler extends WebContentGenerator
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (path.contains("..") && StringUtils.cleanPath(path).contains("../")) {
|
if (path.contains("../")) {
|
||||||
if (logger.isWarnEnabled()) {
|
if (logger.isWarnEnabled()) {
|
||||||
logger.warn(LogFormatUtils.formatValue(
|
logger.warn(LogFormatUtils.formatValue(
|
||||||
"Path contains \"../\" after call to StringUtils#cleanPath: [" + path + "]", -1, true));
|
"Path contains \"../\" after call to StringUtils#cleanPath: [" + path + "]", -1, true));
|
||||||
|
|
|
@ -656,7 +656,6 @@ class ResourceHttpRequestHandlerTests {
|
||||||
testInvalidPath("/../.." + secretPath);
|
testInvalidPath("/../.." + secretPath);
|
||||||
testInvalidPath("/%2E%2E/testsecret/secret.txt");
|
testInvalidPath("/%2E%2E/testsecret/secret.txt");
|
||||||
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) {
|
private void testInvalidPath(String requestPath) {
|
||||||
|
|
Loading…
Reference in New Issue