Skip to content

Commit e3236a9

Browse files
committed
Additional check when unpacking archives. Contributed by Wilfred Spiegelenburg.
1 parent 9502b47 commit e3236a9

File tree

2 files changed

+52
-0
lines changed

2 files changed

+52
-0
lines changed

hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/RunJar.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,12 +117,17 @@ public static void unJar(InputStream inputStream, File toDir,
117117
throws IOException {
118118
try (JarInputStream jar = new JarInputStream(inputStream)) {
119119
int numOfFailedLastModifiedSet = 0;
120+
String targetDirPath = toDir.getCanonicalPath() + File.separator;
120121
for (JarEntry entry = jar.getNextJarEntry();
121122
entry != null;
122123
entry = jar.getNextJarEntry()) {
123124
if (!entry.isDirectory() &&
124125
unpackRegex.matcher(entry.getName()).matches()) {
125126
File file = new File(toDir, entry.getName());
127+
if (!file.getCanonicalPath().startsWith(targetDirPath)) {
128+
throw new IOException("expanding " + entry.getName()
129+
+ " would create file outside of " + toDir);
130+
}
126131
ensureDirectory(file.getParentFile());
127132
try (OutputStream out = new FileOutputStream(file)) {
128133
IOUtils.copyBytes(jar, out, BUFFER_SIZE);
@@ -182,13 +187,18 @@ public static void unJar(File jarFile, File toDir, Pattern unpackRegex)
182187
throws IOException {
183188
try (JarFile jar = new JarFile(jarFile)) {
184189
int numOfFailedLastModifiedSet = 0;
190+
String targetDirPath = toDir.getCanonicalPath() + File.separator;
185191
Enumeration<JarEntry> entries = jar.entries();
186192
while (entries.hasMoreElements()) {
187193
final JarEntry entry = entries.nextElement();
188194
if (!entry.isDirectory() &&
189195
unpackRegex.matcher(entry.getName()).matches()) {
190196
try (InputStream in = jar.getInputStream(entry)) {
191197
File file = new File(toDir, entry.getName());
198+
if (!file.getCanonicalPath().startsWith(targetDirPath)) {
199+
throw new IOException("expanding " + entry.getName()
200+
+ " would create file outside of " + toDir);
201+
}
192202
ensureDirectory(file.getParentFile());
193203
try (OutputStream out = new FileOutputStream(file)) {
194204
IOUtils.copyBytes(in, out, BUFFER_SIZE);

hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/TestRunJar.java

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import static org.junit.Assert.assertEquals;
2222
import static org.junit.Assert.assertFalse;
2323
import static org.junit.Assert.assertTrue;
24+
import static org.junit.Assert.fail;
2425
import static org.mockito.Matchers.any;
2526
import static org.mockito.Mockito.spy;
2627
import static org.mockito.Mockito.times;
@@ -32,6 +33,7 @@
3233
import java.io.FileOutputStream;
3334
import java.io.IOException;
3435
import java.io.InputStream;
36+
import java.nio.charset.StandardCharsets;
3537
import java.util.Random;
3638
import java.util.jar.JarEntry;
3739
import java.util.jar.JarOutputStream;
@@ -255,4 +257,44 @@ public void testClientClassLoaderSkipUnjar() throws Throwable {
255257
// it should not throw an exception
256258
verify(runJar, times(0)).unJar(any(File.class), any(File.class));
257259
}
260+
261+
@Test
262+
public void testUnJar2() throws IOException {
263+
// make a simple zip
264+
File jarFile = new File(TEST_ROOT_DIR, TEST_JAR_NAME);
265+
JarOutputStream jstream =
266+
new JarOutputStream(new FileOutputStream(jarFile));
267+
JarEntry je = new JarEntry("META-INF/MANIFEST.MF");
268+
byte[] data = "Manifest-Version: 1.0\nCreated-By: 1.8.0_1 (Manual)"
269+
.getBytes(StandardCharsets.UTF_8);
270+
je.setSize(data.length);
271+
jstream.putNextEntry(je);
272+
jstream.write(data);
273+
jstream.closeEntry();
274+
je = new JarEntry("../outside.path");
275+
data = "any data here".getBytes(StandardCharsets.UTF_8);
276+
je.setSize(data.length);
277+
jstream.putNextEntry(je);
278+
jstream.write(data);
279+
jstream.closeEntry();
280+
jstream.close();
281+
282+
File unjarDir = getUnjarDir("unjar-path");
283+
284+
// Unjar everything
285+
try {
286+
RunJar.unJar(jarFile, unjarDir, MATCH_ANY);
287+
fail("unJar should throw IOException.");
288+
} catch (IOException e) {
289+
GenericTestUtils.assertExceptionContains(
290+
"would create file outside of", e);
291+
}
292+
try {
293+
RunJar.unJar(new FileInputStream(jarFile), unjarDir, MATCH_ANY);
294+
fail("unJar should throw IOException.");
295+
} catch (IOException e) {
296+
GenericTestUtils.assertExceptionContains(
297+
"would create file outside of", e);
298+
}
299+
}
258300
}

0 commit comments

Comments
 (0)