Click to see the query in the CodeQL repository
A common way to check that a user-supplied path SUBDIR falls inside a directory DIR is to use getCanonicalPath() to remove any path-traversal elements and then check that DIR is a prefix. However, if DIR is not slash-terminated, this can unexpectedly allow access to siblings of DIR.
See also java/partial-path-traversal-from-remote, which is similar to this query but only flags instances with evidence of remote exploitability.
If the user should only access items within a certain directory DIR, ensure that DIR is slash-terminated before checking that DIR is a prefix of the user-provided path, SUBDIR. Note, Java’s getCanonicalPath() returns a non-slash-terminated path string, so a slash must be added to DIR if that method is used.
In this example, the if statement checks if parent.getCanonicalPath() is a prefix of dir.getCanonicalPath(). However, parent.getCanonicalPath() is not slash-terminated. This means that users that supply dir may be also allowed to access siblings of parent and not just children of parent, which is a security issue.
In this example, the if statement checks if parent.toPath() is a prefix of dir.normalize(). Because Path#startsWith does the correct check that dir is a child of parent, users will not be able to access siblings of parent, as desired.
OWASP: Partial Path Traversal.
CVE-2022-23457: ESAPI Vulnerability Report.
Common Weakness Enumeration: CWE-23.