=== How to fix it in Java SE :canonicalization_function1: java.io.File.getCanonicalFile :canonicalization_function2: java.io.File.getCanonicalPath include::../../common/fix/code-rationale.adoc[] [cols="a"] |=== h| Non-compliant code example | [source,java] ---- public class Example { static private String targetDirectory = "/example/directory/"; public void ExtractEntry(ZipFile zipFile) throws IOException { Enumeration entries = zipFile.entries(); ZipEntry entry = entries.nextElement(); InputStream inputStream = zipFile.getInputStream(entry); File file = new File(targetDirectory + entry.getName()); Files.copy(inputStream, file.toPath(), StandardCopyOption.REPLACE_EXISTING); // Noncompliant } } ---- h| Compliant solution | [source,java] ---- public class Example { static private String targetDirectory = "/example/directory/"; public void ExtractEntry(ZipFile zipFile) throws IOException { Enumeration entries = zipFile.entries(); ZipEntry entry = entries.nextElement(); InputStream inputStream = zipFile.getInputStream(entry); File file = new File(targetDirectory + entry.getName()); String canonicalDestinationPath = file.getCanonicalPath(); if (canonicalDestinationPath.startsWith(targetDirectory)) { Files.copy(inputStream, file.toPath(), StandardCopyOption.REPLACE_EXISTING, LinkOption.NOFOLLOW_LINKS); } } } ---- |=== === How does this work? include::../../common/fix/how-does-this-work.adoc[] === Pitfalls include::../../common/pitfalls/partial-path-traversal.adoc[] For example, the following code is vulnerable to partial path injection. Note that the string `targetDirectory` does not end with a path separator: [source, java] ---- static private String targetDirectory = "/Users/John"; public void ExtractEntry(ZipFile zipFile) throws IOException { Enumeration entries = zipFile.entries(); ZipEntry entry = entries.nextElement(); InputStream inputStream = zipFile.getInputStream(entry); File file = new File(entry.getName()); String canonicalDestinationPath = file.getCanonicalPath(); if (canonicalDestinationPath.startsWith(targetDirectory)) { Files.copy(inputStream, file.toPath(), StandardCopyOption.REPLACE_EXISTING, LinkOption.NOFOLLOW_LINKS); } } ---- This check can be bypassed because `"/Users/Johnny".startsWith("/Users/John")` returns `true`. Thus, for validation, `"/Users/John"` should actually be `"/Users/John/"`. **Warning**: Some functions, such as `.getCanonicalPath`, remove the terminating path separator in their return value. + The validation code should be tested to ensure that it cannot be impacted by this issue. https://github.com/aws/aws-sdk-java/security/advisories/GHSA-c28r-hw5m-5gv3[Here is a real-life example of this vulnerability.]