Skip to content

Commit 0270f00

Browse files
rstoyanchevcesarhernandezgt
authored andcommitted
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 spring-projectsgh-33689 (cherry picked from commit 3bfbe30)
1 parent 302edca commit 0270f00

File tree

6 files changed

+75
-15
lines changed

6 files changed

+75
-15
lines changed

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

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,8 @@ public Mono<Resource> apply(ServerRequest request) {
105105
protected String processPath(String path) {
106106
path = StringUtils.replace(path, "\\", "/");
107107
path = cleanDuplicateSlashes(path);
108-
return cleanLeadingSlash(path);
108+
path = cleanLeadingSlash(path);
109+
return normalizePath(path);
109110
}
110111

111112
private String cleanDuplicateSlashes(String path) {
@@ -147,6 +148,21 @@ else if (path.charAt(i) > ' ' && path.charAt(i) != 127) {
147148
return (slash ? "/" : "");
148149
}
149150

151+
private static String normalizePath(String path) {
152+
if (path.contains("%")) {
153+
try {
154+
path = URLDecoder.decode(path, "UTF-8");
155+
}
156+
catch (Exception ex) {
157+
return "";
158+
}
159+
if (path.contains("../")) {
160+
path = StringUtils.cleanPath(path);
161+
}
162+
}
163+
return path;
164+
}
165+
150166
private boolean isInvalidPath(String path) {
151167
if (path.contains("WEB-INF") || path.contains("META-INF")) {
152168
return true;
@@ -157,10 +173,7 @@ private boolean isInvalidPath(String path) {
157173
return true;
158174
}
159175
}
160-
if (path.contains("..") && StringUtils.cleanPath(path).contains("../")) {
161-
return true;
162-
}
163-
return false;
176+
return path.contains("../");
164177
}
165178

166179
/**

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

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -485,7 +485,8 @@ protected Mono<Resource> getResource(ServerWebExchange exchange) {
485485
protected String processPath(String path) {
486486
path = StringUtils.replace(path, "\\", "/");
487487
path = cleanDuplicateSlashes(path);
488-
return cleanLeadingSlash(path);
488+
path = cleanLeadingSlash(path);
489+
return normalizePath(path);
489490
}
490491

491492
private String cleanDuplicateSlashes(String path) {
@@ -527,6 +528,21 @@ else if (path.charAt(i) > ' ' && path.charAt(i) != 127) {
527528
return (slash ? "/" : "");
528529
}
529530

531+
private static String normalizePath(String path) {
532+
if (path.contains("%")) {
533+
try {
534+
path = URLDecoder.decode(path, "UTF-8");
535+
}
536+
catch (Exception ex) {
537+
return "";
538+
}
539+
if (path.contains("../")) {
540+
path = StringUtils.cleanPath(path);
541+
}
542+
}
543+
return path;
544+
}
545+
530546
/**
531547
* Check whether the given path contains invalid escape sequences.
532548
* @param path the path to validate
@@ -588,7 +604,7 @@ protected boolean isInvalidPath(String path) {
588604
return true;
589605
}
590606
}
591-
if (path.contains("..") && StringUtils.cleanPath(path).contains("../")) {
607+
if (path.contains("../")) {
592608
if (logger.isWarnEnabled()) {
593609
logger.warn(LogFormatUtils.formatValue(
594610
"Path contains \"../\" after call to StringUtils#cleanPath: [" + path + "]", -1, true));

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,6 @@ public void testInvalidPath() throws Exception {
319319
testInvalidPath("/../.." + secretPath, handler);
320320
testInvalidPath("/%2E%2E/testsecret/secret.txt", handler);
321321
testInvalidPath("/%2E%2E/testsecret/secret.txt", handler);
322-
testInvalidPath("%2F%2F%2E%2E%2F%2F%2E%2E" + secretPath, handler);
323322
}
324323

325324
private void testInvalidPath(String requestPath, ResourceWebHandler handler) {
@@ -359,7 +358,6 @@ private void testResolvePathWithTraversal(HttpMethod httpMethod) throws Exceptio
359358
testResolvePathWithTraversal(httpMethod, "/url:" + secretPath, location);
360359
testResolvePathWithTraversal(httpMethod, "////../.." + secretPath, location);
361360
testResolvePathWithTraversal(httpMethod, "/%2E%2E/testsecret/secret.txt", location);
362-
testResolvePathWithTraversal(httpMethod, "%2F%2F%2E%2E%2F%2Ftestsecret/secret.txt", location);
363361
testResolvePathWithTraversal(httpMethod, "url:" + secretPath, location);
364362

365363
// The following tests fail with a MalformedURLException on Windows

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

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,8 @@ public Optional<Resource> apply(ServerRequest request) {
106106
protected String processPath(String path) {
107107
path = StringUtils.replace(path, "\\", "/");
108108
path = cleanDuplicateSlashes(path);
109-
return cleanLeadingSlash(path);
109+
path = cleanLeadingSlash(path);
110+
return normalizePath(path);
110111
}
111112

112113
private String cleanDuplicateSlashes(String path) {
@@ -148,6 +149,21 @@ else if (path.charAt(i) > ' ' && path.charAt(i) != 127) {
148149
return (slash ? "/" : "");
149150
}
150151

152+
private static String normalizePath(String path) {
153+
if (path.contains("%")) {
154+
try {
155+
path = URLDecoder.decode(path, "UTF-8");
156+
}
157+
catch (Exception ex) {
158+
return "";
159+
}
160+
if (path.contains("../")) {
161+
path = StringUtils.cleanPath(path);
162+
}
163+
}
164+
return path;
165+
}
166+
151167
private boolean isInvalidPath(String path) {
152168
if (path.contains("WEB-INF") || path.contains("META-INF")) {
153169
return true;
@@ -158,7 +174,7 @@ private boolean isInvalidPath(String path) {
158174
return true;
159175
}
160176
}
161-
return path.contains("..") && StringUtils.cleanPath(path).contains("../");
177+
return path.contains("../");
162178
}
163179

164180
private boolean isInvalidEncodedInputPath(String path) {

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

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -646,7 +646,8 @@ protected Resource getResource(HttpServletRequest request) throws IOException {
646646
protected String processPath(String path) {
647647
path = StringUtils.replace(path, "\\", "/");
648648
path = cleanDuplicateSlashes(path);
649-
return cleanLeadingSlash(path);
649+
path = cleanLeadingSlash(path);
650+
return normalizePath(path);
650651
}
651652

652653
private String cleanDuplicateSlashes(String path) {
@@ -688,6 +689,21 @@ else if (path.charAt(i) > ' ' && path.charAt(i) != 127) {
688689
return (slash ? "/" : "");
689690
}
690691

692+
private static String normalizePath(String path) {
693+
if (path.contains("%")) {
694+
try {
695+
path = URLDecoder.decode(path, "UTF-8");
696+
}
697+
catch (Exception ex) {
698+
return "";
699+
}
700+
if (path.contains("../")) {
701+
path = StringUtils.cleanPath(path);
702+
}
703+
}
704+
return path;
705+
}
706+
691707
/**
692708
* Check whether the given path contains invalid escape sequences.
693709
* @param path the path to validate
@@ -750,7 +766,7 @@ protected boolean isInvalidPath(String path) {
750766
return true;
751767
}
752768
}
753-
if (path.contains("..") && StringUtils.cleanPath(path).contains("../")) {
769+
if (path.contains("../")) {
754770
if (logger.isWarnEnabled()) {
755771
logger.warn(LogFormatUtils.formatValue(
756772
"Path contains \"../\" after call to StringUtils#cleanPath: [" + path + "]", -1, true));

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,6 @@ public void testInvalidPath() throws Exception {
362362
testInvalidPath("/../.." + secretPath, handler);
363363
testInvalidPath("/%2E%2E/testsecret/secret.txt", handler);
364364
testInvalidPath("/%2E%2E/testsecret/secret.txt", handler);
365-
testInvalidPath("%2F%2F%2E%2E%2F%2F%2E%2E" + secretPath, handler);
366365
}
367366

368367
private void testInvalidPath(String requestPath, ResourceHttpRequestHandler handler) throws Exception {
@@ -742,6 +741,8 @@ public void ignoreLastModified() throws Exception {
742741
assertThat(this.response.getContentAsString()).isEqualTo("h1 { color:red; }");
743742
}
744743

744+
745+
745746
@Test
746747
public void servletContextRootValidation() {
747748
StaticWebApplicationContext context = new StaticWebApplicationContext() {
@@ -762,7 +763,7 @@ public Resource getResource(String location) {
762763
}
763764

764765

765-
private long resourceLastModified(String resourceName) throws IOException {
766+
private long resourceLastModified(String resourceName) throws IOException {
766767
return new ClassPathResource(resourceName, getClass()).getFile().lastModified();
767768
}
768769

0 commit comments

Comments
 (0)