{"record":{"id":"c7745ea48385393c","repo":"alibaba/arthas","slug":"bad-zip-entry-currententry","errorCode":null,"errorMessage":"Bad zip entry: {currentEntry}","messagePattern":"Bad zip entry: (.+?)","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"common/src/main/java/com/taobao/arthas/common/IOUtils.java","lineNumber":125,"sourceCode":"        ZipFile zip = null;\n        try {\n            int BUFFER = 1024 * 8;\n\n            zip = new ZipFile(file);\n            File newPath = new File(extractFolder);\n            newPath.mkdirs();\n\n            Enumeration<? extends ZipEntry> zipFileEntries = zip.entries();\n\n            // Process each entry\n            while (zipFileEntries.hasMoreElements()) {\n                // grab a zip file entry\n                ZipEntry entry = (ZipEntry) zipFileEntries.nextElement();\n                String currentEntry = entry.getName();\n\n                File destFile = new File(newPath, currentEntry);\n                if (!isSubFile(newPath, destFile)) {\n                    throw new IOException(\"Bad zip entry: \" + currentEntry);\n                }\n\n                // destFile = new File(newPath, destFile.getName());\n                File destinationParent = destFile.getParentFile();\n\n                // create the parent directory structure if needed\n                destinationParent.mkdirs();\n\n                if (!entry.isDirectory()) {\n                    BufferedInputStream is = null;\n                    BufferedOutputStream dest = null;\n                    try {\n                        is = new BufferedInputStream(zip.getInputStream(entry));\n                        int currentByte;\n                        // establish buffer for writing file\n                        byte data[] = new byte[BUFFER];\n\n                        // write the current file to disk","sourceCodeStart":107,"sourceCodeEnd":143,"githubUrl":"https://github.com/alibaba/arthas/blob/21cf2e9ba52b305290be7223b980ff504bb9cb5b/common/src/main/java/com/taobao/arthas/common/IOUtils.java#L107-L143","documentation":"IOUtils.ungzip/unzip routine extracts each ZipEntry under a base directory newPath. Before writing, it checks isSubFile(newPath, destFile) — a path-traversal (zip-slip) guard ensuring the resolved destination stays inside newPath. If an entry resolves outside the base (e.g. ../ escapes or absolute paths), it throws IOException(\"Bad zip entry: <currentEntry>\").","triggerScenarios":"Extracting a maliciously or accidentally crafted archive whose entry names contain '..' segments or absolute paths that would escape the target directory (classic zip-slip).","commonSituations":"Downloading an Arthas distribution/archive from an untrusted or tampered source; a re-packaged archive with entry names like ../../etc/passwd; an archiver that emitted absolute entry names.","solutions":["Only extract archives from trusted, checksum-verified sources.","If you control packaging, ensure entry names are relative with no '..' segments.","Catch IOException and abort the extraction, deleting partial output.","Run extraction in a sandboxed/throwaway directory so escapes are contained."],"exampleFix":"// before - blindly trust upstream archive\nIOUtils.unzip(downloadedZip, targetDir);  // entry '../evil' -> throws Bad zip entry\n\n// after - verify source + isolate\nif (!checksumMatches(downloadedZip)) throw new IOException(\"untrusted archive\");\nFile sandbox = Files.createTempDirectory(\"unzip\").toFile();\ntry {\n    IOUtils.unzip(downloadedZip, sandbox);\n} catch (IOException e) {\n    FileUtils.deleteQuietly(sandbox);\n    throw e;\n}","handlingStrategy":"validation","validationCode":"File base = newPath.getCanonicalFile();\nfor (ZipEntry e : Collections.list(zip.entries())) {\n    File dest = new File(newPath, e.getName()).getCanonicalFile();\n    if (!dest.toPath().startsWith(base.toPath())) {\n        throw new IOException(\"refusing zip-slip entry: \" + e.getName());\n    }\n}","typeGuard":"static boolean allEntriesInside(ZipFile zip, File base) throws IOException {\n    Path bp = base.getCanonicalFile().toPath();\n    Enumeration<? extends ZipEntry> en = zip.entries();\n    while (en.hasMoreElements()) {\n        Path dp = new File(base, en.nextElement().getName()).getCanonicalFile().toPath();\n        if (!dp.startsWith(bp)) return false;\n    }\n    return true;\n}","tryCatchPattern":"try {\n    IOUtils.unzip(zip, targetDir);\n} catch (IOException e) {\n    if (e.getMessage().startsWith(\"Bad zip entry\")) {\n        FileUtils.deleteQuietly(targetDir); // discard partial extraction\n        throw new SecurityException(\"rejected malicious archive\", e);\n    }\n    throw e;\n}","preventionTips":["Extract only verified archives from trusted sources (checksum/signature).","Extract into a throwaway sandbox directory and move validated contents out.","Inspect entry names for '..' or absolute paths before extracting.","Catch the IOException and purge partial output to avoid leaving attacker-controlled files."],"tags":["security","zip-slip","filesystem","io","common"],"backgroundTag":null,"analyzedSha":"21cf2e9ba52b305290be7223b980ff504bb9cb5b","analyzedAt":"2026-08-14T00:57:07.243Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}