{"record":{"id":"6abd26d473b320b0","repo":"iBotPeaches/Apktool","slug":"path-is-null-or-empty","errorCode":null,"errorMessage":"Path is null or empty","messagePattern":"Path is null or empty","errorType":"validation","errorClass":"InvalidPathException","httpStatus":null,"severity":"warning","filePath":"brut.j.util/src/main/java/brut/util/BrutIO.java","lineNumber":103,"sourceCode":"                }\n            }\n        }\n        return modified;\n    }\n\n    public static CRC32 calculateCrc(InputStream in) throws IOException {\n        CRC32 crc = new CRC32();\n        int bytesRead;\n        byte[] buffer = new byte[8192];\n        while ((bytesRead = in.read(buffer)) != -1) {\n            crc.update(buffer, 0, bytesRead);\n        }\n        return crc;\n    }\n\n    public static String sanitizePath(File baseDir, String path) throws InvalidPathException, IOException {\n        if (path == null || path.isEmpty()) {\n            throw new InvalidPathException(path, \"Path is null or empty\");\n        }\n\n        Path origPath = Paths.get(path);\n        if (origPath.isAbsolute()) {\n            throw new InvalidPathException(path, \"Absolute paths are not allowed\");\n        }\n\n        Path basePath = Paths.get(baseDir.getCanonicalPath());\n        Path resolvedPath = basePath.resolve(origPath).normalize();\n        if (!resolvedPath.startsWith(basePath)) {\n            throw new InvalidPathException(path, \"Path traverses outside the base directory\");\n        }\n\n        return basePath.relativize(resolvedPath).toString();\n    }\n}\n","sourceCodeStart":85,"sourceCodeEnd":120,"githubUrl":"https://github.com/iBotPeaches/Apktool/blob/79b63384d7d7e22917e6ea8b453272da7012515b/brut.j.util/src/main/java/brut/util/BrutIO.java#L85-L120","documentation":"BrutIO.sanitizePath rejects a path argument that is null or empty before doing any traversal checks. It is the first validation in apktool's zip-slip defense: paths coming from archive entries must be non-empty relative paths. InvalidPathException is thrown carrying the offending path and this reason.","triggerScenarios":"Calling sanitizePath(baseDir, null) or sanitizePath(baseDir, \"\"), typically because an archive contained an entry whose name was empty, or because calling code passed an unvalidated/absent name field.","commonSituations":"Repacking archives that contain directory-only entries represented by empty names; programmatically iterating entries and passing a missing attribute; malformed zips produced by third-party packers.","solutions":["Filter entries before sanitizing: skip null/empty names (often pure directory markers)","Fix the producer side if you control archive creation so entries always have names","Guard call sites that may legitimately have no path (optional attributes) instead of passing null","Keep the other sanitizePath rules in mind too: absolute paths and traversal outside baseDir are rejected next"],"exampleFix":"// before\nfor (ZipEntry e : zipEntries) {\n    Path p = Path.of(BrutIO.sanitizePath(baseDir, e.getName())); // empty dir-entry name throws\n}\n\n// after\nfor (ZipEntry e : zipEntries) {\n    String name = e.getName();\n    if (name == null || name.isEmpty()) {\n        continue; // directory marker or malformed entry; nothing to extract\n    }\n    Path p = Path.of(BrutIO.sanitizePath(baseDir, name));\n}","handlingStrategy":"validation","validationCode":"String name = entry.getName();\nif (name == null || name.isEmpty()) {\n    continue; // skip empty/dir-marker entries before sanitizing\n}\nPath safe = Path.of(BrutIO.sanitizePath(baseDir, name));","typeGuard":"boolean isSanitizablePath(String path) {\n    return path != null && !path.isEmpty();\n}","tryCatchPattern":"try {\n    Path p = Path.of(BrutIO.sanitizePath(baseDir, name));\n} catch (InvalidPathException e) {\n    if (\"Path is null or empty\".equals(e.getReason())) {\n        // skip the entry (usually a directory marker) rather than aborting extraction\n    } else {\n        // other rejections: absolute path or traversal — log and quarantine the archive\n    }\n}","preventionTips":["Filter null/empty entry names before extraction loops","Treat sanitizePath rejections as untrusted-input signals, not crashes","Also guard the next rules: reject absolute paths and traversal outside baseDir explicitly"],"tags":["java","path-validation","zip-slip","security"],"backgroundTag":null,"analyzedSha":"79b63384d7d7e22917e6ea8b453272da7012515b","analyzedAt":"2026-08-14T10:43:28.812Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}