Skip to content

Commit d543ffe

Browse files
committed
[api] fix issue in Tar/Zip Utils that resulted in incorrect artifact extraction
1 parent 73f7cb0 commit d543ffe

File tree

7 files changed

+40
-22
lines changed

7 files changed

+40
-22
lines changed

api/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ dependencies {
1515
exclude("junit", "junit")
1616
}
1717
testImplementation(libs.slf4j.simple)
18+
testImplementation(project(":testing"))
1819
testRuntimeOnly(project(":engines:pytorch:pytorch-model-zoo"))
1920
testRuntimeOnly(project(":engines:pytorch:pytorch-jni"))
2021
}

api/src/main/java/ai/djl/util/TarUtils.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,8 @@ public static void untar(InputStream is, Path dir, boolean gzip) throws IOExcept
4848
try (TarArchiveInputStream tis = new TarArchiveInputStream(bis)) {
4949
TarArchiveEntry entry;
5050
while ((entry = tis.getNextEntry()) != null) {
51-
String entryName = ZipUtils.removeLeadingFileSeparator(entry.getName());
52-
if (entryName.contains("..")) {
53-
throw new IOException("Malicious zip entry: " + entryName);
54-
}
51+
String entryName = entry.getName();
52+
ZipUtils.validateArchiveEntry(entryName, dir);
5553
Path file = dir.resolve(entryName).toAbsolutePath();
5654
if (entry.isDirectory()) {
5755
Files.createDirectories(file);

api/src/main/java/ai/djl/util/ZipUtils.java

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,10 @@ public static void unzip(InputStream is, Path dest) throws IOException {
5252
ZipEntry entry;
5353
Set<String> set = new HashSet<>();
5454
while ((entry = zis.getNextEntry()) != null) {
55-
String name = removeLeadingFileSeparator(entry.getName());
56-
if (name.contains("..")) {
57-
throw new IOException("Malicious zip entry: " + name);
58-
}
59-
set.add(name);
60-
Path file = dest.resolve(name).toAbsolutePath();
55+
String entryName = entry.getName();
56+
validateArchiveEntry(entry.getName(), dest);
57+
set.add(entryName);
58+
Path file = dest.resolve(entryName).toAbsolutePath();
6159
if (entry.isDirectory()) {
6260
Files.createDirectories(file);
6361
} else {
@@ -121,14 +119,18 @@ private static void addToZip(Path root, Path file, ZipOutputStream zos) throws I
121119
}
122120
}
123121

124-
static String removeLeadingFileSeparator(String name) {
125-
int index = 0;
126-
for (; index < name.length(); index++) {
127-
if (name.charAt(index) != File.separatorChar) {
128-
break;
129-
}
122+
static void validateArchiveEntry(String name, Path destination) throws IOException {
123+
if (name.contains("..")) {
124+
throw new IOException("Bad archive entry, traversal element is not allowed: " + name);
125+
}
126+
Path expectedOutputPath = destination.resolve(name).normalize();
127+
if (!expectedOutputPath.startsWith(destination.normalize())) {
128+
throw new IOException(
129+
"Bad archive entry "
130+
+ name
131+
+ ". Attempted write outside destination "
132+
+ destination);
130133
}
131-
return name.substring(index);
132134
}
133135

134136
private static final class ValidationInputStream extends FilterInputStream {

api/src/test/java/ai/djl/util/ZipUtilsTest.java

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
*/
1313
package ai.djl.util;
1414

15+
import ai.djl.testing.TestRequirements;
16+
1517
import org.apache.commons.compress.archivers.zip.Zip64Mode;
1618
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
1719
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
@@ -49,13 +51,21 @@ public void testEmptyZipFile() throws IOException {
4951
public void testOffendingTar() throws IOException {
5052
Path path = Paths.get("src/test/resources/offending.tar");
5153
Path output = Paths.get("build/output");
52-
Path file = output.resolve("tmp/empty.txt");
53-
Utils.deleteQuietly(file);
5454
Files.createDirectories(output);
5555
try (InputStream is = Files.newInputStream(path)) {
56-
TarUtils.untar(is, output, false);
56+
Assert.assertThrows(() -> TarUtils.untar(is, output, false));
57+
}
58+
}
59+
60+
@Test
61+
public void testLinuxCreatedWindowsUsedOffendingTar() throws IOException {
62+
TestRequirements.windows();
63+
Path tarPath = Paths.get("src/test/resources/linux_create_windows_use.tar");
64+
Path output = Paths.get("build/output");
65+
Files.createDirectories(output);
66+
try (InputStream is = Files.newInputStream(tarPath)) {
67+
Assert.assertThrows(() -> TarUtils.untar(is, output, false));
5768
}
58-
Assert.assertTrue(Files.exists(file));
5969
}
6070

6171
@Test
Binary file not shown.

integration/src/test/java/ai/djl/integration/IntegrationTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public void runIntegrationTests() {
3838
// TODO: windows CPU build is having OOM issue if 3 engines are loaded and running tests
3939
// together
4040
if (System.getProperty("os.name").startsWith("Win")) {
41-
engines.add("MXNet");
41+
engines.add("PyTorch");
4242
} else if ("aarch64".equals(System.getProperty("os.arch"))) {
4343
engines.add("PyTorch");
4444
} else {

testing/src/main/java/ai/djl/testing/TestRequirements.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,13 @@ public static void notWindows() {
9696
}
9797
}
9898

99+
/** Requires that the test runs on windows, not OSX or linux. */
100+
public static void windows() {
101+
if (!System.getProperty("os.name").toLowerCase().startsWith("win")) {
102+
throw new SkipException("This test requires windows");
103+
}
104+
}
105+
99106
/** Requires that the test runs on x86_64 arch. */
100107
public static void notArm() {
101108
if ("aarch64".equals(System.getProperty("os.arch"))) {

0 commit comments

Comments
 (0)