public record UnzipFile()

in baremaps-core/src/main/java/org/apache/baremaps/workflow/tasks/UnzipFile.java [24:87]


public record UnzipFile(Path file, Path directory) implements Task {

  private static final long THRESHOLD_ENTRIES = 10000;
  private static final long THRESHOLD_SIZE = 10l << 30;
  private static final double THRESHOLD_RATIO = 100;

  private static final Logger logger = LoggerFactory.getLogger(UnzipFile.class);

  @Override
  public void execute(WorkflowContext context) throws Exception {
    var filePath = file.toAbsolutePath();
    var directoryPath = directory.toAbsolutePath();

    try (var zipFile = new ZipFile(filePath.toFile())) {
      var entries = zipFile.entries();
      long totalSizeArchive = 0;
      long totalEntryArchive = 0;

      while (entries.hasMoreElements()) {
        var ze = entries.nextElement();
        var path = directoryPath.resolve(ze.getName());

        var file = path.toFile().getCanonicalFile();
        var directory = directoryPath.toFile().getCanonicalFile();
        if (!file.toPath().startsWith(directory.toPath())) {
          throw new IOException("Entry is outside of the target directory");
        }

        Files.createDirectories(path.getParent());
        Files.write(path, new byte[] {}, StandardOpenOption.CREATE,
            StandardOpenOption.TRUNCATE_EXISTING);

        try (var input = new BufferedInputStream(zipFile.getInputStream(ze));
            var output = new BufferedOutputStream(new FileOutputStream(path.toFile()))) {

          totalEntryArchive++;

          int nBytes = -1;
          byte[] buffer = new byte[4096];
          long totalSizeEntry = 0;

          while ((nBytes = input.read(buffer)) > 0) {
            output.write(buffer, 0, nBytes);
            totalSizeEntry += nBytes;
            totalSizeArchive += nBytes;

            double compressionRatio = (double) totalSizeEntry / (double) ze.getCompressedSize();
            if (compressionRatio > THRESHOLD_RATIO) {
              throw new WorkflowException("Archive compression ratio is too high");
            }
          }

          if (totalSizeArchive > THRESHOLD_SIZE) {
            throw new IOException("Archive is too large");
          }

          if (totalEntryArchive > THRESHOLD_ENTRIES) {
            throw new IOException("Archive contains too many entries");
          }
        }
      }
    }
  }
}