From 089271c1ba3c0f7d5002a05fe183e6aaa9f342bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=87=E1=85=A1=E1=86=A8=E1=84=89=E1=85=A1=E1=86=AB?= =?UTF-8?q?=E1=84=92=E1=85=B4/=E1=84=83=E1=85=A6=E1=84=8B=E1=85=B5?= =?UTF-8?q?=E1=84=90=E1=85=A5=E1=84=91=E1=85=B3=E1=84=85=E1=85=A9=E1=84=83?= =?UTF-8?q?=E1=85=A5=E1=86=A8=E1=84=90=E1=85=B3=E1=84=90=E1=85=B5=E1=86=B7?= Date: Thu, 28 Nov 2024 00:11:31 +0900 Subject: [PATCH 1/4] fix: Ensure protocol slashes are preserved in `getSanitizedPath` Fixed an issue where `getSanitizedPath` incorrectly removed slashes in protocols like `http://`. --- .../web/util/UriComponentsBuilder.java | 22 +++++++----- .../web/util/UriComponentsBuilderTests.java | 35 +++++++++++++++++++ 2 files changed, 48 insertions(+), 9 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/web/util/UriComponentsBuilder.java b/spring-web/src/main/java/org/springframework/web/util/UriComponentsBuilder.java index f436bc2e96e3..feee146a02d7 100644 --- a/spring-web/src/main/java/org/springframework/web/util/UriComponentsBuilder.java +++ b/spring-web/src/main/java/org/springframework/web/util/UriComponentsBuilder.java @@ -908,16 +908,20 @@ public PathComponent build() { } private static String getSanitizedPath(final StringBuilder path) { - int index = path.indexOf("//"); - if (index >= 0) { - StringBuilder sanitized = new StringBuilder(path); - while (index != -1) { - sanitized.deleteCharAt(index); - index = sanitized.indexOf("//", index); - } - return sanitized.toString(); + String pathString = path.toString(); + int protocolIndex = pathString.indexOf("://"); + boolean hasProtocol = protocolIndex != -1; + + if (hasProtocol) { + String protocol = path.substring(0, protocolIndex + 3); + String restOfPath = path.substring(protocolIndex + 3); + + String sanitizedRestPath = restOfPath.replaceAll("/{2,}", "/"); + + return protocol + sanitizedRestPath; } - return path.toString(); + + return pathString.replaceAll("/{2,}", "/"); } public void removeTrailingSlash() { diff --git a/spring-web/src/test/java/org/springframework/web/util/UriComponentsBuilderTests.java b/spring-web/src/test/java/org/springframework/web/util/UriComponentsBuilderTests.java index ce22e8e4c885..da83f7f15386 100644 --- a/spring-web/src/test/java/org/springframework/web/util/UriComponentsBuilderTests.java +++ b/spring-web/src/test/java/org/springframework/web/util/UriComponentsBuilderTests.java @@ -25,11 +25,14 @@ import java.util.Map; import java.util.Optional; import java.util.function.BiConsumer; +import java.util.stream.Stream; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.EnumSource; +import org.junit.jupiter.params.provider.MethodSource; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.web.util.UriComponentsBuilder.ParserType; @@ -890,4 +893,36 @@ void expandPortAndPathWithoutSeparator(ParserType parserType) { assertThat(uri.toString()).isEqualTo("ws://localhost:7777/test"); } + @ParameterizedTest + @MethodSource("providePathsForSanitization") + void verifySanitizedPath(StringBuilder inputPath, String expected) { + assertThat(invokeGetSanitizedPath(inputPath)).isEqualTo(expected); + } + + private static Stream providePathsForSanitization() { + return Stream.of( + Arguments.of(new StringBuilder("http://example.com//path/to//resource"), "http://example.com/path/to/resource"), + Arguments.of(new StringBuilder("https://example.com//path"), "https://example.com/path"), + Arguments.of(new StringBuilder("example.com//path"), "example.com/path"), + Arguments.of(new StringBuilder("example.com//////////////path//to"), "example.com/path/to") + ); + } + + private String invokeGetSanitizedPath(StringBuilder path) { + try { + Class outerClass = Class.forName("org.springframework.web.util.UriComponentsBuilder"); + + Class innerClass = Arrays.stream(outerClass.getDeclaredClasses()) + .filter(clazz -> clazz.getSimpleName().equals("FullPathComponentBuilder")) + .findFirst() + .orElseThrow(() -> new ClassNotFoundException("FullPathComponentBuilder not found")); + + var method = innerClass.getDeclaredMethod("getSanitizedPath", StringBuilder.class); + method.setAccessible(true); + return (String) method.invoke(null, path); + } catch (Exception e) { + throw new RuntimeException("Failed to invoke getSanitizedPath", e); + } + } + } From ebd25d0668abbda2a6ea0a6f65d5606d381633a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=87=E1=85=A1=E1=86=A8=E1=84=89=E1=85=A1=E1=86=AB?= =?UTF-8?q?=E1=84=92=E1=85=B4/=E1=84=83=E1=85=A6=E1=84=8B=E1=85=B5?= =?UTF-8?q?=E1=84=90=E1=85=A5=E1=84=91=E1=85=B3=E1=84=85=E1=85=A9=E1=84=83?= =?UTF-8?q?=E1=85=A5=E1=86=A8=E1=84=90=E1=85=B3=E1=84=90=E1=85=B5=E1=86=B7?= Date: Thu, 28 Nov 2024 00:57:35 +0900 Subject: [PATCH 2/4] test: Replaced `var` with `Method` type in `invokeGetSanitizedPath` test utility method --- .../springframework/web/util/UriComponentsBuilderTests.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spring-web/src/test/java/org/springframework/web/util/UriComponentsBuilderTests.java b/spring-web/src/test/java/org/springframework/web/util/UriComponentsBuilderTests.java index da83f7f15386..afb11c51ae89 100644 --- a/spring-web/src/test/java/org/springframework/web/util/UriComponentsBuilderTests.java +++ b/spring-web/src/test/java/org/springframework/web/util/UriComponentsBuilderTests.java @@ -16,6 +16,7 @@ package org.springframework.web.util; +import java.lang.reflect.Method; import java.net.URI; import java.util.Arrays; import java.util.Collection; @@ -917,7 +918,7 @@ private String invokeGetSanitizedPath(StringBuilder path) { .findFirst() .orElseThrow(() -> new ClassNotFoundException("FullPathComponentBuilder not found")); - var method = innerClass.getDeclaredMethod("getSanitizedPath", StringBuilder.class); + Method method = innerClass.getDeclaredMethod("getSanitizedPath", StringBuilder.class); method.setAccessible(true); return (String) method.invoke(null, path); } catch (Exception e) { From 08c8d4956b910feaec388ded85d28f4e7e952001 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=87=E1=85=A1=E1=86=A8=E1=84=89=E1=85=A1=E1=86=AB?= =?UTF-8?q?=E1=84=92=E1=85=B4/=E1=84=83=E1=85=A6=E1=84=8B=E1=85=B5?= =?UTF-8?q?=E1=84=90=E1=85=A5=E1=84=91=E1=85=B3=E1=84=85=E1=85=A9=E1=84=83?= =?UTF-8?q?=E1=85=A5=E1=86=A8=E1=84=90=E1=85=B3=E1=84=90=E1=85=B5=E1=86=B7?= Date: Fri, 29 Nov 2024 10:54:16 +0900 Subject: [PATCH 3/4] style: fix Checkstyle issues in UriComponentsBuilderTests.java --- .../springframework/web/util/UriComponentsBuilderTests.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/spring-web/src/test/java/org/springframework/web/util/UriComponentsBuilderTests.java b/spring-web/src/test/java/org/springframework/web/util/UriComponentsBuilderTests.java index afb11c51ae89..0fbf25e2b8a8 100644 --- a/spring-web/src/test/java/org/springframework/web/util/UriComponentsBuilderTests.java +++ b/spring-web/src/test/java/org/springframework/web/util/UriComponentsBuilderTests.java @@ -32,8 +32,8 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.EnumSource; - import org.junit.jupiter.params.provider.MethodSource; + import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.web.util.UriComponentsBuilder.ParserType; @@ -921,7 +921,8 @@ private String invokeGetSanitizedPath(StringBuilder path) { Method method = innerClass.getDeclaredMethod("getSanitizedPath", StringBuilder.class); method.setAccessible(true); return (String) method.invoke(null, path); - } catch (Exception e) { + } + catch (Exception e) { throw new RuntimeException("Failed to invoke getSanitizedPath", e); } } From 8964223f9d7c9bab0807ff598feeaf13e6eb5bae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=87=E1=85=A1=E1=86=A8=E1=84=89=E1=85=A1=E1=86=AB?= =?UTF-8?q?=E1=84=92=E1=85=B4/=E1=84=83=E1=85=A6=E1=84=8B=E1=85=B5?= =?UTF-8?q?=E1=84=90=E1=85=A5=E1=84=91=E1=85=B3=E1=84=85=E1=85=A9=E1=84=83?= =?UTF-8?q?=E1=85=A5=E1=86=A8=E1=84=90=E1=85=B3=E1=84=90=E1=85=B5=E1=86=B7?= Date: Fri, 29 Nov 2024 12:25:12 +0900 Subject: [PATCH 4/4] style: fix Checkstyle issues in UriComponentsBuilderTests.java --- .../org/springframework/web/util/UriComponentsBuilderTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-web/src/test/java/org/springframework/web/util/UriComponentsBuilderTests.java b/spring-web/src/test/java/org/springframework/web/util/UriComponentsBuilderTests.java index 0fbf25e2b8a8..0fb56b0c4359 100644 --- a/spring-web/src/test/java/org/springframework/web/util/UriComponentsBuilderTests.java +++ b/spring-web/src/test/java/org/springframework/web/util/UriComponentsBuilderTests.java @@ -902,7 +902,7 @@ void verifySanitizedPath(StringBuilder inputPath, String expected) { private static Stream providePathsForSanitization() { return Stream.of( - Arguments.of(new StringBuilder("http://example.com//path/to//resource"), "http://example.com/path/to/resource"), + Arguments.of(new StringBuilder("https://example.com//path/to//resource"), "https://example.com/path/to/resource"), Arguments.of(new StringBuilder("https://example.com//path"), "https://example.com/path"), Arguments.of(new StringBuilder("example.com//path"), "example.com/path"), Arguments.of(new StringBuilder("example.com//////////////path//to"), "example.com/path/to")