diff --git a/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/ZipUtil.java b/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/ZipUtil.java index 57797fc57e..dbaa98ce7c 100644 --- a/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/ZipUtil.java +++ b/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/ZipUtil.java @@ -18,6 +18,7 @@ import com.google.common.io.ByteStreams; import java.io.BufferedInputStream; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -37,13 +38,19 @@ public class ZipUtil { * @throws IOException when I/O error occurs */ public static void unzip(Path archive, Path destination) throws IOException { + String canonicalDestination = destination.toFile().getCanonicalPath(); + try (InputStream fileIn = Files.newInputStream(archive); ZipInputStream zipIn = new ZipInputStream(new BufferedInputStream(fileIn))) { for (ZipEntry entry = zipIn.getNextEntry(); entry != null; entry = zipIn.getNextEntry()) { - // TODO: check Zip-Slip vulnerability: https://snyk.io/research/zip-slip-vulnerability#java Path entryPath = destination.resolve(entry.getName()); + String canonicalTarget = entryPath.toFile().getCanonicalPath(); + if (!canonicalTarget.startsWith(canonicalDestination + File.separator)) { + throw new IOException("Blocked unzipping files outside destination: " + entry.getName()); + } + if (entry.isDirectory()) { Files.createDirectories(entryPath); } else { diff --git a/jib-plugins-common/src/test/java/com/google/cloud/tools/jib/plugins/common/ZipUtilTest.java b/jib-plugins-common/src/test/java/com/google/cloud/tools/jib/plugins/common/ZipUtilTest.java index a34fc9aa4b..777b0d10cc 100644 --- a/jib-plugins-common/src/test/java/com/google/cloud/tools/jib/plugins/common/ZipUtilTest.java +++ b/jib-plugins-common/src/test/java/com/google/cloud/tools/jib/plugins/common/ZipUtilTest.java @@ -22,7 +22,9 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import org.hamcrest.CoreMatchers; import org.junit.Assert; +import org.junit.Assume; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; @@ -45,6 +47,22 @@ public void testUnzip_nonExistingDestination() throws URISyntaxException, IOExce Assert.assertTrue(Files.exists(destination)); } + @Test + public void testZipSlipVulnerability_windows() throws URISyntaxException { + Assume.assumeTrue(System.getProperty("os.name").startsWith("Windows")); + + Path archive = Paths.get(Resources.getResource("test-archives/zip-slip-win.zip").toURI()); + verifyZipSlipSafe(archive); + } + + @Test + public void testZipSlipVulnerability_unix() throws URISyntaxException { + Assume.assumeFalse(System.getProperty("os.name").startsWith("Windows")); + + Path archive = Paths.get(Resources.getResource("test-archives/zip-slip.zip").toURI()); + verifyZipSlipSafe(archive); + } + private void verifyUnzip(Path destination) throws URISyntaxException, IOException { Path archive = Paths.get(Resources.getResource("test-archives/test.zip").toURI()); @@ -59,4 +77,15 @@ private void verifyUnzip(Path destination) throws URISyntaxException, IOExceptio Assert.assertEquals("file2", Files.readAllLines(file2).get(0)); Assert.assertEquals("file3", Files.readAllLines(file3).get(0)); } + + private void verifyZipSlipSafe(Path archive) { + try { + ZipUtil.unzip(archive, tempFolder.getRoot().toPath()); + Assert.fail("Should block Zip-Slip"); + } catch (IOException ex) { + Assert.assertThat( + ex.getMessage(), + CoreMatchers.startsWith("Blocked unzipping files outside destination: ")); + } + } } diff --git a/jib-plugins-common/src/test/resources/test-archives/zip-slip-win.zip b/jib-plugins-common/src/test/resources/test-archives/zip-slip-win.zip new file mode 100644 index 0000000000..3474c88bec Binary files /dev/null and b/jib-plugins-common/src/test/resources/test-archives/zip-slip-win.zip differ diff --git a/jib-plugins-common/src/test/resources/test-archives/zip-slip.zip b/jib-plugins-common/src/test/resources/test-archives/zip-slip.zip new file mode 100644 index 0000000000..38b3f499de Binary files /dev/null and b/jib-plugins-common/src/test/resources/test-archives/zip-slip.zip differ