-
Notifications
You must be signed in to change notification settings - Fork 717
Fix zip slip check in StructContext on Windows #22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix zip slip check in StructContext on Windows #22
Conversation
(cherry picked from commit 226a520)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR fixes a Windows-specific bug in the zip-slip protection logic of StructContext.addArchive that caused valid JAR/ZIP archives to be incorrectly rejected as corrupted. The issue occurred because the original implementation used File.getCanonicalPath() on synthetic zipFile\entry paths, which fails on Windows even for legitimate ZIP entries. The fix replaces the filesystem-based check with a normalized string-based validation that prevents path traversal while avoiding platform-specific path resolution issues.
Key changes:
- Replaced
File.getCanonicalPath()check with string-based path normalization - Changed zip-slip detection to check for absolute paths and
..sequences directly in entry names
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| File test = new File(file.getAbsolutePath(), name); | ||
| if (!test.getCanonicalPath().startsWith(file.getCanonicalPath() + File.separator)) { // check for zip slip exploit | ||
| String normalizedName = name.replace('\\', '/'); | ||
| if (normalizedName.startsWith("/") || normalizedName.startsWith("../") || normalizedName.contains("/../")) { |
Copilot
AI
Dec 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The zip-slip protection is incomplete. It doesn't handle cases where a path ends with .. (e.g., foo/..) or contains multiple consecutive slashes that could be normalized away. Consider using a more robust path normalization approach or additional checks for these edge cases.
| File test = new File(file.getAbsolutePath(), name); | ||
| if (!test.getCanonicalPath().startsWith(file.getCanonicalPath() + File.separator)) { // check for zip slip exploit | ||
| String normalizedName = name.replace('\\', '/'); | ||
| if (normalizedName.startsWith("/") || normalizedName.startsWith("../") || normalizedName.contains("/../")) { |
Copilot
AI
Dec 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The check should also reject paths that start with .. without a slash (e.g., ..foo), as startsWith(\"../\") only catches cases with a trailing slash. Consider changing to startsWith(\"..\") to catch all cases where the path begins with two dots.
| if (normalizedName.startsWith("/") || normalizedName.startsWith("../") || normalizedName.contains("/../")) { | |
| if (normalizedName.startsWith("/") || normalizedName.startsWith("..") || normalizedName.contains("/../")) { |
|
您的邮件已被phoenix查收
欢迎下次来信
|
|
@Miguel0888 This is an unofficial mirror of the actual code; a PR here will have no effect, unfortunately. You should open a pull request at https://github.com/JetBrains/intellij-community/tree/master/plugins/java-decompiler/engine |
|
Summary
This PR fixes a Windows-specific bug in
StructContext.addArchivethat prevents Fernflower from decompiling any JAR/ZIP archives when running on Windows.The issue is not caused by corrupted archives – it is triggered by the zip-slip protection using
File.getCanonicalPath()on paths that are not real directories butzipFile\entrycombinations. On Windows this can throw anIOExceptioneven for perfectly valid entries, which then causes Fernflower to treat the whole archive as "corrupted".The fix replaces the filesystem-based zip-slip check with a string-based check on the normalized entry name. This preserves the security goal (rejecting absolute paths and
..escapes) while avoiding Windows-specific path resolution bugs.Problem / Error message
On Windows, any decompilation that involves archives inside an input directory fails with:
This happens not only for complex plugin archives, but can be reproduced with a minimal example:
a.zipcontaining a single empty fileb(no subdirectories).a.zip.StructContext.addArchivethrows the aboveIOExceptionfromgetCanonicalPath(), and the archive is reported as "Corrupted archive file".If the file
bis removed from the ZIP (so the ZIP is technically valid but empty), the error disappears. The problem is therefore unrelated to the ZIP structure itself and entirely due to how the path check is implemented.This also means that on Windows Fernflower cannot reliably "convert" lib JARs/ZIPs into decompiled versions: as soon as a problematic entry hits this check, processing aborts with the above error.
Root cause
The relevant code in
StructContext.addArchivecurrently does:Here,
fileis the ZIP/JAR itself, e.g.:For an entry
name = "b"(or e.g."TAX/b"), this constructs:Calling
getCanonicalPath()on such a "zipFile\entry" path is not meaningful in this context (Fernflower does not actually extract entries to disk), and on Windows this can fail with:This
IOExceptionbubbles up to theaddSpacemethod and is logged as:so the entire archive is discarded even though:
The current zip-slip protection therefore has two problems:
zipFile\entryfilesystem paths, which are not the actual extraction target.File.getCanonicalPath()for these pseudo-paths, which is fragile and platform-specific (fails on Windows for combinations that are perfectly valid as ZIP entries).Fernflower only reads entries from the archive, it does not actually extract them to the filesystem under
file.getAbsolutePath(), so using canonical filesystem paths here is not necessary.Fix
The fix keeps the idea of rejecting suspicious entry paths (zip slip protection), but changes the implementation to a normalized string-based check that does not depend on filesystem resolution:
Everything else in
addArchiveremains unchanged:.classentries are still loaded and decompiled as before.otherEntry/dirEntry.On Windows, the crucial difference is:
File.getCanonicalPath()onzipFile\entrypaths.IOExceptionis thrown for valid ZIP entries."Corrupted archive file"errors are processed successfully.Security considerations
The original intention of the code is to prevent zip-slip style path traversal attacks. In this context:
file.getAbsolutePath()..javafiles into the configured destination viaIResultSaver.Given that, the important safety property is:
The new check still enforces exactly this:
/(absolute paths),../,/../in the middle.com/example/Foo.class,b,TAX/b, etc.This preserves the zip-slip protection at the logical entry-name level, while removing the dependency on platform-specific canonical path resolution that caused valid archives to fail on Windows.
In other words:
zipFile\entryfilesystem path.Impact
plugins/directories).File.getCanonicalPath()for paths that are not real extraction targets.