GoogleContainerTools/jib · critical · IOException

Blocked unzipping files outside destination: ${offender}

Error message

Blocked unzipping files outside destination: ${offender}

What it means

TarExtractor blocks extracting a tar entry whose canonical resolved path falls outside the destination directory, a Zip-Slip/path-traversal defense. A maliciously crafted tar (entry names like ../../etc/passwd) must not write files outside the extraction root, so Jib throws an IOException naming the offender.

Source

Thrown at jib-core/src/main/java/com/google/cloud/tools/jib/tar/TarExtractor.java:83

        && 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<TarArchiveEntry> entries = new ArrayList<>();
    try (InputStream in = new BufferedInputStream(Files.newInputStream(source));
        TarArchiveInputStream tarArchiveInputStream = new TarArchiveInputStream(in)) {
      for (TarArchiveEntry entry = tarArchiveInputStream.getNextEntry();
          entry != null;
          entry = tarArchiveInputStream.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 " + source;
          throw new IOException("Blocked unzipping files outside destination: " + offender);
        }
        if (entry.isDirectory()) {
          Files.createDirectories(entryPath);
        } else {
          if (entryPath.getParent() != null) {
            Files.createDirectories(entryPath.getParent());
          }

          if (entry.isSymbolicLink()) {
            Files.createSymbolicLink(entryPath, Paths.get(entry.getLinkName()));
          } else {
            try (OutputStream out = new BufferedOutputStream(Files.newOutputStream(entryPath))) {
              ByteStreams.copy(tarArchiveInputStream, out);
            }
          }
        }
      }
    }

View on GitHub (pinned to fb949e2676)

Solutions

  1. Inspect the tar entries (tar -tf) and rebuild the archive with relative, non-escaping entry names.
  2. Only extract tars from trusted sources.
  3. Verify entry.getName() contains no leading '/' or '..' segments before extraction.
  4. Use the offender path in the message to locate and remove the malicious entry.

Example fix

// before: tar entries like '../../etc/evil'
TarArchiveOutputStream out = ...; out.putArchiveEntry(new TarArchiveEntry("../../etc/evil"));
// after
out.putArchiveEntry(new TarArchiveEntry("app/etc/evil")); // stays inside destination
Defensive patterns

Strategy: validation

Validate before calling

// Pre-scan tar entries for traversal before extraction
try (TarArchiveInputStream in = new TarArchiveInputStream(new BufferedInputStream(Files.newInputStream(tar)))) {
  TarArchiveEntry e;
  while ((e = in.getNextTarEntry()) != null) {
    if (e.getName().startsWith("/") || e.getName().contains("..")) throw new IOException("Unsafe entry: " + e.getName());
  }
}

Try / catch

try { TarExtractor.extract(tar, dest, false); } catch (IOException e) { if (e.getMessage().startsWith("Blocked unzipping")) { logger.error("Rejected malicious tar: {}", e.getMessage()); } else { throw e; } }

Prevention

When it happens

Trigger: Tar contains an entry whose name escapes destination after canonicalization (../ traversal, absolute paths, symlink tricks); canonicalTarget does not start with canonicalDestination + File.separator.

Common situations: Extracting untrusted or third-party tars/layers; tar built with entries containing '../'; relocated/reused layer tars from an unknown source.

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/07c4ee1adc95a370. Report an issue: GitHub.