apache/hadoop · error · IOException

expanding " + entry.getName() + " would create file outside

Error message

expanding " + entry.getName() + " would create file outside of " + unzipDir

What it means

unZip(File inFile, File unzipDir) (the ZipFile-based variant reading from a file rather than a stream) throws IOException("expanding <entry> would create file outside of <unzipDir>") when an entry's canonical path escapes the target directory. Same Zip Slip guard as the stream variant: entry names with '../' segments or absolute paths are rejected before any bytes are written. The abort happens lazily, mid-iteration, so earlier entries may already be extracted.

Source

Thrown at hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/FileUtil.java:841

   * @param inFile The zip file as input
   * @param unzipDir The unzip directory where to unzip the zip file.
   * @throws IOException An I/O exception has occurred
   */
  public static void unZip(File inFile, File unzipDir) throws IOException {
    Enumeration<? extends ZipArchiveEntry> entries;
    ZipFile zipFile = new ZipFile(inFile);

    try {
      entries = zipFile.getEntries();
      String targetDirPath = unzipDir.getCanonicalPath() + File.separator;
      while (entries.hasMoreElements()) {
        ZipArchiveEntry entry = entries.nextElement();
        if (!entry.isDirectory()) {
          InputStream in = zipFile.getInputStream(entry);
          try {
            File file = new File(unzipDir, entry.getName());
            if (!file.getCanonicalPath().startsWith(targetDirPath)) {
              throw new IOException("expanding " + entry.getName()
                  + " would create file outside of " + unzipDir);
            }
            if (!file.getParentFile().mkdirs()) {
              if (!file.getParentFile().isDirectory()) {
                throw new IOException("Mkdirs failed to create " +
                                      file.getParentFile().toString());
              }
            }
            OutputStream out = Files.newOutputStream(file.toPath());
            try {
              byte[] buffer = new byte[8192];
              int i;
              while ((i = in.read(buffer)) != -1) {
                out.write(buffer, 0, i);
              }
            } finally {
              out.close();
            }

View on GitHub (pinned to 2add963021)

Solutions

  1. Quarantine and reject the archive; capture entry.getName() in logs for triage — this is attempted path traversal, treat it as a security event
  2. Rebuild or re-download the artifact from a trusted source and verify a checksum/signature before unzipping
  3. Pre-validate entries (canonical-path containment check) before calling unZip so rejection is atomic
  4. Extract as an unprivileged user into a scratch dir to limit damage from partially-extracted content

Example fix

// before
FileUtil.unZip(new File("plugin.zip"), pluginDir); // aborts mid-archive

// after: fail fast on the first bad entry, before anything is written
Path root = pluginDir.getCanonicalFile().toPath();
try (ZipFile zf = new ZipFile(new File("plugin.zip"))) {
  Enumeration<ZipArchiveEntry> en = zf.getEntries();
  while (en.hasMoreElements()) {
    Path resolved = root.resolve(en.nextElement().getName()).normalize();
    if (!resolved.startsWith(root)) {
      throw new IOException("Unsafe zip entry rejected");
    }
  }
}
FileUtil.unZip(new File("plugin.zip"), pluginDir);
Defensive patterns

Strategy: try-catch

Validate before calling

Path root = unzipDir.getCanonicalFile().toPath();
try (ZipFile zf = new ZipFile(inFile)) {
  Enumeration<ZipArchiveEntry> en = zf.getEntries();
  while (en.hasMoreElements()) {
    if (!root.resolve(en.nextElement().getName()).normalize().startsWith(root)) {
      throw new SecurityException("Unsafe zip entry");
    }
  }
}

Try / catch

try {
  FileUtil.unZip(inFile, unzipDir);
} catch (IOException e) {
  if (e.getMessage() != null && e.getMessage().contains("outside of")) {
    rejectArchive(inFile); // security event: traversal attempt
  } else throw e;
}

Prevention

When it happens

Trigger: unZip(new File("bundle.zip"), unzipDir) where bundle.zip contains entries like '../../../sbin/install' or absolute paths; hostile or corrupted third-party archives fed to the file-based API.

Common situations: Downloading plugin/connector zips and extracting on the node; accepting user-supplied archives; mirrored artifacts tampered in transit or by a compromised upstream.

Related errors


AI-assisted analysis of apache/hadoop@2add963021 (2026-08-22). Data as JSON: /api/errors/d1651e72f4e509c4. Report an issue: GitHub.