diff --git a/az-core/src/main/java/azkaban/utils/Utils.java b/az-core/src/main/java/azkaban/utils/Utils.java
index 3f84e25..0a6fffa 100644
--- a/az-core/src/main/java/azkaban/utils/Utils.java
+++ b/az-core/src/main/java/azkaban/utils/Utils.java
@@ -199,6 +199,12 @@ public class Utils {
while (entries.hasMoreElements()) {
final ZipEntry entry = (ZipEntry) entries.nextElement();
final File newFile = new File(dest, entry.getName());
+ if (!newFile.getCanonicalPath().startsWith(dest.getCanonicalPath())) {
+ throw new IOException(
+ "Extracting zip entry would have resulted in a file outside the specified destination"
+ + " directory.");
+ }
+
if (entry.isDirectory()) {
newFile.mkdirs();
} else {
diff --git a/az-core/src/test/java/azkaban/utils/UtilsTest.java b/az-core/src/test/java/azkaban/utils/UtilsTest.java
new file mode 100644
index 0000000..7889b7c
--- /dev/null
+++ b/az-core/src/test/java/azkaban/utils/UtilsTest.java
@@ -0,0 +1,54 @@
+/*
+* Copyright 2018 LinkedIn Corp.
+*
+* 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 azkaban.utils;
+
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipOutputStream;
+import org.junit.Test;
+
+/**
+ * Test class for azkaban.utils.Utils
+ */
+public class UtilsTest {
+
+ /**
+ * An insecure zip file may hold path traversal filenames. During unzipping, the filename gets
+ * concatenated to the target directory. The final path may end up outside the target directory,
+ * causing security issues.
+ *
+ * @throws IOException the io exception
+ */
+ @Test
+ public void testUnzipInsecureFile() throws IOException {
+ final File zipFile = new File("myTest.zip");
+ try (final ZipOutputStream out = new ZipOutputStream(new FileOutputStream(zipFile))) {
+ final ZipEntry entry = new ZipEntry("../../../../../evil.txt");
+ out.putNextEntry(entry);
+ }
+
+ final ZipFile source = new ZipFile(zipFile);
+ final File dest = Utils.createTempDir();
+ assertThatThrownBy(() -> Utils.unzip(source, dest)).isInstanceOf(IOException.class)
+ .hasMessageContaining("Extracting zip entry would have resulted in a file outside the "
+ + "specified destination directory.");
+ }
+}