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
10 KB
Binary file not shown.
142 Bytes
Binary file not shown.
Binary file not shown.
Binary file not shown.
10 KB
Binary file not shown.
142 Bytes
Binary file not shown.
Binary file not shown.

0 commit comments

Comments
 (0)