GoogleContainerTools/jib · critical · IOException

Blocked unzipping files outside destination: ${entryName} fr

Error message

Blocked unzipping files outside destination: ${entryName} from ${archive}

What it means

ZipUtil.unzip guards against zip-slip path traversal: each entry's resolved canonical path must remain under the destination's canonical path. When an entry name escapes the destination (e.g. via ../ segments or absolute entry names), unzip throws IOException 'Blocked unzipping files outside destination: <entryName> from <archive>'.

Source

Thrown at jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/ZipUtil.java:80

      throws IOException {
    if (enableReproducibleTimestamps
        && Files.isDirectory(destination)
        && destination.toFile().list().length != 0) {
      throw new IllegalStateException(
          "Cannot enable reproducible timestamps. They can only be enabled when the target root doesn't exist or is an empty directory");
    }
    String canonicalDestination = destination.toFile().getCanonicalPath();
    List<ZipEntry> entries = new ArrayList<>();
    try (InputStream fileIn = new BufferedInputStream(Files.newInputStream(archive));
        ZipInputStream zipIn = new ZipInputStream(fileIn)) {
      for (ZipEntry entry = zipIn.getNextEntry(); entry != null; entry = zipIn.getNextEntry()) {
        entries.add(entry);
        Path entryPath = destination.resolve(entry.getName());

        String canonicalTarget = entryPath.toFile().getCanonicalPath();
        if (!canonicalTarget.startsWith(canonicalDestination + File.separator)) {
          String offender = entry.getName() + " from " + archive;
          throw new IOException("Blocked unzipping files outside destination: " + offender);
        }

        if (entry.isDirectory()) {
          Files.createDirectories(entryPath);
        } else {
          if (entryPath.getParent() != null) {
            Files.createDirectories(entryPath.getParent());
          }
          try (OutputStream out = new BufferedOutputStream(Files.newOutputStream(entryPath))) {
            ByteStreams.copy(zipIn, out);
          }
        }
      }
    }
    preserveModificationTimes(destination, entries, enableReproducibleTimestamps);
  }

  /**

View on GitHub (pinned to fb949e2676)

Solutions

  1. Inspect the offending archive: the entry name after 'Blocked unzipping files outside destination:' identifies the malicious entry — replace the archive with a trusted copy.
  2. Re-download or rebuild the archive from a trusted source (verify checksums) since legitimate JARs should not contain traversal entries.
  3. Sanitize entry names before creating archives you control, ensuring they are relative and contain no '..' segments.

Example fix

// before (creating an archive)
ZipEntry entry = new ZipEntry("../outside.txt");
// after
ZipEntry entry = new ZipEntry("safe/relative/outside.txt");
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-scan archive entries for traversal before unzipping
try (ZipInputStream zis = new ZipInputStream(Files.newInputStream(archive))) {
  ZipEntry e;
  while ((e = zis.getNextEntry()) != null) {
    Path resolved = destination.resolve(e.getName()).normalize();
    if (!resolved.startsWith(destination.normalize())) {
      throw new IOException("unsafe zip entry: " + e.getName());
    }
  }
}

Try / catch

try {
  ZipUtil.unzip(archive, destination, enableRepro);
} catch (IOException e) {
  if (e.getMessage().startsWith("Blocked unzipping files outside destination")) {
    logger.error("Zip-slip blocked; the archive is untrusted: " + e.getMessage());
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: unzip processes a ZipEntry whose getName() contains '..' segments or an absolute path so that destination.resolve(entry.getName()).getCanonicalPath() does not start with canonicalDestination + File.separator.

Common situations: Extracting a maliciously or accidentally crafted archive whose entries contain '../../etc/passwd' style names; archives built on Windows with absolute entry names; build caches poisoned with tampered dependency archives.

Understand the failure class

Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.

Related errors


AI-assisted analysis of GoogleContainerTools/jib@fb949e2676 (2026-09-06). Data as JSON: /api/errors/e13b6a6e210ac849. Report an issue: GitHub.