diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/jar/JarUrl.java b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/jar/JarUrl.java index 1e40ced32f1f..ca2230d36d7b 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/jar/JarUrl.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/jar/JarUrl.java @@ -79,7 +79,7 @@ public static URL create(File file, String nestedEntryName, String path) { } private static String getJarReference(File file, String nestedEntryName) { - String jarFilePath = file.toURI().getPath(); + String jarFilePath = file.toURI().getRawPath().replace("!", "%21"); return (nestedEntryName != null) ? "nested:" + jarFilePath + "/!" + nestedEntryName : "file:" + jarFilePath; } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/net/protocol/jar/JarUrlConnectionTests.java b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/net/protocol/jar/JarUrlConnectionTests.java index c9553c731379..83f522553332 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/net/protocol/jar/JarUrlConnectionTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/net/protocol/jar/JarUrlConnectionTests.java @@ -512,6 +512,20 @@ void getLastModifiedHeaderReturnsFileModifiedTime() throws IOException { } } + @Test + void getJarFileWhenInFolderWithEncodedCharsReturnsJarFile() throws Exception { + this.temp = new File(this.temp, "te#st"); + this.temp.mkdirs(); + this.file = new File(this.temp, "test.jar"); + this.url = JarUrl.create(this.file, "nested.jar"); + assertThat(this.url.toString()).contains("te%23st"); + TestJar.create(this.file); + JarUrlConnection connection = JarUrlConnection.open(this.url); + JarFile jarFile = connection.getJarFile(); + assertThat(jarFile).isNotNull(); + assertThat(jarFile.getEntry("3.dat")).isNotNull(); + } + private long withoutNanos(long epochMilli) { return Instant.ofEpochMilli(epochMilli).with(ChronoField.NANO_OF_SECOND, 0).toEpochMilli(); } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/net/protocol/jar/JarUrlTests.java b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/net/protocol/jar/JarUrlTests.java index e35db7f04f0e..7ca3fb683995 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/net/protocol/jar/JarUrlTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/net/protocol/jar/JarUrlTests.java @@ -25,6 +25,8 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; +import org.springframework.boot.loader.net.util.UrlDecoder; + import static org.assertj.core.api.Assertions.assertThat; /** @@ -84,4 +86,14 @@ void createWithFileNameAndPathReturnsUrl() { assertThat(url).hasToString("jar:nested:%s/!lib.jar!/com/example/My.class".formatted(this.jarFileUrlPath)); } + @Test + void createWithReservedCharsInName() throws Exception { + String badFolderName = "foo#bar!/baz/!oof"; + this.temp = new File(this.temp, badFolderName); + setup(); + URL url = JarUrl.create(this.jarFile, "lib.jar", "com/example/My.class"); + assertThat(url).hasToString("jar:nested:%s/!lib.jar!/com/example/My.class".formatted(this.jarFileUrlPath)); + assertThat(UrlDecoder.decode(url.toString())).contains(badFolderName); + } + }