{"record":{"id":"d1651e72f4e509c4","repo":"apache/hadoop","slug":"expanding-entry-getname-would-create-fil-d1651e","errorCode":null,"errorMessage":"expanding \" + entry.getName() + \" would create file outside of \" + unzipDir","messagePattern":"expanding \" \\+ entry\\.getName\\(\\) \\+ \" would create file outside of \" \\+ unzipDir","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/FileUtil.java","lineNumber":841,"sourceCode":"   * @param inFile The zip file as input\n   * @param unzipDir The unzip directory where to unzip the zip file.\n   * @throws IOException An I/O exception has occurred\n   */\n  public static void unZip(File inFile, File unzipDir) throws IOException {\n    Enumeration<? extends ZipArchiveEntry> entries;\n    ZipFile zipFile = new ZipFile(inFile);\n\n    try {\n      entries = zipFile.getEntries();\n      String targetDirPath = unzipDir.getCanonicalPath() + File.separator;\n      while (entries.hasMoreElements()) {\n        ZipArchiveEntry entry = entries.nextElement();\n        if (!entry.isDirectory()) {\n          InputStream in = zipFile.getInputStream(entry);\n          try {\n            File file = new File(unzipDir, entry.getName());\n            if (!file.getCanonicalPath().startsWith(targetDirPath)) {\n              throw new IOException(\"expanding \" + entry.getName()\n                  + \" would create file outside of \" + unzipDir);\n            }\n            if (!file.getParentFile().mkdirs()) {\n              if (!file.getParentFile().isDirectory()) {\n                throw new IOException(\"Mkdirs failed to create \" +\n                                      file.getParentFile().toString());\n              }\n            }\n            OutputStream out = Files.newOutputStream(file.toPath());\n            try {\n              byte[] buffer = new byte[8192];\n              int i;\n              while ((i = in.read(buffer)) != -1) {\n                out.write(buffer, 0, i);\n              }\n            } finally {\n              out.close();\n            }","sourceCodeStart":823,"sourceCodeEnd":859,"githubUrl":"https://github.com/apache/hadoop/blob/2add9630210752f88ceb1bb74eb65e37bf41da8e/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/FileUtil.java#L823-L859","documentation":"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.","triggerScenarios":"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.","commonSituations":"Downloading plugin/connector zips and extracting on the node; accepting user-supplied archives; mirrored artifacts tampered in transit or by a compromised upstream.","solutions":["Quarantine and reject the archive; capture entry.getName() in logs for triage — this is attempted path traversal, treat it as a security event","Rebuild or re-download the artifact from a trusted source and verify a checksum/signature before unzipping","Pre-validate entries (canonical-path containment check) before calling unZip so rejection is atomic","Extract as an unprivileged user into a scratch dir to limit damage from partially-extracted content"],"exampleFix":"// before\nFileUtil.unZip(new File(\"plugin.zip\"), pluginDir); // aborts mid-archive\n\n// after: fail fast on the first bad entry, before anything is written\nPath root = pluginDir.getCanonicalFile().toPath();\ntry (ZipFile zf = new ZipFile(new File(\"plugin.zip\"))) {\n  Enumeration<ZipArchiveEntry> en = zf.getEntries();\n  while (en.hasMoreElements()) {\n    Path resolved = root.resolve(en.nextElement().getName()).normalize();\n    if (!resolved.startsWith(root)) {\n      throw new IOException(\"Unsafe zip entry rejected\");\n    }\n  }\n}\nFileUtil.unZip(new File(\"plugin.zip\"), pluginDir);","handlingStrategy":"try-catch","validationCode":"Path root = unzipDir.getCanonicalFile().toPath();\ntry (ZipFile zf = new ZipFile(inFile)) {\n  Enumeration<ZipArchiveEntry> en = zf.getEntries();\n  while (en.hasMoreElements()) {\n    if (!root.resolve(en.nextElement().getName()).normalize().startsWith(root)) {\n      throw new SecurityException(\"Unsafe zip entry\");\n    }\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  FileUtil.unZip(inFile, unzipDir);\n} catch (IOException e) {\n  if (e.getMessage() != null && e.getMessage().contains(\"outside of\")) {\n    rejectArchive(inFile); // security event: traversal attempt\n  } else throw e;\n}","preventionTips":["Verify checksums/signatures of downloaded zips before extraction","Treat 'would create file outside of' as a security signal, never sanitize and continue","Run extraction unprivileged into disposable directories"],"tags":["zip-slip","path-traversal","security","archive-extraction","unzip"],"backgroundTag":"zip-slip-path-traversal","analyzedSha":"2add9630210752f88ceb1bb74eb65e37bf41da8e","analyzedAt":"2026-08-22T19:55:07.957Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}