Skip to content

Commit

Permalink
Add unzip util method (#906)
Browse files Browse the repository at this point in the history
* Add unzip util method
* Eliminate Zip-Slip vulnernability (#908)
  • Loading branch information
chanseokoh authored Aug 30, 2018
1 parent 7bf07db commit 223c031
Show file tree
Hide file tree
Showing 5 changed files with 155 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* Copyright 2018 Google LLC. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/

package com.google.cloud.tools.jib.plugins.common;

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;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

/** Utility class for Zip archives. */
public class ZipUtil {

/**
* Unzips {@code archive} into {@code destination}.
*
* @param archive zip archive to unzip
* @param destination target root for unzipping
* @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()) {
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 {
try (OutputStream out = Files.newOutputStream(entryPath)) {
ByteStreams.copy(zipIn, out);
}
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*
* Copyright 2018 Google LLC. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/

package com.google.cloud.tools.jib.plugins.common;

import com.google.common.io.Resources;
import java.io.IOException;
import java.net.URISyntaxException;
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;

/** Tests for {@link ZipUtil}. */
public class ZipUtilTest {

@Rule public final TemporaryFolder tempFolder = new TemporaryFolder();

@Test
public void testUnzip() throws URISyntaxException, IOException {
verifyUnzip(tempFolder.getRoot().toPath());
}

@Test
public void testUnzip_nonExistingDestination() throws URISyntaxException, IOException {
Path destination = tempFolder.getRoot().toPath().resolve("non/exisiting");
verifyUnzip(destination);

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());

ZipUtil.unzip(archive, destination);

Assert.assertTrue(Files.isDirectory(destination.resolve("my-zip/some/sub/folder")));

Path file1 = destination.resolve("file1.txt");
Path file2 = destination.resolve("my-zip/file2.txt");
Path file3 = destination.resolve("my-zip/some/sub/folder/file3.txt");
Assert.assertEquals("file1", Files.readAllLines(file1).get(0));
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: "));
}
}
}
Binary file not shown.
Binary file not shown.
Binary file not shown.

0 comments on commit 223c031

Please sign in to comment.