Skip to content

Commit 9ff4ed2

Browse files
committed
Java: recognize Path.toRealPath() as path normalization sanitizer
PathNormalizeSanitizer recognized Path.normalize() and File.getCanonicalPath()/getCanonicalFile(), but not Path.toRealPath(). toRealPath() is strictly stronger than normalize() (resolves symlinks and verifies file existence in addition to normalizing ".." components), and is functionally equivalent to File.getCanonicalPath() for the NIO.2 API. CERT FIO16-J and OWASP both recommend it for path traversal defense. This adds toRealPath to PathNormalizeSanitizer alongside normalize, reducing false positives for code using idiomatic NIO.2 path handling.
1 parent fb8b569 commit 9ff4ed2

File tree

3 files changed

+26
-1
lines changed

3 files changed

+26
-1
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* The `java/path-injection` and `java/zipslip` queries now recognize `Path.toRealPath()` as a path normalization sanitizer, consistent with the existing treatment of `Path.normalize()` and `File.getCanonicalPath()`. This reduces false positives for code that uses the NIO.2 API for path canonicalization.

java/ql/lib/semmle/code/java/security/PathSanitizer.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ private class PathNormalizeSanitizer extends MethodCall {
243243
PathNormalizeSanitizer() {
244244
exists(RefType t | this.getMethod().getDeclaringType() = t |
245245
(t instanceof TypePath or t instanceof FilesKt) and
246-
this.getMethod().hasName("normalize")
246+
this.getMethod().hasName(["normalize", "toRealPath"])
247247
or
248248
t instanceof TypeFile and
249249
this.getMethod().hasName(["getCanonicalPath", "getCanonicalFile"])

java/ql/test/query-tests/security/CWE-022/semmle/tests/TaintedPath.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,27 @@ public void sendUserFileGood3(Socket sock, String user) throws Exception {
7272
}
7373
}
7474

75+
public void sendUserFileGood5(Socket sock, String user) throws Exception {
76+
BufferedReader filenameReader =
77+
new BufferedReader(new InputStreamReader(sock.getInputStream(), "UTF-8"));
78+
String filename = filenameReader.readLine();
79+
80+
Path publicFolder = Paths.get("/home/" + user + "/public").toRealPath();
81+
Path filePath = publicFolder.resolve(filename).toRealPath();
82+
83+
// GOOD: toRealPath() normalizes the path (resolves ".." and symlinks),
84+
// equivalent to File.getCanonicalPath()
85+
if (!filePath.startsWith(publicFolder + File.separator)) {
86+
throw new IllegalArgumentException("Invalid filename");
87+
}
88+
BufferedReader fileReader = new BufferedReader(new FileReader(filePath.toString()));
89+
String fileLine = fileReader.readLine();
90+
while (fileLine != null) {
91+
sock.getOutputStream().write(fileLine.getBytes());
92+
fileLine = fileReader.readLine();
93+
}
94+
}
95+
7596
public void sendUserFileGood4(Socket sock, String user) throws IOException {
7697
BufferedReader filenameReader =
7798
new BufferedReader(new InputStreamReader(sock.getInputStream(), "UTF-8"));

0 commit comments

Comments
 (0)