Skip to content

Commit 0015fd6

Browse files
stsypanovjhoeller
authored andcommitted
Improve URI/query strings sanitization
1 parent b077e4c commit 0015fd6

File tree

6 files changed

+47
-30
lines changed

6 files changed

+47
-30
lines changed

spring-jdbc/src/main/java/org/springframework/jdbc/core/namedparam/NamedParameterUtils.java

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -83,7 +83,7 @@ public static ParsedSql parseSqlStatement(final String sql) {
8383
Assert.notNull(sql, "SQL must not be null");
8484

8585
Set<String> namedParameters = new HashSet<>();
86-
String sqlToUse = sql;
86+
StringBuilder sqlToUse = new StringBuilder(sql);
8787
List<ParameterHolder> parameterList = new ArrayList<>();
8888

8989
char[] statement = sql.toCharArray();
@@ -155,7 +155,7 @@ public static ParsedSql parseSqlStatement(final String sql) {
155155
int j = i + 1;
156156
if (j < statement.length && statement[j] == ':') {
157157
// escaped ":" should be skipped
158-
sqlToUse = sqlToUse.substring(0, i - escapes) + sqlToUse.substring(i - escapes + 1);
158+
sqlToUse.deleteCharAt(i - escapes);
159159
escapes++;
160160
i = i + 2;
161161
continue;
@@ -174,7 +174,7 @@ public static ParsedSql parseSqlStatement(final String sql) {
174174
}
175175
i++;
176176
}
177-
ParsedSql parsedSql = new ParsedSql(sqlToUse);
177+
ParsedSql parsedSql = new ParsedSql(sqlToUse.toString());
178178
for (ParameterHolder ph : parameterList) {
179179
parsedSql.addNamedParameter(ph.getParameterName(), ph.getStartIndex(), ph.getEndIndex());
180180
}

spring-r2dbc/src/main/java/org/springframework/r2dbc/core/NamedParameterUtils.java

+3-4
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ public static ParsedSql parseSqlStatement(String sql) {
9595
Assert.notNull(sql, "SQL must not be null");
9696

9797
Set<String> namedParameters = new HashSet<>();
98-
String sqlToUse = sql;
98+
StringBuilder sqlToUse = new StringBuilder(sql);
9999
List<ParameterHolder> parameterList = new ArrayList<>();
100100

101101
char[] statement = sql.toCharArray();
@@ -171,8 +171,7 @@ public static ParsedSql parseSqlStatement(String sql) {
171171
int j = i + 1;
172172
if (j < statement.length && statement[j] == ':') {
173173
// escaped ":" should be skipped
174-
sqlToUse = sqlToUse.substring(0, i - escapes)
175-
+ sqlToUse.substring(i - escapes + 1);
174+
sqlToUse.deleteCharAt(i - escapes);
176175
escapes++;
177176
i = i + 2;
178177
continue;
@@ -181,7 +180,7 @@ public static ParsedSql parseSqlStatement(String sql) {
181180
}
182181
i++;
183182
}
184-
ParsedSql parsedSql = new ParsedSql(sqlToUse);
183+
ParsedSql parsedSql = new ParsedSql(sqlToUse.toString());
185184
for (ParameterHolder ph : parameterList) {
186185
parsedSql.addNamedParameter(ph.getParameterName(), ph.getStartIndex(), ph.getEndIndex());
187186
}

spring-web/src/main/java/org/springframework/web/util/UriComponentsBuilder.java

+13-7
Original file line numberDiff line numberDiff line change
@@ -1023,15 +1023,21 @@ public PathComponent build() {
10231023
if (this.path.length() == 0) {
10241024
return null;
10251025
}
1026-
String path = this.path.toString();
1027-
while (true) {
1028-
int index = path.indexOf("//");
1029-
if (index == -1) {
1030-
break;
1026+
String sanitized = getSanitizedPath(this.path);
1027+
return new HierarchicalUriComponents.FullPathComponent(sanitized);
1028+
}
1029+
1030+
private static String getSanitizedPath(final StringBuilder path) {
1031+
int index = path.indexOf("//");
1032+
if (index >= 0) {
1033+
StringBuilder sanitized = new StringBuilder(path);
1034+
while (index != -1) {
1035+
sanitized.deleteCharAt(index);
1036+
index = sanitized.indexOf("//", index);
10311037
}
1032-
path = path.substring(0, index) + path.substring(index + 1);
1038+
return sanitized.toString();
10331039
}
1034-
return new HierarchicalUriComponents.FullPathComponent(path);
1040+
return path.toString();
10351041
}
10361042

10371043
public void removeTrailingSlash() {

spring-web/src/main/java/org/springframework/web/util/UrlPathHelper.java

+19-15
Original file line numberDiff line numberDiff line change
@@ -401,18 +401,17 @@ else if (index1 == requestUri.length()) {
401401
* <li>replace all "//" by "/"</li>
402402
* </ul>
403403
*/
404-
private String getSanitizedPath(final String path) {
405-
String sanitized = path;
406-
while (true) {
407-
int index = sanitized.indexOf("//");
408-
if (index < 0) {
409-
break;
410-
}
411-
else {
412-
sanitized = sanitized.substring(0, index) + sanitized.substring(index + 1);
404+
private static String getSanitizedPath(final String path) {
405+
int index = path.indexOf("//");
406+
if (index >= 0) {
407+
StringBuilder sanitized = new StringBuilder(path);
408+
while (index != -1) {
409+
sanitized.deleteCharAt(index);
410+
index = sanitized.indexOf("//", index);
413411
}
412+
return sanitized.toString();
414413
}
415-
return sanitized;
414+
return path;
416415
}
417416

418417
/**
@@ -612,15 +611,20 @@ public String removeSemicolonContent(String requestUri) {
612611
removeSemicolonContentInternal(requestUri) : removeJsessionid(requestUri));
613612
}
614613

615-
private String removeSemicolonContentInternal(String requestUri) {
614+
private static String removeSemicolonContentInternal(String requestUri) {
616615
int semicolonIndex = requestUri.indexOf(';');
616+
if (semicolonIndex == -1) {
617+
return requestUri;
618+
}
619+
StringBuilder sb = new StringBuilder(requestUri);
617620
while (semicolonIndex != -1) {
618621
int slashIndex = requestUri.indexOf('/', semicolonIndex);
619-
String start = requestUri.substring(0, semicolonIndex);
620-
requestUri = (slashIndex != -1) ? start + requestUri.substring(slashIndex) : start;
621-
semicolonIndex = requestUri.indexOf(';', semicolonIndex);
622+
if (slashIndex >= 0) {
623+
sb.delete(semicolonIndex, slashIndex);
624+
}
625+
semicolonIndex = sb.indexOf(";", semicolonIndex);
622626
}
623-
return requestUri;
627+
return sb.toString();
624628
}
625629

626630
private String removeJsessionid(String requestUri) {

spring-web/src/test/java/org/springframework/web/util/UriComponentsBuilderTests.java

+5
Original file line numberDiff line numberDiff line change
@@ -1200,4 +1200,9 @@ void toUriStringWithCurlyBraces() {
12001200
assertThat(UriComponentsBuilder.fromUriString("/path?q={asa}asa").toUriString()).isEqualTo("/path?q=%7Basa%7Dasa");
12011201
}
12021202

1203+
@Test
1204+
void verifyDoubleSlashReplacedWithSingleOne() {
1205+
String path = UriComponentsBuilder.fromPath("/home/").path("/path").build().getPath();
1206+
assertThat(path).isEqualTo("/home/path");
1207+
}
12031208
}

spring-web/src/test/java/org/springframework/web/util/UrlPathHelperTests.java

+3
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,9 @@ public void getRequestUri() {
104104

105105
request.setRequestURI("/foo+bar");
106106
assertThat(helper.getRequestUri(request)).isEqualTo("/foo+bar");
107+
108+
request.setRequestURI("/home/" + "/path");
109+
assertThat(helper.getRequestUri(request)).isEqualTo("/home/path");
107110
}
108111

109112
@Test

0 commit comments

Comments
 (0)