apache/flink · error · IOException
Illegal escape from target directory
Error message
Illegal escape from target directory
What it means
FileUtils (unzip/expand of an archive into a target directory) is a Zip-Slip guard: for each ZipEntry it builds targetDirectory/entryName and verifies the result still starts with the target directory's path string. If an entry name like '../../etc/passwd' or an absolute path resolves outside the target, it throws IOException("Illegal escape from target directory") to prevent the archive from overwriting arbitrary files.
Source
Thrown at flink-core/src/main/java/org/apache/flink/util/FileUtils.java:537
* @throws IOException if file open fails or in case of unsafe access outside target directory
*/
public static Path expandDirectory(Path file, Path targetDirectory) throws IOException {
FileSystem sourceFs = file.getFileSystem();
FileSystem targetFs = targetDirectory.getFileSystem();
Path rootDir = null;
try (ZipInputStream zis = new ZipInputStream(sourceFs.open(file))) {
ZipEntry entry;
String targetDirStr = targetDirectory.toString();
while ((entry = zis.getNextEntry()) != null) {
Path relativePath = new Path(entry.getName());
if (rootDir == null) {
// the first entry contains the name of the original directory that was zipped
rootDir = relativePath;
}
Path newFile = new Path(targetDirectory, relativePath);
if (!newFile.toString().startsWith(targetDirStr)) {
throw new IOException("Illegal escape from target directory");
}
if (entry.isDirectory()) {
targetFs.mkdirs(newFile);
} else {
try (FSDataOutputStream fileStream =
targetFs.create(newFile, FileSystem.WriteMode.NO_OVERWRITE)) {
// do not close the streams here as it prevents access to further zip
// entries
IOUtils.copyBytes(zis, fileStream, false);
}
}
zis.closeEntry();
}
}
return new Path(targetDirectory, rootDir);
}
View on GitHub (pinned to 2f3c205e92)
Solutions
- Inspect the archive with `unzip -l` or `jar tf` and look for entries starting with '/', '..' or drive letters; reject or re-create the archive
- Re-package the zip from a trusted source so all entry names are relative paths under the intended root
- If you control the producer, fix it to write entries with normalized relative names (no leading slash, no '..' segments)
Example fix
// producing safe entries when zipping
// before
zos.putNextEntry(new ZipEntry("/abs/or/../escaping/name"));
// after
zos.putNextEntry(new ZipEntry("rel/dir/name")); Defensive patterns
Strategy: validation
Validate before calling
static void assertSafeEntries(Path zip) throws IOException {
try (ZipInputStream zis = new ZipInputStream(Files.newInputStream(zip))) {
for (ZipEntry e; (e = zis.getNextEntry()) != null; ) {
String n = e.getName();
if (n.startsWith("/") || n.contains("..")) throw new IOException("Unsafe entry: " + n);
}
}
} Try / catch
try { FileUtils.unzip(...) } catch (IOException e) { if (e.getMessage().contains("Illegal escape")) rejectArchive(); else throw e; } Prevention
- Only extract archives from trusted, checksum-verified sources
- Scan entry names for '..', leading '/', and drive letters before extraction
- Extract into a dedicated, low-privilege directory so even an escape is contained
When it happens
Trigger: Unzipping a maliciously crafted or corrupted archive whose entry names contain '../' segments, rooted/absolute paths, or drive letters that make new Path(targetDirectory, relativePath) escape targetDirStr; can also fire on benign-but-odd archives where the string startsWith check fails for the normalized path.
Common situations: Deploying user-supplied job JARs/bundles or community distribution archives; downloading a partially-corrupted zip; a zip created by a tool that emits entries with leading '/' or Windows-style components.
Related errors
- expanding {entry.getName()} would create entry outside of {t
- Expand {entry.getName()} would create a file outside of {tar
- URL is invalid. This should not happen.
- Could not look up the main(String[]) method from the class %
- An I/O error occurred while creating temporary file to extra
AI-assisted analysis of apache/flink@2f3c205e92 (2026-08-14).
Data as JSON: /api/errors/794fdb27229997c8.
Report an issue: GitHub.