{"record":{"id":"cfe9028fa1bd447b","repo":"MuntashirAkon/AppManager","slug":"zip-slip-vulnerability-detected-expected-dest-new-file-cfe902","errorCode":null,"errorMessage":"Zip slip vulnerability detected!\nExpected dest: ${new File(realDestPath, entry.getName())}\nActual path: ${realFilePath}","messagePattern":"Zip slip vulnerability detected!\nExpected dest: (.+?)\nActual path: (.+?)","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"critical","filePath":"app/src/main/java/io/github/muntashirakon/AppManager/utils/TarUtils.java","lineNumber":195,"sourceCode":"                            continue;\n                        }\n                        String linkName = entry.getLinkName();\n                        // There's no need to check if the linkName exists as it may be extracted\n                        // after the link has been created\n                        // Special check for /data/app\n                        if (linkName.startsWith(\"/data/app/\")) {\n                            linkName = getAbsolutePathToDataApp(linkName, realDataAppPath);\n                        }\n                        file.delete();\n                        if (!file.createNewSymbolicLink(linkName)) {\n                            throw new IOException(\"Couldn't create symbolic link \" + file + \" pointing to \" + linkName);\n                        }\n                        continue;  // links do not need permission fixes\n                    } else {\n                        // Zip slip vulnerability might still be present\n                        String realFilePath = file.getRealFilePath();\n                        if (realDestPath != null && realFilePath != null && !realFilePath.startsWith(realDestPath)) {\n                            throw new IOException(\"Zip slip vulnerability detected!\" +\n                                    \"\\nExpected dest: \" + new File(realDestPath, entry.getName()) +\n                                    \"\\nActual path: \" + realFilePath);\n                        }\n                        if (!entry.isDirectory()) {\n                            try (OutputStream os = file.openOutputStream()) {\n                                IoUtils.copy(tis, os);\n                            }\n                        }\n                    }\n                    // Fix permissions\n                    TarArchiveEntry finalEntry = entry;\n                    ExUtils.exceptionAsIgnored(() -> Paths.setPermissions(file, finalEntry.getMode(),\n                            finalEntry.getUserId(), finalEntry.getGroupId()));\n                    // Restore timestamp\n                    long modificationTime = entry.getModTime().getTime();\n                    if (modificationTime > 0) { // Backward-compatibility\n                        file.setLastModified(entry.getModTime().getTime());\n                    }","sourceCodeStart":177,"sourceCodeEnd":213,"githubUrl":"https://github.com/MuntashirAkon/AppManager/blob/0152f468fc9463ee02dc2ca83f6fe4989a2c4ca5/app/src/main/java/io/github/muntashirakon/AppManager/utils/TarUtils.java#L177-L213","documentation":"After writing regular (non-link) entries, extract re-verifies the resolved real path: file.getRealFilePath() must still be inside realDestPath. If a symlink created earlier in the same archive (or pre-existing on disk) redirects the path outside the destination, it throws IOException(\"Zip slip vulnerability detected!...\") with the expected vs. actual path. This is the second, symlink-aware layer of the zip-slip defense beyond the early name check (error 417).","triggerScenarios":"Extracting an archive where a symlink entry established a path leading outside the destination and a subsequent entry resolves through it; archives containing symlink-to-parent chains like 'link -> .' plus 'link/../escape'.","commonSituations":"Maliciously crafted archives designed to escape the extraction directory via links; archives that passed the naive name check (no '..' in entry names) but exploit filesystem-level resolution; re-extracting into a directory containing stale symlinks.","solutions":["Reject archives containing symlink entries whose targets resolve outside the destination, before extraction","Extract into a fresh, empty destination directory to avoid pre-existing symlink traps","Treat this exception as evidence of a malicious archive: delete all partially extracted files and quarantine the input","Catch IOException and detect the 'Zip slip' message to apply security-specific handling"],"exampleFix":"// before\nTarUtils.extract(tarIn, destDir, ...);\n// after\ntry {\n    TarUtils.extract(tarIn, destDir, ...);\n} catch (IOException e) {\n    if (e.getMessage().contains(\"Zip slip\")) {\n        FileUtils.deleteDir(destDir);\n        throw new SecurityException(\"Malicious archive blocked: \" + e.getMessage(), e);\n    }\n    throw e;\n}","handlingStrategy":"try-catch","validationCode":"// Reject symlink entries whose targets escape the destination before extraction\nif (entry.isSymbolicLink() && !new File(destDir, linkTarget).getCanonicalPath().startsWith(destDir.getCanonicalPath())) {\n    throw new SecurityException(\"Unsafe symlink: \" + linkTarget);\n}","typeGuard":"boolean symlinkStaysInDir(File dest, String linkTarget) throws IOException {\n    return new File(dest, linkTarget).getCanonicalPath().startsWith(dest.getCanonicalPath() + File.separator);\n}","tryCatchPattern":"try {\n    TarUtils.extract(in, dest, filters);\n} catch (IOException e) {\n    if (e.getMessage().contains(\"Zip slip\")) {\n        FileUtils.deleteDir(dest);\n        throw new SecurityException(\"Malicious archive blocked: \" + e.getMessage(), e);\n    }\n    throw e;\n}","preventionTips":["Never extract into a directory that already contains symlinks","Treat any archive containing outbound symlinks as untrusted and inspect it first","Clean up partially extracted files when this error fires — the archive is malicious or corrupted"],"tags":["security","zip-slip","symlink","path-traversal","archive"],"backgroundTag":"path-traversal-blocked","analyzedSha":"0152f468fc9463ee02dc2ca83f6fe4989a2c4ca5","analyzedAt":"2026-09-12T14:03:37.243Z","contentChangedAt":"2026-09-12T14:03:37.243Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}