apache/flink · critical · IOException
Expand {entry.getName()} would create a file outside of {tar
Error message
Expand {entry.getName()} would create a file outside of {targetPath} What it means
Zip-slip protection in extractZipFileWithPermissions: each zip entry's canonical output path must start with the canonical target directory. A zip entry using absolute paths or `../` segments that resolves outside the target is rejected. This is the zip counterpart of the tar traversal guard.
Source
Thrown at flink-core/src/main/java/org/apache/flink/util/CompressionUtils.java:226
* attacks.
*/
private static String makeSecureShellPath(String filePath) {
return filePath.replace("'", "'\\''");
}
public static void extractZipFileWithPermissions(String zipFilePath, String targetPath)
throws IOException {
try (ZipFile zipFile = new ZipFile(zipFilePath)) {
Enumeration<ZipArchiveEntry> entries = zipFile.getEntries();
boolean isUnix = isUnix();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
String canonicalTargetPath = new File(targetPath).getCanonicalPath() + File.separator;
while (entries.hasMoreElements()) {
ZipArchiveEntry entry = entries.nextElement();
File outputFile = new File(canonicalTargetPath, entry.getName());
if (!outputFile.getCanonicalPath().startsWith(canonicalTargetPath)) {
throw new IOException(
"Expand "
+ entry.getName()
+ " would create a file outside of "
+ targetPath);
}
if (entry.isDirectory()) {
if (!outputFile.exists()) {
if (!outputFile.mkdirs()) {
throw new IOException(
"Create dir: " + outputFile.getAbsolutePath() + " failed!");
}
}
} else {
File parentDir = outputFile.getParentFile();
if (!parentDir.exists()) {
if (!parentDir.mkdirs()) {
throw new IOException(View on GitHub (pinned to 2f3c205e92)
Solutions
- Inspect the offending entry: `unzip -l archive.zip` and look for absolute or `../` prefixed names
- Rebuild the zip from trusted content with relative entry names
- Treat the archive as untrusted input and reject it; never relax the check
Example fix
// before
CompressionUtils.extractZipFileWithPermissions(untrustedZip, target);
// after: validate entries first
try (ZipFile z = new ZipFile(untrustedZip)) {
z.getEntries().asIterator().forEachRemaining(e -> {
if (e.getName().startsWith("/") || e.getName().contains(".."))
throw new IOException("Illegal entry " + e.getName());
});
}
CompressionUtils.extractZipFileWithPermissions(untrustedZip, target); Defensive patterns
Strategy: validation
Validate before calling
try (ZipFile z = new ZipFile(zipPath)) {
Enumeration<ZipArchiveEntry> en = z.getEntries();
while (en.hasMoreElements()) {
String n = en.nextElement().getName();
if (n.startsWith("/") || n.contains("..")) throw new IOException("Unsafe zip entry " + n);
}
} Try / catch
catch (IOException) and permanently reject the archive; log the entry name for audit — traversal is a security signal, not a transient error.
Prevention
- Treat archives from users/network as untrusted; validate entries before extraction
- Verify signatures/checksums of downloaded zips and jars before unpacking
When it happens
Trigger: Extracting a zip/jar containing entries such as `../evil.class` or `/etc/evil` — common in maliciously crafted archives or in zips produced by tools that store absolute member names. Reached when the file suffix is not tar-ish (zip/jar/unknown suffix fallback).
Common situations: Extracting untrusted user JARs or plugin bundles; zips created with absolute paths (some archive tools do this); penetration testing or an actual supply-chain attempt.
Related errors
- expanding {entry.getName()} would create entry outside of {t
- Create dir: {outputFile.getAbsolutePath()} failed!
- Create file: {outputFile.getAbsolutePath()} failed!
- Could not parse value for key '%s'.
- Could not register security manager due to no permission to
AI-assisted analysis of apache/flink@2f3c205e92 (2026-08-14).
Data as JSON: /api/errors/b77d529a67a57140.
Report an issue: GitHub.