|
16 | 16 | import java.io.Closeable;
|
17 | 17 | import java.io.File;
|
18 | 18 | import java.io.IOException;
|
19 |
| -import java.io.InputStream; |
| 19 | +import java.net.URI; |
| 20 | +import java.nio.file.FileSystem; |
| 21 | +import java.nio.file.FileSystemAlreadyExistsException; |
20 | 22 | import java.nio.file.FileSystems;
|
21 | 23 | import java.nio.file.Files;
|
22 | 24 | import java.nio.file.Path;
|
23 | 25 | import java.nio.file.attribute.FileTime;
|
24 |
| -import java.util.Enumeration; |
| 26 | +import java.util.HashMap; |
| 27 | +import java.util.Iterator; |
| 28 | +import java.util.List; |
25 | 29 | import java.util.Locale;
|
26 |
| -import java.util.zip.ZipEntry; |
27 |
| -import java.util.zip.ZipFile; |
| 30 | +import java.util.Map; |
| 31 | +import java.util.stream.Collectors; |
| 32 | +import java.util.stream.Stream; |
28 | 33 |
|
29 | 34 | public class FS
|
30 | 35 | {
|
@@ -184,40 +189,60 @@ public static void extract(Path archive, Path destination) throws IOException
|
184 | 189 |
|
185 | 190 | public static void extractZip(Path archive, Path destination) throws IOException
|
186 | 191 | {
|
187 |
| - try (ZipFile zip = new ZipFile(archive.toFile())) |
| 192 | + StartLog.info("extract %s to %s", archive, destination); |
| 193 | + URI jaruri = URI.create("jar:" + archive.toUri()); |
| 194 | + Map<String, ?> fsEnv = Map.of(); |
| 195 | + try (FileSystem zipFs = FileSystems.newFileSystem(jaruri, fsEnv)) |
188 | 196 | {
|
189 |
| - StartLog.info("extract %s to %s", archive, destination); |
190 |
| - Enumeration<? extends ZipEntry> entries = zip.entries(); |
191 |
| - while (entries.hasMoreElements()) |
192 |
| - { |
193 |
| - ZipEntry entry = entries.nextElement(); |
| 197 | + copyZipContents(zipFs.getPath("/"), destination); |
| 198 | + } |
| 199 | + catch (FileSystemAlreadyExistsException e) |
| 200 | + { |
| 201 | + FileSystem zipFs = FileSystems.getFileSystem(jaruri); |
| 202 | + copyZipContents(zipFs.getPath("/"), destination); |
| 203 | + } |
| 204 | + } |
194 | 205 |
|
195 |
| - if (entry.isDirectory() || entry.getName().startsWith("/META-INF")) |
196 |
| - { |
197 |
| - // skip |
198 |
| - continue; |
199 |
| - } |
| 206 | + public static void copyZipContents(Path root, Path destination) throws IOException |
| 207 | + { |
| 208 | + if (!Files.exists(destination)) |
| 209 | + { |
| 210 | + Files.createDirectories(destination); |
| 211 | + } |
| 212 | + URI outputDirURI = destination.toUri(); |
| 213 | + URI archiveURI = root.toUri(); |
| 214 | + int archiveURISubIndex = archiveURI.toASCIIString().indexOf("!/") + 2; |
200 | 215 |
|
201 |
| - String entryName = entry.getName(); |
202 |
| - Path destFile = destination.resolve(entryName).normalize().toAbsolutePath(); |
203 |
| - // make sure extracted path does not escape the destination directory |
204 |
| - if (!destFile.startsWith(destination)) |
| 216 | + try (Stream<Path> entriesStream = Files.walk(root)) |
| 217 | + { |
| 218 | + // ensure proper unpack order (eg: directories before files) |
| 219 | + Stream<Path> sorted = entriesStream |
| 220 | + .filter((path) -> path.getNameCount() > 0) |
| 221 | + .filter((path) -> !path.getName(0).toString().equalsIgnoreCase("META-INF")) |
| 222 | + .sorted(); |
| 223 | + |
| 224 | + Iterator<Path> pathIterator = sorted.iterator(); |
| 225 | + while (pathIterator.hasNext()) |
| 226 | + { |
| 227 | + Path path = pathIterator.next(); |
| 228 | + URI entryURI = path.toUri(); |
| 229 | + String subURI = entryURI.toASCIIString().substring(archiveURISubIndex); |
| 230 | + URI outputPathURI = outputDirURI.resolve(subURI); |
| 231 | + Path outputPath = Path.of(outputPathURI); |
| 232 | + StartLog.info("zipFs: %s > %s", path, outputPath); |
| 233 | + if (Files.isDirectory(path)) |
205 | 234 | {
|
206 |
| - throw new IOException(String.format("Malicious Archive %s found with bad entry \"%s\"", |
207 |
| - archive, entryName)); |
| 235 | + if (!Files.exists(outputPath)) |
| 236 | + Files.createDirectory(outputPath); |
208 | 237 | }
|
209 |
| - if (!Files.exists(destFile)) |
| 238 | + else if (Files.exists(outputPath)) |
210 | 239 | {
|
211 |
| - FS.ensureDirectoryExists(destFile.getParent()); |
212 |
| - try (InputStream input = zip.getInputStream(entry)) |
213 |
| - { |
214 |
| - StartLog.debug("extracting %s", destFile); |
215 |
| - Files.copy(input, destFile); |
216 |
| - } |
| 240 | + StartLog.debug("skipping extraction (file exists) %s", outputPath); |
217 | 241 | }
|
218 | 242 | else
|
219 | 243 | {
|
220 |
| - StartLog.debug("skipping extract (file exists) %s", destFile); |
| 244 | + StartLog.info("extracting %s to %s", path, outputPath); |
| 245 | + Files.copy(path, outputPath); |
221 | 246 | }
|
222 | 247 | }
|
223 | 248 | }
|
|
0 commit comments