Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Backports for addressing CVE-2024-38819 #12

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@ public Mono<Resource> apply(ServerRequest request) {
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) {
Expand Down Expand Up @@ -146,6 +147,29 @@ else if (path.charAt(i) > ' ' && path.charAt(i) != 127) {
return (slash ? "/" : "");
}

private static String normalizePath(String path) {
String result = path;
if (result.contains("%")) {
result = decode(result);
if (result.contains("%")) {
result = decode(result);
}
if (result.contains("../")) {
return StringUtils.cleanPath(result);
}
}
return path;
}

private static String decode(String path) {
try {
return URLDecoder.decode(path, StandardCharsets.UTF_8);
}
catch (Exception ex) {
return "";
}
}

private boolean isInvalidPath(String path) {
if (path.contains("WEB-INF") || path.contains("META-INF")) {
return true;
Expand All @@ -156,10 +180,7 @@ private boolean isInvalidPath(String path) {
return true;
}
}
if (path.contains("..") && StringUtils.cleanPath(path).contains("../")) {
return true;
}
return false;
return path.contains("../");
}

/**
Expand Down Expand Up @@ -212,7 +233,7 @@ else if (resource instanceof ClassPathResource classPathResource) {
return true;
}
locationPath = (locationPath.endsWith("/") || locationPath.isEmpty() ? locationPath : locationPath + "/");
return (resourcePath.startsWith(locationPath) && !isInvalidEncodedInputPath(resourcePath));
return (resourcePath.startsWith(locationPath) && !isInvalidEncodedResourcePath(resourcePath));
}

private boolean isInvalidEncodedResourcePath(String resourcePath) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -496,7 +496,8 @@ private String getResourcePath(ServerWebExchange exchange) {
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) {
Expand Down Expand Up @@ -538,6 +539,29 @@ else if (path.charAt(i) > ' ' && path.charAt(i) != 127) {
return (slash ? "/" : "");
}

private static String normalizePath(String path) {
String result = path;
if (result.contains("%")) {
result = decode(result);
if (result.contains("%")) {
result = decode(result);
}
if (result.contains("../")) {
return StringUtils.cleanPath(result);
}
}
return path;
}

private static String decode(String path) {
try {
return URLDecoder.decode(path, StandardCharsets.UTF_8);
}
catch (Exception ex) {
return "";
}
}

/**
* Check whether the given path contains invalid escape sequences.
* @param path the path to validate
Expand Down Expand Up @@ -596,7 +620,7 @@ protected boolean isInvalidPath(String path) {
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));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,6 @@ void invalidPath() throws Exception {
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) {
Expand Down Expand Up @@ -392,7 +391,6 @@ void resolvePathWithTraversal(HttpMethod method) throws Exception {
testResolvePathWithTraversal(method, "/url:" + secretPath, location);
testResolvePathWithTraversal(method, "////../.." + secretPath, location);
testResolvePathWithTraversal(method, "/%2E%2E/testsecret/secret.txt", location);
testResolvePathWithTraversal(method, "%2F%2F%2E%2E%2F%2Ftestsecret/secret.txt", location);
testResolvePathWithTraversal(method, "url:" + secretPath, location);

// The following tests fail with a MalformedURLException on Windows
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,8 @@ public Optional<Resource> apply(ServerRequest request) {
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) {
Expand Down Expand Up @@ -147,6 +148,29 @@ else if (path.charAt(i) > ' ' && path.charAt(i) != 127) {
return (slash ? "/" : "");
}

private static String normalizePath(String path) {
String result = path;
if (result.contains("%")) {
result = decode(result);
if (result.contains("%")) {
result = decode(result);
}
if (result.contains("../")) {
return StringUtils.cleanPath(result);
}
}
return path;
}

private static String decode(String path) {
try {
return URLDecoder.decode(path, StandardCharsets.UTF_8);
}
catch (Exception ex) {
return "";
}
}

private boolean isInvalidPath(String path) {
if (path.contains("WEB-INF") || path.contains("META-INF")) {
return true;
Expand All @@ -157,7 +181,7 @@ private boolean isInvalidPath(String path) {
return true;
}
}
return path.contains("..") && StringUtils.cleanPath(path).contains("../");
return path.contains("../");
}

private boolean isInvalidEncodedInputPath(String path) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -650,7 +650,8 @@ protected Resource getResource(HttpServletRequest request) throws IOException {
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) {
Expand Down Expand Up @@ -692,6 +693,29 @@ else if (path.charAt(i) > ' ' && path.charAt(i) != 127) {
return (slash ? "/" : "");
}

private static String normalizePath(String path) {
String result = path;
if (result.contains("%")) {
result = decode(result);
if (result.contains("%")) {
result = decode(result);
}
if (result.contains("../")) {
return StringUtils.cleanPath(result);
}
}
return path;
}

private static String decode(String path) {
try {
return URLDecoder.decode(path, StandardCharsets.UTF_8);
}
catch (Exception ex) {
return "";
}
}

/**
* Check whether the given path contains invalid escape sequences.
* @param path the path to validate
Expand Down Expand Up @@ -751,7 +775,7 @@ protected boolean isInvalidPath(String path) {
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));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,6 @@ void testInvalidPath() throws Exception {
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, ResourceHttpRequestHandler handler) throws Exception {
Expand Down