Merge branch '4.0.x'

Closes gh-50510
This commit is contained in:
Moritz Halbritter
2026-05-27 10:47:58 +02:00
2 changed files with 47 additions and 6 deletions
@@ -26,6 +26,7 @@ import java.io.OutputStream;
import java.io.PrintStream;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributeView;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileTime;
@@ -57,6 +58,7 @@ import org.springframework.util.StringUtils;
* The {@code 'extract'} tools command.
*
* @author Moritz Halbritter
* @author Dongliang Xie
*/
class ExtractCommand extends Command {
@@ -363,14 +365,18 @@ class ExtractCommand extends Command {
}
private static File assertFileIsContainedInDirectory(File directory, File file, String name) throws IOException {
String canonicalOutputPath = directory.getCanonicalPath() + File.separator;
String canonicalEntryPath = file.getCanonicalPath();
Assert.state(canonicalEntryPath.startsWith(canonicalOutputPath),
Path canonicalOutputPath = directory.getCanonicalFile().toPath();
Path canonicalEntryPath = file.getCanonicalFile().toPath();
Assert.state(isFileContainedInDirectory(canonicalOutputPath, canonicalEntryPath),
() -> "Entry '%s' would be written to '%s'. This is outside the output location of '%s'. Verify the contents of your archive."
.formatted(name, canonicalEntryPath, canonicalOutputPath));
return file;
}
private static boolean isFileContainedInDirectory(Path directory, Path file) {
return !file.equals(directory) && file.startsWith(directory);
}
@FunctionalInterface
private interface EntryNameTransformer {
@@ -515,9 +521,9 @@ class ExtractCommand extends Command {
}
private File assertLayerDirectoryLocation(File layerDirectory, String layerName) throws IOException {
String canonicalOutputPath = this.directory.getCanonicalPath() + File.separator;
String canonicalLayerPath = layerDirectory.getCanonicalPath();
Assert.state(canonicalLayerPath.startsWith(canonicalOutputPath),
Path canonicalOutputPath = this.directory.getCanonicalFile().toPath();
Path canonicalLayerPath = layerDirectory.getCanonicalFile().toPath();
Assert.state(isFileContainedInDirectory(canonicalOutputPath, canonicalLayerPath),
() -> "Layer '%s' would be written to '%s'. This is outside the output location of '%s'. Verify the contents of your archive."
.formatted(layerName, canonicalLayerPath, canonicalOutputPath));
return layerDirectory;
@@ -20,11 +20,13 @@ import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributeView;
import java.nio.file.attribute.BasicFileAttributes;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.jar.Manifest;
@@ -45,6 +47,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
* Tests for {@link ExtractCommand}.
*
* @author Moritz Halbritter
* @author Dongliang Xie
*/
class ExtractCommandTests extends AbstractJarModeTests {
@@ -370,6 +373,38 @@ class ExtractCommandTests extends AbstractJarModeTests {
.doesNotContain("test/spring-boot-loader/org/springframework/boot/loader/launch/JarLauncher.class");
}
@Test
void extractWhenDestinationIsFileSystemRoot() throws IOException {
Path layerDirectory = ExtractCommandTests.this.tempDir.toPath()
.resolve("root-output")
.resolve("dependencies")
.toAbsolutePath()
.normalize();
Path outputRoot = layerDirectory.getRoot();
String layerName = outputRoot.relativize(layerDirectory).toString().replace(File.separatorChar, '/');
Layers layers = new Layers() {
@Override
public Iterator<String> iterator() {
return List.of(layerName).iterator();
}
@Override
public String getLayer(String entryName) {
return layerName;
}
@Override
public String getApplicationLayerName() {
return layerName;
}
};
runCommand((context) -> new ExtractCommand(context, layers), ExtractCommandTests.this.archive,
"--destination", outputRoot.toString(), "--force", "--launcher", "--layers");
assertThat(layerDirectory.resolve("BOOT-INF/lib/dependency-1.jar")).exists();
}
}
}