{"record":{"id":"d870bdcacf294650","repo":"apache/hadoop","slug":"expanding-entry-getname-would-create-ent","errorCode":null,"errorMessage":"expanding \" + entry.getName() + \" would create entry outside of \" + outputDir","messagePattern":"expanding \" \\+ entry\\.getName\\(\\) \\+ \" would create entry outside of \" \\+ outputDir","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/FileUtil.java","lineNumber":1135,"sourceCode":"      }\n      inputStream = new BufferedInputStream(inputStream);\n      tis = new TarArchiveInputStream(inputStream);\n\n      for (TarArchiveEntry entry = tis.getNextTarEntry(); entry != null;) {\n        unpackEntries(tis, entry, untarDir);\n        entry = tis.getNextTarEntry();\n      }\n    } finally {\n      IOUtils.cleanupWithLogger(LOG, tis, inputStream);\n    }\n  }\n\n  private static void unpackEntries(TarArchiveInputStream tis,\n      TarArchiveEntry entry, File outputDir) throws IOException {\n    String targetDirPath = outputDir.getCanonicalPath() + File.separator;\n    File outputFile = new File(outputDir, entry.getName());\n    if (!outputFile.getCanonicalPath().startsWith(targetDirPath)) {\n      throw new IOException(\"expanding \" + entry.getName()\n          + \" would create entry outside of \" + outputDir);\n    }\n\n    if (entry.isSymbolicLink() || entry.isLink()) {\n      String canonicalTargetPath = getCanonicalPath(entry.getLinkName(), outputDir);\n      if (!canonicalTargetPath.startsWith(targetDirPath)) {\n        throw new IOException(\n            \"expanding \" + entry.getName() + \" would create entry outside of \" + outputDir);\n      }\n    }\n\n    if (entry.isDirectory()) {\n      File subDir = new File(outputDir, entry.getName());\n      if (!subDir.mkdirs() && !subDir.isDirectory()) {\n        throw new IOException(\"Mkdirs failed to create tar internal dir \"\n            + outputDir);\n      }\n","sourceCodeStart":1117,"sourceCodeEnd":1153,"githubUrl":"https://github.com/apache/hadoop/blob/2add9630210752f88ceb1bb74eb65e37bf41da8e/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/FileUtil.java#L1117-L1153","documentation":"unpackEntries, the engine behind unTarUsingJava, resolves each tar entry to File(outputDir, entry.getName()) and throws IOException(\"expanding <entry> would create entry outside of <outputDir>\") when its canonical path does not start with outputDir's canonical path + separator. This is the tar equivalent of the Zip Slip guard: entry names with '../' or absolute components are rejected. It fires during extraction, after earlier entries may already be on disk.","triggerScenarios":"unTarUsingJava (Windows path, or fallback) on a tar containing entries like '../../bin/sh' or '/etc/ld.so.preload'; hostile tars from untrusted sources; corrupt archives with malformed name fields.","commonSituations":"Extracting third-party native bundles; CVE-style tar traversal payloads; test fixtures built with weird tools.","solutions":["Reject and quarantine the tar; log entry.getName() — this is a traversal attempt, not a transient fault","Rebuild the tar with relative, contained paths and verify checksums before deployment","Pre-scan with TarArchiveInputStream and reject entries whose resolved normalize()d path leaves outputDir","Extract unprivileged into a scratch directory to limit partial-extraction damage"],"exampleFix":"// before\nFileUtil.unTar(in, untarDir, gzipped); // Java path aborts on traversal entry\n\n// after: pre-validate names against the canonical root\nPath root = untarDir.getCanonicalFile().toPath();\ntry (TarArchiveInputStream t =\n         new TarArchiveInputStream(\n             gzipped ? new GzipCompressorInputStream(in) : in)) {\n  for (TarArchiveEntry e = t.getNextTarEntry(); e != null;\n       e = t.getNextTarEntry()) {\n    if (!root.resolve(e.getName()).normalize().startsWith(root)) {\n      throw new IOException(\"Unsafe tar entry: \" + e.getName());\n    }\n  }\n}","handlingStrategy":"try-catch","validationCode":"Path root = untarDir.getCanonicalFile().toPath();\ntry (TarArchiveInputStream t = new TarArchiveInputStream(in)) {\n  for (TarArchiveEntry e = t.getNextTarEntry(); e != null; e = t.getNextTarEntry()) {\n    if (!root.resolve(e.getName()).normalize().startsWith(root)) {\n      throw new SecurityException(\"Tar slip entry: \" + e.getName());\n    }\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  FileUtil.unTar(in, dir, gzipped);\n} catch (IOException e) {\n  if (e.getMessage() != null && e.getMessage().contains(\"outside of\")) {\n    quarantine(tarFile); // traversal attempt in entry name\n  } else throw e;\n}","preventionTips":["Only untar trusted, checksum-verified artifacts","Pre-scan names so rejection happens before any bytes are written","Extract into disposable low-privilege scratch dirs"],"tags":["zip-slip","path-traversal","security","untar","archive-extraction"],"backgroundTag":"zip-slip-path-traversal","analyzedSha":"2add9630210752f88ceb1bb74eb65e37bf41da8e","analyzedAt":"2026-08-22T19:55:07.957Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}