From 06569e76f6155562bc0220fc97a8ac641e57a56d Mon Sep 17 00:00:00 2001 From: "_ext Slovak, Jiri" Date: Wed, 21 Feb 2024 22:00:23 +0100 Subject: [PATCH 1/2] Decode URL content before passing it to NestedLocation.parse URL can contains empty spaced encoded as %20, so it should be decoded before passing it to NestedLocation. NestedLocation expects file system path which should not contain URL encoded values. See gh-39675 --- .../boot/loader/net/protocol/nested/NestedUrlConnection.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/nested/NestedUrlConnection.java b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/nested/NestedUrlConnection.java index 0409fe6e3d99..3dd03491f441 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/nested/NestedUrlConnection.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/nested/NestedUrlConnection.java @@ -38,6 +38,7 @@ import java.util.Map; import java.util.Map.Entry; +import org.springframework.boot.loader.net.util.UrlDecoder; import org.springframework.boot.loader.ref.Cleaner; /** @@ -76,7 +77,7 @@ class NestedUrlConnection extends URLConnection { private NestedLocation parseNestedLocation(URL url) throws MalformedURLException { try { - return NestedLocation.parse(url.getPath()); + return NestedLocation.parse(UrlDecoder.decode(url.getPath())); } catch (IllegalArgumentException ex) { throw new MalformedURLException(ex.getMessage()); From a457638e6c764b020eeb9e7a1ecde157f0bc9462 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Wed, 21 Feb 2024 17:30:12 -0800 Subject: [PATCH 2/2] Polish 'Decode URL content before passing it to NestedLocation.parse' See gh-39675' Closes gh-39675' --- .../protocol/nested/NestedUrlConnection.java | 2 +- .../nested/NestedUrlConnectionTests.java | 31 ++++++++++++++----- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/nested/NestedUrlConnection.java b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/nested/NestedUrlConnection.java index 3dd03491f441..7daac7ee950f 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/nested/NestedUrlConnection.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/nested/NestedUrlConnection.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/net/protocol/nested/NestedUrlConnectionTests.java b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/net/protocol/nested/NestedUrlConnectionTests.java index b194610486e2..606d00c53edc 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/net/protocol/nested/NestedUrlConnectionTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/net/protocol/nested/NestedUrlConnectionTests.java @@ -121,13 +121,7 @@ void getPermissionReturnsFilePermission() throws Exception { @Test void getInputStreamReturnsContentOfNestedJar() throws Exception { NestedUrlConnection connection = new NestedUrlConnection(this.url); - try (InputStream actual = connection.getInputStream()) { - try (ZipContent zipContent = ZipContent.open(this.jarFile.toPath())) { - try (InputStream expected = zipContent.getEntry("nested.jar").openContent().asInputStream()) { - assertThat(actual).hasSameContentAs(expected); - } - } - } + assertHasSameContentAsNestedJar(connection); } @Test @@ -163,6 +157,29 @@ void getLastModifiedHeaderReturnsFileModifiedTime() throws IOException { } } + @Test + void createDecodesUrlPath() throws Exception { + File withSpace = new File(this.temp, "te st"); + withSpace.mkdirs(); + this.jarFile = new File(withSpace, "test.jar"); + TestJar.create(this.jarFile); + this.url = new URL("nested:" + this.jarFile.toURI().getRawPath() + "/!nested.jar"); + assertThat(this.url.toString()).contains("%20"); + NestedUrlConnection connection = new NestedUrlConnection(this.url); + assertHasSameContentAsNestedJar(connection); + assertThat(connection.getLastModified()).isEqualTo(this.jarFile.lastModified()); + } + + private void assertHasSameContentAsNestedJar(NestedUrlConnection connection) throws IOException { + try (InputStream actual = connection.getInputStream()) { + try (ZipContent zipContent = ZipContent.open(this.jarFile.toPath())) { + try (InputStream expected = zipContent.getEntry("nested.jar").openContent().asInputStream()) { + assertThat(actual).hasSameContentAs(expected); + } + } + } + } + private long withoutNanos(long epochMilli) { return Instant.ofEpochMilli(epochMilli).with(ChronoField.NANO_OF_SECOND, 0).toEpochMilli(); }