package eu.openaire.urls_controller.util; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; public class FileUnZipper { private static final Logger logger = LoggerFactory.getLogger(FileUnZipper.class); public static void unzipFolder(Path source, Path target) throws Exception { try ( ZipInputStream zis = new ZipInputStream(new FileInputStream(source.toFile())) ) { // Iterate over the files in zip and un-zip them. ZipEntry zipEntry = zis.getNextEntry(); while ( zipEntry != null ) { Path targetPath = zipSlipProtect(zipEntry, target); if ( zipEntry.getName().endsWith(File.separator) ) // If we have a directory. Files.createDirectories(targetPath); else { // Some zip -files store only the file-paths and not separate directories. We need to create parent directories, e.g data/folder/file.txt Path parentPath = targetPath.getParent(); if ( (parentPath != null) && Files.notExists(parentPath) ) { Files.createDirectories(parentPath); } Files.copy(zis, targetPath, StandardCopyOption.REPLACE_EXISTING); // Copy an individual entry. } zipEntry = zis.getNextEntry(); } zis.closeEntry(); } } // Protect from a Zip Slip attack: https://snyk.io/research/zip-slip-vulnerability public static Path zipSlipProtect(ZipEntry zipEntry, Path targetDir) throws IOException { Path targetDirResolved = targetDir.resolve(zipEntry.getName()); // Make sure normalized file still has targetDir as its prefix, else throw an exception. Path normalizePath = targetDirResolved.normalize(); if ( !normalizePath.startsWith(targetDir) ) { throw new IOException("Bad zip entry: " + zipEntry.getName()); } return normalizePath; } }