From ed0d9cc3b1066f3965f54287c4b8ed17bf4497e7 Mon Sep 17 00:00:00 2001 From: sgayangi Date: Wed, 18 Dec 2024 11:09:18 +0530 Subject: [PATCH 1/4] Update processPath for double encoding --- .../server/PathResourceLookupFunction.java | 23 +++++++++++++++---- .../reactive/resource/ResourceWebHandler.java | 20 ++++++++++++++-- .../resource/ResourceWebHandlerTests.java | 2 -- .../function/PathResourceLookupFunction.java | 20 ++++++++++++++-- .../resource/ResourceHttpRequestHandler.java | 20 ++++++++++++++-- .../ResourceHttpRequestHandlerTests.java | 1 - 6 files changed, 72 insertions(+), 14 deletions(-) diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/PathResourceLookupFunction.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/PathResourceLookupFunction.java index de5d4fa742b5..72e20d872e87 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/PathResourceLookupFunction.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/PathResourceLookupFunction.java @@ -104,7 +104,8 @@ public Mono 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) { StringBuilder sb = null; @@ -145,6 +146,21 @@ else if (path.charAt(i) > ' ' && path.charAt(i) != 127) { 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; @@ -155,10 +171,7 @@ private boolean isInvalidPath(String path) { return true; } } - if (path.contains("..") && StringUtils.cleanPath(path).contains("../")) { - return true; - } - return false; + return path.contains("../"); } /** diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/resource/ResourceWebHandler.java b/spring-webflux/src/main/java/org/springframework/web/reactive/resource/ResourceWebHandler.java index 94866018839d..937d3f7d1982 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/resource/ResourceWebHandler.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/resource/ResourceWebHandler.java @@ -485,7 +485,8 @@ protected Mono getResource(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) { @@ -527,6 +528,21 @@ else if (path.charAt(i) > ' ' && path.charAt(i) != 127) { 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 @@ -588,7 +604,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)); diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/resource/ResourceWebHandlerTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/resource/ResourceWebHandlerTests.java index c213b1672ac4..a8e714539e48 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/resource/ResourceWebHandlerTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/resource/ResourceWebHandlerTests.java @@ -319,7 +319,6 @@ public 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, ResourceWebHandler handler) { @@ -359,7 +358,6 @@ private void testResolvePathWithTraversal(HttpMethod httpMethod) throws Exceptio testResolvePathWithTraversal(httpMethod, "/url:" + secretPath, location); testResolvePathWithTraversal(httpMethod, "////../.." + secretPath, location); testResolvePathWithTraversal(httpMethod, "/%2E%2E/testsecret/secret.txt", location); - testResolvePathWithTraversal(httpMethod, "%2F%2F%2E%2E%2F%2Ftestsecret/secret.txt", location); testResolvePathWithTraversal(httpMethod, "url:" + secretPath, location); // The following tests fail with a MalformedURLException on Windows diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/function/PathResourceLookupFunction.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/function/PathResourceLookupFunction.java index b61f0bf519a3..33a7de5a22d4 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/function/PathResourceLookupFunction.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/function/PathResourceLookupFunction.java @@ -103,7 +103,8 @@ public Optional 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) { StringBuilder sb = null; @@ -144,6 +145,21 @@ else if (path.charAt(i) > ' ' && path.charAt(i) != 127) { 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; @@ -154,7 +170,7 @@ private boolean isInvalidPath(String path) { return true; } } - return path.contains("..") && StringUtils.cleanPath(path).contains("../"); + return path.contains("../"); } private boolean isInvalidEncodedInputPath(String path) { diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java index 83eb33b24e2a..0117af6d87a9 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java @@ -646,7 +646,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) { @@ -688,6 +689,21 @@ else if (path.charAt(i) > ' ' && path.charAt(i) != 127) { 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 @@ -750,7 +766,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)); diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandlerTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandlerTests.java index 834aab2694a6..5546bcd61179 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandlerTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandlerTests.java @@ -362,7 +362,6 @@ public 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 { From 95aa9d11aabce091a419be54f38ec28f7707b876 Mon Sep 17 00:00:00 2001 From: sgayangi Date: Wed, 18 Dec 2024 11:14:22 +0530 Subject: [PATCH 2/4] Normalize static resource path early --- .../server/PathResourceLookupFunction.java | 24 ++++++++++++------- .../reactive/resource/ResourceWebHandler.java | 24 ++++++++++++------- .../function/PathResourceLookupFunction.java | 24 ++++++++++++------- .../resource/ResourceHttpRequestHandler.java | 24 ++++++++++++------- 4 files changed, 64 insertions(+), 32 deletions(-) diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/PathResourceLookupFunction.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/PathResourceLookupFunction.java index 72e20d872e87..6af7d313688b 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/PathResourceLookupFunction.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/PathResourceLookupFunction.java @@ -147,20 +147,28 @@ else if (path.charAt(i) > ' ' && path.charAt(i) != 127) { } private static String normalizePath(String path) { - if (path.contains("%")) { - try { - path = URLDecoder.decode(path, StandardCharsets.UTF_8); + String result = path; + if (result.contains("%")) { + result = decode(result); + if (result.contains("%")) { + result = decode(result); } - catch (Exception ex) { - return ""; - } - if (path.contains("../")) { - path = StringUtils.cleanPath(path); + 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; diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/resource/ResourceWebHandler.java b/spring-webflux/src/main/java/org/springframework/web/reactive/resource/ResourceWebHandler.java index 937d3f7d1982..7dc9f2e3df1a 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/resource/ResourceWebHandler.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/resource/ResourceWebHandler.java @@ -529,20 +529,28 @@ else if (path.charAt(i) > ' ' && path.charAt(i) != 127) { } private static String normalizePath(String path) { - if (path.contains("%")) { - try { - path = URLDecoder.decode(path, StandardCharsets.UTF_8); + String result = path; + if (result.contains("%")) { + result = decode(result); + if (result.contains("%")) { + result = decode(result); } - catch (Exception ex) { - return ""; - } - if (path.contains("../")) { - path = StringUtils.cleanPath(path); + 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 diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/function/PathResourceLookupFunction.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/function/PathResourceLookupFunction.java index 33a7de5a22d4..e6de5f178310 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/function/PathResourceLookupFunction.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/function/PathResourceLookupFunction.java @@ -146,20 +146,28 @@ else if (path.charAt(i) > ' ' && path.charAt(i) != 127) { } private static String normalizePath(String path) { - if (path.contains("%")) { - try { - path = URLDecoder.decode(path, StandardCharsets.UTF_8); + String result = path; + if (result.contains("%")) { + result = decode(result); + if (result.contains("%")) { + result = decode(result); } - catch (Exception ex) { - return ""; - } - if (path.contains("../")) { - path = StringUtils.cleanPath(path); + 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; diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java index 0117af6d87a9..a446f5171fea 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java @@ -690,20 +690,28 @@ else if (path.charAt(i) > ' ' && path.charAt(i) != 127) { } private static String normalizePath(String path) { - if (path.contains("%")) { - try { - path = URLDecoder.decode(path, StandardCharsets.UTF_8); + String result = path; + if (result.contains("%")) { + result = decode(result); + if (result.contains("%")) { + result = decode(result); } - catch (Exception ex) { - return ""; - } - if (path.contains("../")) { - path = StringUtils.cleanPath(path); + 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 From f1b80cccbd198cb33ed66c710ff70fac55417ef1 Mon Sep 17 00:00:00 2001 From: sgayangi Date: Wed, 18 Dec 2024 14:37:43 +0530 Subject: [PATCH 3/4] Fix build failures --- .../reactive/function/server/PathResourceLookupFunction.java | 2 +- .../web/reactive/resource/ResourceWebHandler.java | 2 +- .../web/servlet/function/PathResourceLookupFunction.java | 2 +- .../web/servlet/resource/ResourceHttpRequestHandler.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/PathResourceLookupFunction.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/PathResourceLookupFunction.java index 6af7d313688b..157ad9924d0d 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/PathResourceLookupFunction.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/PathResourceLookupFunction.java @@ -162,7 +162,7 @@ private static String normalizePath(String path) { private static String decode(String path) { try { - return URLDecoder.decode(path, StandardCharsets.UTF_8); + return URLDecoder.decode(path, "UTF-8"); } catch (Exception ex) { return ""; diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/resource/ResourceWebHandler.java b/spring-webflux/src/main/java/org/springframework/web/reactive/resource/ResourceWebHandler.java index 7dc9f2e3df1a..a6185261efd9 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/resource/ResourceWebHandler.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/resource/ResourceWebHandler.java @@ -544,7 +544,7 @@ private static String normalizePath(String path) { private static String decode(String path) { try { - return URLDecoder.decode(path, StandardCharsets.UTF_8); + return URLDecoder.decode(path, "UTF-8"); } catch (Exception ex) { return ""; diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/function/PathResourceLookupFunction.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/function/PathResourceLookupFunction.java index e6de5f178310..ba39f61aa1ca 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/function/PathResourceLookupFunction.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/function/PathResourceLookupFunction.java @@ -161,7 +161,7 @@ private static String normalizePath(String path) { private static String decode(String path) { try { - return URLDecoder.decode(path, StandardCharsets.UTF_8); + return URLDecoder.decode(path, "UTF-8"); } catch (Exception ex) { return ""; diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java index a446f5171fea..bc4634016f15 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java @@ -705,7 +705,7 @@ private static String normalizePath(String path) { private static String decode(String path) { try { - return URLDecoder.decode(path, StandardCharsets.UTF_8); + return URLDecoder.decode(path, "UTF-8"); } catch (Exception ex) { return ""; From 131fa1237931cf81803566a9fdbeb9bda6baee2d Mon Sep 17 00:00:00 2001 From: sgayangi Date: Wed, 18 Dec 2024 14:38:00 +0530 Subject: [PATCH 4/4] Bump version to 5.3.39-wso2v4 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index ac7b72a5e295..d030ca65435a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -version=5.3.39-wso2v3 +version=5.3.39-wso2v4 org.gradle.jvmargs=-Xmx2048m org.gradle.caching=true org.gradle.parallel=true