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
- Inspect the tar entries (tar -tf) and rebuild the archive with relative, non-escaping entry names.
- Only extract tars from trusted sources.
- Verify entry.getName() contains no leading '/' or '..' segments before extraction.
- 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
- Only extract tars from trusted sources.
- Never build entries with '..' or absolute-path names.
- Scan archives with security tooling (e.g. tar -tf) from untrusted parties.
- Keep Jib updated for current Zip-Slip mitigations.
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
- Blocked unzipping files outside destination: ${entryName} fr
- Missing option: --name must be specified when using --target
- No or multiple layer files found for layer hash <diffId> in
- SHA-256 algorithm implementation not found - might be a brok
- SHA-256 algorithm produced invalid hash: + ex.getMessage()
AI-assisted analysis of GoogleContainerTools/jib@fb949e2676 (2026-09-06).
Data as JSON: /api/errors/07c4ee1adc95a370.
Report an issue: GitHub.