apache/hadoop · error · IOException
expanding " + entry.getName() + " would create file outside
Error message
expanding " + entry.getName() + " would create file outside of " + toDir
What it means
unZip(InputStream, File toDir) throws IOException("expanding <entry> would create file outside of <toDir>") when new File(toDir, entry.getName()).getCanonicalPath() does not start with toDir's canonical path plus File.separator. This is the Zip Slip defense: a archive entry containing '../' segments, an absolute path, or a symlink trick would otherwise write outside the extraction directory. Seeing it means the zip is malicious or corrupt and the guard correctly aborted extraction.
Source
Thrown at hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/FileUtil.java:750
/**
* Given a stream input it will unzip the it in the unzip directory.
* passed as the second parameter
* @param inputStream The zip file as input
* @param toDir The unzip directory where to unzip the zip file.
* @throws IOException an exception occurred
*/
public static void unZip(InputStream inputStream, File toDir)
throws IOException {
try (ZipArchiveInputStream zip = new ZipArchiveInputStream(inputStream)) {
int numOfFailedLastModifiedSet = 0;
String targetDirPath = toDir.getCanonicalPath() + File.separator;
for(ZipArchiveEntry entry = zip.getNextZipEntry();
entry != null;
entry = zip.getNextZipEntry()) {
if (!entry.isDirectory()) {
File file = new File(toDir, entry.getName());
if (!file.getCanonicalPath().startsWith(targetDirPath)) {
throw new IOException("expanding " + entry.getName()
+ " would create file outside of " + toDir);
}
File parent = file.getParentFile();
if (!parent.mkdirs() &&
!parent.isDirectory()) {
throw new IOException("Mkdirs failed to create " +
parent.getAbsolutePath());
}
try (OutputStream out = Files.newOutputStream(file.toPath())) {
IOUtils.copyBytes(zip, out, BUFFER_SIZE);
}
if (!file.setLastModified(entry.getTime())) {
numOfFailedLastModifiedSet++;
}
if (entry.getPlatform() == ZipArchiveEntry.PLATFORM_UNIX) {
Files.setPosixFilePermissions(file.toPath(), permissionsFromMode(entry.getUnixMode()));
}
}View on GitHub (pinned to 2add963021)
Solutions
- Treat the archive as untrusted: reject it, log entry.getName(), and alert — do not try to 'fix' the entry by stripping '../' silently
- Re-create the artifact with a trusted archiver (relative entry names, no absolute components) and redeploy
- Pre-scan entries with ZipArchiveInputStream and reject any whose resolved path leaves the target dir before calling unZip
- Extract into a dedicated, low-privilege directory so even a slipped write has minimal blast radius
Example fix
// before
FileUtil.unZip(uploaded, toDir); // aborts: entry escapes toDir
// after: validate before extracting
try (ZipArchiveInputStream z = new ZipArchiveInputStream(Files.newInputStream(uploaded.toPath()))) {
String root = toDir.getCanonicalPath() + File.separator;
for (ZipArchiveEntry e = z.getNextZipEntry(); e != null; e = z.getNextZipEntry()) {
if (!new File(toDir, e.getName()).getCanonicalPath().startsWith(root)) {
throw new IOException("Rejected unsafe entry " + e.getName());
}
}
}
FileUtil.unZip(uploaded, toDir); Defensive patterns
Strategy: try-catch
Validate before calling
String root = toDir.getCanonicalPath() + File.separator;
try (ZipArchiveInputStream z = new ZipArchiveInputStream(input)) {
for (ZipArchiveEntry e = z.getNextZipEntry(); e != null; e = z.getNextZipEntry()) {
if (!new File(toDir, e.getName()).getCanonicalPath().startsWith(root)) {
throw new SecurityException("Zip slip entry: " + e.getName());
}
}
} Try / catch
try {
FileUtil.unZip(in, toDir);
} catch (IOException e) {
if (e.getMessage() != null && e.getMessage().contains("outside of")) {
// malicious/corrupt archive: quarantine it, alert, do NOT sanitize automatically
quarantine(archiveFile);
} else throw e;
} Prevention
- Only extract archives from trusted, checksum-verified sources
- Extract into a dedicated scratch directory with least privilege
- Pre-scan entry names before extraction so rejection is atomic, since unZip aborts midway
When it happens
Trigger: unZip() on a crafted zip with entries like '../../etc/passwd' or absolute names like '/etc/cron.d/x'; corrupted archives whose entry names contain path separators at odd places; archives produced by tools emitting entries with leading '../' normalization hazards.
Common situations: Processing user-uploaded or third-party bundles (connectors, plugins, nightly artifacts); supply-chain style zip-slip attack payloads; regression after switching archive producers.
Related errors
- expanding " + entry.getName() + " would create file outside
- expanding " + entry.getName() + " would create entry outside
- expanding {} would create file outside of {}
- Mkdirs failed to create " + parent.getAbsolutePath()
- Mkdirs failed to create " + file.getParentFile().toString()
AI-assisted analysis of apache/hadoop@2add963021 (2026-08-22).
Data as JSON: /api/errors/977f821520e868a0.
Report an issue: GitHub.