High severity7.5GHSA Advisory· Published Dec 19, 2024· Updated Apr 15, 2026
CVE-2024-38819
CVE-2024-38819
Description
Applications serving static resources through the functional web frameworks WebMvc.fn or WebFlux.fn are vulnerable to path traversal attacks. An attacker can craft malicious HTTP requests and obtain any file on the file system that is also accessible to the process in which the Spring application is running.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
org.springframework:spring-webfluxMaven | >= 6.1.0, < 6.1.14 | 6.1.14 |
org.springframework:spring-webmvcMaven | >= 6.1.0, < 6.1.14 | 6.1.14 |
org.springframework:spring-webfluxMaven | <= 5.3.39 | — |
org.springframework:spring-webmvcMaven | <= 5.3.39 | — |
org.springframework:spring-webfluxMaven | >= 6.0.0, <= 6.0.23 | — |
org.springframework:spring-webmvcMaven | >= 6.0.0, <= 6.0.23 | — |
Affected products
1- Range: >= 6.0.0, <= 6.0.23
Patches
2fb7890d73975Update processPath for double encoding
4 files changed · +64 −32
spring-webflux/src/main/java/org/springframework/web/reactive/function/server/PathResourceLookupFunction.java+16 −8 modified@@ -148,20 +148,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;
spring-webflux/src/main/java/org/springframework/web/reactive/resource/ResourceWebHandler.java+16 −8 modified@@ -567,20 +567,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
spring-webmvc/src/main/java/org/springframework/web/servlet/function/PathResourceLookupFunction.java+16 −8 modified@@ -149,20 +149,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;
spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java+16 −8 modified@@ -726,20 +726,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
3bfbe30a7814Normalize static resource path early
6 files changed · +72 −14
spring-webflux/src/main/java/org/springframework/web/reactive/function/server/PathResourceLookupFunction.java+18 −5 modified@@ -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) { @@ -146,6 +147,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; @@ -156,10 +172,7 @@ private boolean isInvalidPath(String path) { return true; } } - if (path.contains("..") && StringUtils.cleanPath(path).contains("../")) { - return true; - } - return false; + return path.contains("../"); } /**
spring-webflux/src/main/java/org/springframework/web/reactive/resource/ResourceWebHandler.java+18 −2 modified@@ -523,7 +523,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) { @@ -565,6 +566,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 @@ -623,7 +639,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));
spring-webflux/src/test/java/org/springframework/web/reactive/resource/ResourceWebHandlerTests.java+0 −2 modified@@ -670,7 +670,6 @@ void shouldRejectInvalidPath() 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) { @@ -705,7 +704,6 @@ void resolvePathWithTraversal(HttpMethod method) throws Exception { 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
spring-webmvc/src/main/java/org/springframework/web/servlet/function/PathResourceLookupFunction.java+18 −2 modified@@ -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) { @@ -147,6 +148,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; @@ -157,7 +173,7 @@ private boolean isInvalidPath(String path) { return true; } } - return path.contains("..") && StringUtils.cleanPath(path).contains("../"); + return path.contains("../"); } private boolean isInvalidEncodedInputPath(String path) {
spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java+18 −2 modified@@ -682,7 +682,8 @@ private static String getPath(HttpServletRequest 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) { @@ -724,6 +725,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 @@ -783,7 +799,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));
spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandlerTests.java+0 −1 modified@@ -656,7 +656,6 @@ void shouldRejectInvalidPath() throws Exception { 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) {
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
8- github.com/advisories/GHSA-g5vr-rgqm-vf78ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2024-38819ghsaADVISORY
- github.com/spring-projects/spring-framework/commit/3bfbe30a7814c9ea1556d40df9bd87ddb3ba372dghsaWEB
- github.com/spring-projects/spring-framework/commit/fb7890d73975a3d9e0763e0926df2bd0a608e87eghsaWEB
- github.com/spring-projects/spring-framework/issues/33689ghsaWEB
- security.netapp.com/advisory/ntap-20250110-0010ghsaWEB
- spring.io/security/cve-2024-38819nvdWEB
- security.netapp.com/advisory/ntap-20250110-0010/nvd
News mentions
0No linked articles in our index yet.