Skip to content

Commit 7415cc5

Browse files
committed
[api] fix issue in Tar/Zip Utils that resulted in incorrect artifact extraction (#3544)
1 parent 503289a commit 7415cc5

12 files changed

+49
-24
lines changed

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("Invalid archive entry, contains traversal elements: " + name);
125+
}
126+
Path expectedOutputPath = destination.resolve(name).toAbsolutePath().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: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,15 +47,40 @@ public void testEmptyZipFile() throws IOException {
4747

4848
@Test
4949
public void testOffendingTar() throws IOException {
50-
Path path = Paths.get("src/test/resources/offending.tar");
50+
String[] offendingTars =
51+
new String[] {
52+
"src/test/resources/linux-created-offender-traversal-elements.tar",
53+
"src/test/resources/windows-created-offender-traversal-elements.tar",
54+
"src/test/resources/linux-created-offender-root.tar",
55+
"src/test/resources/windows-created-offender-root.tar",
56+
};
5157
Path output = Paths.get("build/output");
52-
Path file = output.resolve("tmp/empty.txt");
53-
Utils.deleteQuietly(file);
5458
Files.createDirectories(output);
55-
try (InputStream is = Files.newInputStream(path)) {
56-
TarUtils.untar(is, output, false);
59+
for (String offendingTar : offendingTars) {
60+
Path tarPath = Paths.get(offendingTar);
61+
try (InputStream is = Files.newInputStream(tarPath)) {
62+
Assert.assertThrows(() -> TarUtils.untar(is, output, false));
63+
}
64+
}
65+
}
66+
67+
@Test
68+
public void testOffendingZip() throws IOException {
69+
String[] offendingTars =
70+
new String[] {
71+
"src/test/resources/linux-created-offender-traversal-elements.zip",
72+
"src/test/resources/windows-created-offender-traversal-elements.zip",
73+
"src/test/resources/linux-created-offender-root.zip",
74+
"src/test/resources/windows-created-offender-root.zip",
75+
};
76+
Path output = Paths.get("build/output");
77+
Files.createDirectories(output);
78+
for (String offendingTar : offendingTars) {
79+
Path tarPath = Paths.get(offendingTar);
80+
try (InputStream is = Files.newInputStream(tarPath)) {
81+
Assert.assertThrows(() -> ZipUtils.unzip(is, output));
82+
}
5783
}
58-
Assert.assertTrue(Files.exists(file));
5984
}
6085

6186
@Test
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

0 commit comments

Comments
 (0)