Commit ac258a95 authored by Andy Wilkinson's avatar Andy Wilkinson

Merge branch '2.3.x' into 2.4.x

Closes gh-25508
parents 2682b380 1ac9b3fa
/* /*
* Copyright 2012-2020 the original author or authors. * Copyright 2012-2021 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -29,7 +29,6 @@ import java.util.zip.ZipInputStream; ...@@ -29,7 +29,6 @@ import java.util.zip.ZipInputStream;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.StreamUtils; import org.springframework.util.StreamUtils;
import org.springframework.util.StringUtils;
/** /**
* The {@code 'extract'} tools command. * The {@code 'extract'} tools command.
...@@ -86,15 +85,18 @@ class ExtractCommand extends Command { ...@@ -86,15 +85,18 @@ class ExtractCommand extends Command {
} }
private void write(ZipInputStream zip, ZipEntry entry, File destination) throws IOException { private void write(ZipInputStream zip, ZipEntry entry, File destination) throws IOException {
String path = StringUtils.cleanPath(entry.getName()); String canonicalOutputPath = destination.getCanonicalPath() + File.separator;
File file = new File(destination, path); File file = new File(destination, entry.getName());
if (file.getAbsolutePath().startsWith(destination.getAbsolutePath())) { String canonicalEntryPath = file.getCanonicalPath();
mkParentDirs(file); Assert.state(canonicalEntryPath.startsWith(canonicalOutputPath),
try (OutputStream out = new FileOutputStream(file)) { () -> "Entry '" + entry.getName() + "' would be written to '" + canonicalEntryPath
StreamUtils.copy(zip, out); + "'. This is outside the output location of '" + canonicalOutputPath
} + "'. Verify the contents of your archive.");
Files.setAttribute(file.toPath(), "creationTime", entry.getCreationTime()); mkParentDirs(file);
try (OutputStream out = new FileOutputStream(file)) {
StreamUtils.copy(zip, out);
} }
Files.setAttribute(file.toPath(), "creationTime", entry.getCreationTime());
} }
private void mkParentDirs(File file) throws IOException { private void mkParentDirs(File file) throws IOException {
......
/* /*
* Copyright 2012-2020 the original author or authors. * Copyright 2012-2021 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -23,6 +23,7 @@ import java.io.IOException; ...@@ -23,6 +23,7 @@ import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.Iterator; import java.util.Iterator;
import java.util.function.Consumer;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream; import java.util.zip.ZipOutputStream;
...@@ -41,6 +42,7 @@ import static org.mockito.BDDMockito.given; ...@@ -41,6 +42,7 @@ import static org.mockito.BDDMockito.given;
* Tests for {@link ExtractCommand}. * Tests for {@link ExtractCommand}.
* *
* @author Phillip Webb * @author Phillip Webb
* @author Andy Wilkinson
*/ */
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
class ExtractCommandTests { class ExtractCommandTests {
...@@ -77,6 +79,7 @@ class ExtractCommandTests { ...@@ -77,6 +79,7 @@ class ExtractCommandTests {
assertThat(new File(this.extract, "b/b/b.jar")).exists(); assertThat(new File(this.extract, "b/b/b.jar")).exists();
assertThat(new File(this.extract, "c/c/c.jar")).exists(); assertThat(new File(this.extract, "c/c/c.jar")).exists();
assertThat(new File(this.extract, "d")).isDirectory(); assertThat(new File(this.extract, "d")).isDirectory();
assertThat(new File(this.extract.getParentFile(), "e.jar")).doesNotExist();
} }
@Test @Test
...@@ -99,6 +102,7 @@ class ExtractCommandTests { ...@@ -99,6 +102,7 @@ class ExtractCommandTests {
assertThat(this.extract.list()).containsOnly("a", "c"); assertThat(this.extract.list()).containsOnly("a", "c");
assertThat(new File(this.extract, "a/a/a.jar")).exists(); assertThat(new File(this.extract, "a/a/a.jar")).exists();
assertThat(new File(this.extract, "c/c/c.jar")).exists(); assertThat(new File(this.extract, "c/c/c.jar")).exists();
assertThat(new File(this.extract.getParentFile(), "e.jar")).doesNotExist();
} }
@Test @Test
...@@ -114,7 +118,29 @@ class ExtractCommandTests { ...@@ -114,7 +118,29 @@ class ExtractCommandTests {
.withMessageContaining("not compatible with layertools"); .withMessageContaining("not compatible with layertools");
} }
@Test
void runWithJarFileThatWouldWriteEntriesOutsideDestinationFails() throws IOException {
this.jarFile = createJarFile("test.jar", (out) -> {
try {
out.putNextEntry(new ZipEntry("e/../../e.jar"));
out.closeEntry();
}
catch (IOException ex) {
throw new IllegalStateException(ex);
}
});
given(this.context.getJarFile()).willReturn(this.jarFile);
assertThatIllegalStateException()
.isThrownBy(() -> this.command.run(Collections.emptyMap(), Collections.emptyList()))
.withMessageContaining("Entry 'e/../../e.jar' would be written");
}
private File createJarFile(String name) throws IOException { private File createJarFile(String name) throws IOException {
return createJarFile(name, (out) -> {
});
}
private File createJarFile(String name, Consumer<ZipOutputStream> streamHandler) throws IOException {
File file = new File(this.temp, name); File file = new File(this.temp, name);
try (ZipOutputStream out = new ZipOutputStream(new FileOutputStream(file))) { try (ZipOutputStream out = new ZipOutputStream(new FileOutputStream(file))) {
out.putNextEntry(new ZipEntry("a/")); out.putNextEntry(new ZipEntry("a/"));
...@@ -131,6 +157,7 @@ class ExtractCommandTests { ...@@ -131,6 +157,7 @@ class ExtractCommandTests {
out.closeEntry(); out.closeEntry();
out.putNextEntry(new ZipEntry("d/")); out.putNextEntry(new ZipEntry("d/"));
out.closeEntry(); out.closeEntry();
streamHandler.accept(out);
} }
return file; return file;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment