Skip to content

Commit 3fb29e9

Browse files
committed
SCANJLIB-229 Improve tar.gz extraction
1 parent 06428b1 commit 3fb29e9

3 files changed

Lines changed: 27 additions & 10 deletions

File tree

lib/src/main/java/org/sonarsource/scanner/lib/internal/util/CompressionUtils.java

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ public static Path unzip(Path zip, Path toDir, Predicate<ZipEntry> filter) throw
8585
if (filter.test(entry)) {
8686
var target = toDir.resolve(entry.getName());
8787

88-
verifyInsideTargetDirectory(entry, target, targetDirNormalizedPath);
88+
verifyInsideTargetDirectory(entry.getName(), target, targetDirNormalizedPath);
8989

9090
if (entry.isDirectory()) {
9191
throwExceptionIfDirectoryIsNotCreatable(target);
@@ -100,10 +100,10 @@ public static Path unzip(Path zip, Path toDir, Predicate<ZipEntry> filter) throw
100100
}
101101
}
102102

103-
private static void verifyInsideTargetDirectory(ZipEntry entry, Path entryPath, Path targetDirNormalizedPath) {
103+
private static void verifyInsideTargetDirectory(String entryName, Path entryPath, Path targetDirNormalizedPath) {
104104
if (!entryPath.normalize().startsWith(targetDirNormalizedPath)) {
105105
// vulnerability - trying to create a file outside the target directory
106-
throw new IllegalStateException("Unzipping an entry outside the target directory is not allowed: " + entry.getName());
106+
throw new IllegalStateException("Extracting an entry outside the target directory is not allowed: " + entryName);
107107
}
108108
}
109109

@@ -123,6 +123,7 @@ private static void copy(ZipFile zipFile, ZipEntry entry, Path to) throws IOExce
123123
}
124124

125125
public static void extractTarGz(Path compressedFile, Path targetDir) throws IOException {
126+
Path targetDirNormalizedPath = targetDir.normalize();
126127
try (InputStream fis = Files.newInputStream(compressedFile);
127128
InputStream bis = new BufferedInputStream(fis);
128129
InputStream gzis = new GzipCompressorInputStream(bis);
@@ -132,18 +133,21 @@ public static void extractTarGz(Path compressedFile, Path targetDir) throws IOEx
132133
if (!tarArchiveInputStream.canReadEntryData(targzEntry)) {
133134
continue;
134135
}
135-
var entry = targetDir.resolve(targzEntry.getName());
136+
var target = targetDir.resolve(targzEntry.getName());
137+
138+
verifyInsideTargetDirectory(targzEntry.getName(), target, targetDirNormalizedPath);
139+
136140
if (targzEntry.isDirectory()) {
137-
Files.createDirectories(entry);
141+
Files.createDirectories(target);
138142
} else {
139-
if (!Files.isDirectory(entry.getParent())) {
140-
Files.createDirectories(entry.getParent());
143+
if (!Files.isDirectory(target.getParent())) {
144+
Files.createDirectories(target.getParent());
141145
}
142-
Files.copy(tarArchiveInputStream, entry, StandardCopyOption.REPLACE_EXISTING);
146+
Files.copy(tarArchiveInputStream, target, StandardCopyOption.REPLACE_EXISTING);
143147
int mode = targzEntry.getMode();
144148
if (mode != 0 && !IS_OS_WINDOWS) {
145149
Set<PosixFilePermission> permissions = fromFileMode(mode);
146-
Files.setPosixFilePermissions(entry, permissions);
150+
Files.setPosixFilePermissions(target, permissions);
147151
}
148152
}
149153
}

lib/src/test/java/org/sonarsource/scanner/lib/internal/util/CompressionUtilsTest.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ void fail_if_unzipping_file_outside_target_directory() {
6767

6868
assertThatThrownBy(() -> CompressionUtils.unzip(zip, toDir))
6969
.isInstanceOf(IllegalStateException.class)
70-
.hasMessage("Unzipping an entry outside the target directory is not allowed: ../../../../../../../../../../../../../../../../" +
70+
.hasMessage("Extracting an entry outside the target directory is not allowed: ../../../../../../../../../../../../../../../../" +
7171
"../../../../../../../../../../../../../../../../../../../../../../../../tmp/evil.txt");
7272
}
7373

@@ -79,6 +79,19 @@ void extract_tar_gz() throws IOException {
7979
assertThat(toDir.toFile().list()).hasSize(3);
8080
}
8181

82+
@Test
83+
void fail_if_extracting_targz_file_outside_target_directory() {
84+
var targz = Paths.get("src/test/resources/slip.tar.gz");
85+
var toDir = temp.resolve("dir");
86+
87+
assertThatThrownBy(() -> CompressionUtils.extractTarGz(targz, toDir))
88+
.isInstanceOf(IllegalStateException.class)
89+
.hasMessage("Extracting an entry outside the target directory is not allowed: ../../../../../../../../../../../../../../../../../"
90+
+ "../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../"
91+
+ "../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../"
92+
+ "../tmp/slipped.txt");
93+
}
94+
8295
@Test
8396
void fileMode_conversion() {
8497
assertThat(CompressionUtils.fromFileMode(Integer.parseInt("000", 8))).isEmpty();

lib/src/test/resources/slip.tar.gz

210 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)