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 stored file path only, need create parent directories, e.g data/folder/file.txt if ( targetPath.getParent() != null ) { if ( Files.notExists(targetPath.getParent()) ) { Files.createDirectories(targetPath.getParent()); } } Files.copy(zis, targetPath, StandardCopyOption.REPLACE_EXISTING); } 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; } }