apache/flink · critical · IOException

expanding {entry.getName()} would create entry outside of {t

Error message

expanding {entry.getName()} would create entry outside of {targetDir}

What it means

Zip-slip protection in the Java tar extraction path (unpackEntry): each entry's resolved canonical path must stay inside the target directory. Entries whose names escape via `../` or absolute paths are rejected before any file is written. This is a security guard against path traversal in malicious archives.

Source

Thrown at flink-core/src/main/java/org/apache/flink/util/CompressionUtils.java:171

        try (InputStream fi = Files.newInputStream(Paths.get(inFilePath));
                InputStream bi = new BufferedInputStream(fi);
                final TarArchiveInputStream tai =
                        new TarArchiveInputStream(
                                gzipped ? new GzipCompressorInputStream(bi) : bi)) {
            final File targetDir = new File(targetDirPath);
            TarArchiveEntry entry;
            while ((entry = tai.getNextTarEntry()) != null) {
                unpackEntry(tai, entry, targetDir);
            }
        }
    }

    private static void unpackEntry(
            TarArchiveInputStream tis, TarArchiveEntry entry, File targetDir) throws IOException {
        String targetDirPath = targetDir.getCanonicalPath() + File.separator;
        File outputFile = new File(targetDir, entry.getName());
        if (!outputFile.getCanonicalPath().startsWith(targetDirPath)) {
            throw new IOException(
                    "expanding " + entry.getName() + " would create entry outside of " + targetDir);
        }

        if (entry.isDirectory()) {
            if (!outputFile.mkdirs() && !outputFile.isDirectory()) {
                throw new IOException("Failed to create directory " + outputFile);
            }

            for (TarArchiveEntry e : entry.getDirectoryEntries()) {
                unpackEntry(tis, e, outputFile);
            }

            return;
        }

        if (entry.isSymbolicLink()) {
            // create symbolic link relative to tar parent dir
            Files.createSymbolicLink(

View on GitHub (pinned to 2f3c205e92)

Solutions

  1. Do not bypass the check — inspect the archive (`tar -tvf`) and identify the offending entry names
  2. Rebuild the archive from trusted sources without `../` or absolute entry paths
  3. Reject the input entirely if it comes from an untrusted source and fails this check

Example fix

// before: archive contains entry "../../outside.txt"
CompressionUtils.extractTarFile(bundle, targetDir); // throws

// after: sanitize at build time
tar -cvf bundle.tar -C srcDir .   # entries relative, no '../' segments
CompressionUtils.extractTarFile(bundle, targetDir);
Defensive patterns

Strategy: validation

Validate before calling

try (TarArchiveInputStream t = new TarArchiveInputStream(new FileInputStream(f))) {
    TarArchiveEntry e;
    while ((e = t.getNextTarEntry()) != null) {
        if (e.getName().startsWith("/") || e.getName().contains(".."))
            throw new IOException("Refusing unsafe entry " + e.getName());
    }
}

Try / catch

catch (IOException) and reject the archive — never catch-and-continue for traversal errors; they indicate a hostile or broken input.

Prevention

When it happens

Trigger: Extracting a tar containing entries like `../../etc/passwd`, `/etc/something`, or symlinked traversal that resolves outside the extraction root, via extractTarFileUsingJava (non-Unix isUnix()=false path, i.e. Windows, or when the Java fallback is used).

Common situations: Downloading a user-supplied or third-party tar (UDF bundles, plugins) that contains crafted relative paths; archives produced by tools that emit entries with leading `../` segments; attempts to extract a deliberately malicious archive.

Related errors


AI-assisted analysis of apache/flink@2f3c205e92 (2026-08-14). Data as JSON: /api/errors/55324f72b311c017. Report an issue: GitHub.