Skip to content

Commit

Permalink
Normalize the destination directory when extracting zip files (#1125)
Browse files Browse the repository at this point in the history
Otherwise, relative installDirectory paths cannot be used because the zip-slip
check fails with "Bad zip entry" exception even the zip is fine.

Closes #1124
  • Loading branch information
mvilliger committed Nov 30, 2023
1 parent 4045aaf commit fc8b9a1
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 8 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ Last public release: [![Maven Central](https://maven-badges.herokuapp.com/maven-

## Changelog

### 1.14.3

* Prevent `Bad zip entry` exceptions when installing Node to a relative directory ([#1124](https://github.com/eirslett/frontend-maven-plugin/issues/1124))

### 1.14.2

* Prevent corrupt downloaded files by waiting for the download to complete before writing the file to disk.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,17 @@
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.AccessDeniedException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
Expand Down Expand Up @@ -73,31 +77,29 @@ public void extract(String archive, String destinationDirectory) throws ArchiveE
"Unexpected interruption of while waiting for extraction process", e);
}
} else if ("zip".equals(FileUtils.getExtension(archiveFile.getAbsolutePath()))) {
ZipFile zipFile = new ZipFile(archiveFile);
try {
Path destinationPath = Paths.get(destinationDirectory).normalize();
try (ZipFile zipFile = new ZipFile(archiveFile)) {
Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
final File destPath = new File(destinationDirectory, entry.getName());
if (!destPath.toPath().normalize().startsWith(destinationDirectory)) {
final Path destPath = destinationPath.resolve(entry.getName()).normalize();
if (!destPath.startsWith(destinationPath)) {
throw new RuntimeException("Bad zip entry");
}
prepDestination(destPath, entry.isDirectory());
prepDestination(destPath.toFile(), entry.isDirectory());
if (!entry.isDirectory()) {
InputStream in = null;
OutputStream out = null;
try {
in = zipFile.getInputStream(entry);
out = new FileOutputStream(destPath);
out = new BufferedOutputStream(Files.newOutputStream(destPath));
IOUtils.copy(in, out);
} finally {
IOUtils.closeQuietly(in);
IOUtils.closeQuietly(out);
}
}
}
} finally {
zipFile.close();
}
} else {
// TarArchiveInputStream can be constructed with a normal FileInputStream if
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
public class DefaultArchiveExtractorTest {

private final String BAD_TAR = "src/test/resources/bad.tgz";
private final String BAD_ZIP = "src/test/resources/bad.zip"; // sample zip with zip slip vulnerability
private final String GOOD_TAR = "src/test/resources/good.tgz";
private final String GOOD_ZIP = "src/test/resources/good.zip";

@TempDir
public File temp;
Expand Down Expand Up @@ -48,6 +50,26 @@ public void extractBadTarFile() {
extractor.extract(BAD_TAR, temp.getPath()));
}

@Test
public void extractGoodZipFile() throws Exception {
assertGoodZipExtractedTo(temp);
}

@Test
public void extractGoodZipFileWithRelTarget() throws Exception {
assertGoodZipExtractedTo(createRelPath(temp));
}

@Test
public void extractBadZipFile() {
assertBadZipThrowsException(temp);
}

@Test
public void extractBadZipFileWithRelTarget() throws Exception {
assertBadZipThrowsException(createRelPath(temp));
}

@Test
public void extractBadTarFileSymlink() {
File destination = new File(temp + "/destination");
Expand All @@ -56,6 +78,26 @@ public void extractBadTarFileSymlink() {
Assertions.assertThrows(ArchiveExtractionException.class, () -> extractor.extract(BAD_TAR, link.toString()));
}

private void assertBadZipThrowsException(File targetDir) {
Assertions.assertThrows(RuntimeException.class, () ->
extractor.extract(BAD_ZIP, targetDir.getPath()));
}

private void assertGoodZipExtractedTo(File targetDir) throws Exception {
extractor.extract(GOOD_ZIP, targetDir.getPath());
String nameOfFileInZip = "zip";
Assertions.assertTrue(new File(temp, nameOfFileInZip).isFile(), "zip content not found in target directory");
}

private static File createRelPath(File orig) throws IOException {
orig = orig.getCanonicalFile();
String dirName = orig.getName();
File result = new File(orig, "../" + dirName);
Assertions.assertNotEquals(orig, result); // ensure result is different from input
Assertions.assertEquals(orig, result.getCanonicalFile()); // ensure result points to same dir
return result;
}

private Path createSymlinkOrSkipTest(Path link, Path target) {
try {
return Files.createSymbolicLink(link, target);
Expand Down
Binary file added frontend-plugin-core/src/test/resources/bad.zip
Binary file not shown.
Binary file added frontend-plugin-core/src/test/resources/good.zip
Binary file not shown.

0 comments on commit fc8b9a1

Please sign in to comment.