srikanth-lingala / zip4j

A Java library for zip files and streams
Apache License 2.0
2k stars 307 forks source link

Extracting ZIP with entry named `.` deletes the output directory #545

Open Marcono1234 opened 1 month ago

Marcono1234 commented 1 month ago

Version

Zip4j 2.11.5 Windows 10 (but might apply to Linux as well)

Description

When extracting a ZIP which contains an entry named ., Zip4j deletes the output directory before eventually throwing an exception. This is unexpected and looks like a bug. Zip4j should not delete the output directory, instead the user code should do that, if desired.

The underlying issue seems to be that Zip4j does not ignore an entry named . (respectively throw an exception) but instead treats it as regular file and tries to write to it. However, because it points to the existing output directory, trying to open the directory as file fails. Zip4j then erroneously tries to perform clean-up and actually deletes the directory here: https://github.com/srikanth-lingala/zip4j/blob/c4c993cb143db99832eadd03699bd8228a9793b8/src/main/java/net/lingala/zip4j/tasks/AbstractExtractFileTask.java#L114-L117

Note: This actually applies to more than just the literal string ., for example also to . (with trailing space). So to detect this is might be best to check the result of getCanonicalPath(), or to adjust AbstractExtractFileTask#unzipFile to fail fast if outputFile is an existing directory (and not try to delete it).

Example code

Path zipPath = Files.createTempFile("zip-test", ".zip");
Path outputDir = Files.createTempDirectory("zip-test");

try (ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(zipPath))) {
    zipOut.putNextEntry(new ZipEntry("."));
}

try (ZipFile zipFile = new ZipFile(zipPath.toFile())) {
    zipFile.extractAll(outputDir.toString());
} catch (ZipException e) {
    System.out.println("Encountered expected exception: " + e);
} finally {
    Files.delete(zipPath);
}
System.out.println("Output dir exists: " + Files.isDirectory(outputDir));

This will unexpectedly print

Output dir exists: false