MuntashirAkon/AppManager · critical · IOException
Zip slip vulnerability detected! Expected dest: ${new File(r
Error message
Zip slip vulnerability detected!
Expected dest: ${new File(realDestPath, entry.getName())}
Actual path: ${realFilePath} What it means
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).
Source
Thrown at app/src/main/java/io/github/muntashirakon/AppManager/utils/TarUtils.java:195
continue;
}
String linkName = entry.getLinkName();
// There's no need to check if the linkName exists as it may be extracted
// after the link has been created
// Special check for /data/app
if (linkName.startsWith("/data/app/")) {
linkName = getAbsolutePathToDataApp(linkName, realDataAppPath);
}
file.delete();
if (!file.createNewSymbolicLink(linkName)) {
throw new IOException("Couldn't create symbolic link " + file + " pointing to " + linkName);
}
continue; // links do not need permission fixes
} else {
// Zip slip vulnerability might still be present
String realFilePath = file.getRealFilePath();
if (realDestPath != null && realFilePath != null && !realFilePath.startsWith(realDestPath)) {
throw new IOException("Zip slip vulnerability detected!" +
"\nExpected dest: " + new File(realDestPath, entry.getName()) +
"\nActual path: " + realFilePath);
}
if (!entry.isDirectory()) {
try (OutputStream os = file.openOutputStream()) {
IoUtils.copy(tis, os);
}
}
}
// Fix permissions
TarArchiveEntry finalEntry = entry;
ExUtils.exceptionAsIgnored(() -> Paths.setPermissions(file, finalEntry.getMode(),
finalEntry.getUserId(), finalEntry.getGroupId()));
// Restore timestamp
long modificationTime = entry.getModTime().getTime();
if (modificationTime > 0) { // Backward-compatibility
file.setLastModified(entry.getModTime().getTime());
}View on GitHub (pinned to 0152f468fc)
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
Example fix
// before
TarUtils.extract(tarIn, destDir, ...);
// after
try {
TarUtils.extract(tarIn, destDir, ...);
} catch (IOException e) {
if (e.getMessage().contains("Zip slip")) {
FileUtils.deleteDir(destDir);
throw new SecurityException("Malicious archive blocked: " + e.getMessage(), e);
}
throw e;
} Defensive patterns
Strategy: try-catch
Validate before calling
// Reject symlink entries whose targets escape the destination before extraction
if (entry.isSymbolicLink() && !new File(destDir, linkTarget).getCanonicalPath().startsWith(destDir.getCanonicalPath())) {
throw new SecurityException("Unsafe symlink: " + linkTarget);
} Type guard
boolean symlinkStaysInDir(File dest, String linkTarget) throws IOException {
return new File(dest, linkTarget).getCanonicalPath().startsWith(dest.getCanonicalPath() + File.separator);
} Try / catch
try {
TarUtils.extract(in, dest, filters);
} catch (IOException e) {
if (e.getMessage().contains("Zip slip")) {
FileUtils.deleteDir(dest);
throw new SecurityException("Malicious archive blocked: " + e.getMessage(), e);
}
throw e;
} Prevention
- 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
When it happens
Trigger: 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'.
Common situations: 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.
Understand the failure class
Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.
Related errors
- Zip slip vulnerability detected! Expected dest: ${new File(r
- Zip slip vulnerability detected!\nExpected dest: " + new Fil
- Zip slip vulnerability detected!\nExpected dest: " + new Fil
- Couldn't create symbolic link ${file} pointing to ${linkName
- Could not create directories in the parent directory.
AI-assisted analysis of MuntashirAkon/AppManager@0152f468fc (2026-09-12).
Data as JSON: /api/errors/cfe9028fa1bd447b.
Report an issue: GitHub.