Skip to content

Commit 2f622ad

Browse files
committed
Refactor by introducing helper predicates
1 parent 85b2642 commit 2f622ad

File tree

1 file changed

+75
-71
lines changed

1 file changed

+75
-71
lines changed

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

Lines changed: 75 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -49,25 +49,18 @@ private module ValidationMethod<DataFlow::guardChecksSig/3 validationGuard> {
4949
*/
5050
private predicate exactPathMatchGuard(Guard g, Expr e, boolean branch) {
5151
exists(MethodAccess ma, RefType t |
52-
(
53-
t instanceof TypeString or
54-
t instanceof TypeUri or
55-
t instanceof TypePath or
56-
t instanceof TypeFile or
57-
t.hasQualifiedName("android.net", "Uri")
58-
) and
59-
e = ma.getQualifier().getUnderlyingExpr()
60-
or
61-
(
62-
ma.getMethod().getDeclaringType() instanceof StringsKt
63-
or
64-
ma.getMethod().getDeclaringType() instanceof FilesKt
65-
) and
66-
e = ma.getArgument(0).getUnderlyingExpr()
52+
t instanceof TypeString or
53+
t instanceof TypeUri or
54+
t instanceof TypePath or
55+
t instanceof TypeFile or
56+
t.hasQualifiedName("android.net", "Uri") or
57+
t instanceof StringsKt or
58+
t instanceof FilesKt
6759
|
60+
e = getVisualQualifier(ma).getUnderlyingExpr() and
6861
ma.getMethod().getDeclaringType() = t and
6962
ma = g and
70-
ma.getMethod().getName() = ["equals", "equalsIgnoreCase"] + ["", "$default"] and
63+
getSourceMethod(ma.getMethod()).hasName(["equals", "equalsIgnoreCase"]) and
7164
branch = true
7265
)
7366
}
@@ -224,29 +217,19 @@ private class ConstantOrRegex extends Expr {
224217
}
225218

226219
private predicate isStringPrefixMatch(MethodAccess ma, Expr checkedExpr) {
227-
exists(Method m |
220+
exists(Method m, RefType t |
221+
m.getDeclaringType() = t and
222+
(t instanceof TypeString or t instanceof StringsKt) and
228223
m = ma.getMethod() and
229-
(
230-
m.getDeclaringType() instanceof TypeString and
231-
checkedExpr = ma.getQualifier().getUnderlyingExpr()
232-
or
233-
m.getDeclaringType() instanceof StringsKt and
234-
checkedExpr = ma.getArgument(0).getUnderlyingExpr()
235-
)
224+
checkedExpr = getVisualQualifier(ma).getUnderlyingExpr()
236225
|
237-
m.hasName("startsWith" + ["", "$default"])
226+
getSourceMethod(m).hasName("startsWith")
238227
or
239-
exists(int argPos |
240-
m.getDeclaringType() instanceof TypeString and argPos = 0
241-
or
242-
m.getDeclaringType() instanceof StringsKt and argPos = 1
243-
|
244-
m.hasName("regionMatches" + ["", "$default"]) and
245-
ma.getArgument(argPos).(CompileTimeConstantExpr).getIntValue() = 0
246-
or
247-
m.hasName("matches") and
248-
not ma.getArgument(argPos).(ConstantOrRegex).getStringValue().matches(".*%")
249-
)
228+
getSourceMethod(m).hasName("regionMatches") and
229+
getVisualArgument(ma, 0).(CompileTimeConstantExpr).getIntValue() = 0
230+
or
231+
m.hasName("matches") and
232+
not getVisualArgument(ma, 0).(ConstantOrRegex).getStringValue().matches(".*%")
250233
)
251234
}
252235

@@ -256,30 +239,23 @@ private predicate isStringPrefixMatch(MethodAccess ma, Expr checkedExpr) {
256239
private predicate isStringPartialMatch(MethodAccess ma, Expr checkedExpr) {
257240
isStringPrefixMatch(ma, checkedExpr)
258241
or
259-
(
260-
ma.getMethod().getDeclaringType() instanceof TypeString and
261-
checkedExpr = ma.getQualifier().getUnderlyingExpr()
262-
or
263-
ma.getMethod().getDeclaringType() instanceof StringsKt and
264-
checkedExpr = ma.getArgument(0).getUnderlyingExpr()
242+
exists(RefType t | t = ma.getMethod().getDeclaringType() |
243+
t instanceof TypeString or t instanceof StringsKt
265244
) and
266-
ma.getMethod()
267-
.hasName(["contains", "matches", "regionMatches", "indexOf", "lastIndexOf"] + ["", "$default"])
245+
getSourceMethod(ma.getMethod())
246+
.hasName(["contains", "matches", "regionMatches", "indexOf", "lastIndexOf"]) and
247+
checkedExpr = getVisualQualifier(ma).getUnderlyingExpr()
268248
}
269249

270250
/**
271251
* Holds if `ma` is a call to a method that checks whether `checkedExpr` starts with a prefix.
272252
*/
273253
private predicate isPathPrefixMatch(MethodAccess ma, Expr checkedExpr) {
274-
exists(RefType t |
275-
t instanceof TypePath and checkedExpr = ma.getQualifier().getUnderlyingExpr()
276-
or
277-
t instanceof FilesKt and
278-
checkedExpr = ma.getArgument(0).getUnderlyingExpr()
279-
|
280-
t = ma.getMethod().getDeclaringType() and
281-
ma.getMethod().hasName("startsWith" + ["", "$default"])
282-
)
254+
exists(RefType t | t = ma.getMethod().getDeclaringType() |
255+
t instanceof TypePath or t instanceof FilesKt
256+
) and
257+
getSourceMethod(ma.getMethod()).hasName("startsWith") and
258+
checkedExpr = getVisualQualifier(ma)
283259
}
284260

285261
private predicate isDisallowedWord(ConstantOrRegex word) {
@@ -291,22 +267,19 @@ private class PathTraversalGuard extends PathGuard {
291267
Expr checkedExpr;
292268

293269
PathTraversalGuard() {
294-
exists(MethodAccess ma |
295-
(
296-
ma.getMethod().getDeclaringType() instanceof TypeString and
297-
checkedExpr = ma.getQualifier().getUnderlyingExpr()
298-
or
299-
ma.getMethod().getDeclaringType() instanceof StringsKt and
300-
checkedExpr = ma.getArgument(0).getUnderlyingExpr()
301-
) and
270+
exists(MethodAccess ma, Method m, RefType t |
271+
m = ma.getMethod() and
272+
t = m.getDeclaringType() and
273+
(t instanceof TypeString or t instanceof StringsKt) and
274+
checkedExpr = getVisualQualifier(ma).getUnderlyingExpr() and
302275
ma.getAnArgument().(CompileTimeConstantExpr).getStringValue() = ".."
303276
|
304277
this = ma and
305-
ma.getMethod().hasName("contains" + ["", "$default"])
278+
getSourceMethod(m).hasName("contains")
306279
or
307280
exists(EqualityTest eq |
308281
this = eq and
309-
ma.getMethod().hasName(["indexOf", "lastIndexOf"] + ["", "$default"]) and
282+
getSourceMethod(m).hasName(["indexOf", "lastIndexOf"]) and
310283
eq.getAnOperand() = ma and
311284
eq.getAnOperand().(CompileTimeConstantExpr).getIntValue() = -1
312285
)
@@ -325,15 +298,46 @@ private class PathTraversalGuard extends PathGuard {
325298
/** A complementary sanitizer that protects against path traversal using path normalization. */
326299
private class PathNormalizeSanitizer extends MethodAccess {
327300
PathNormalizeSanitizer() {
328-
exists(RefType t |
329-
t instanceof TypePath or
330-
t instanceof FilesKt
331-
|
332-
this.getMethod().getDeclaringType() = t and
301+
exists(RefType t | this.getMethod().getDeclaringType() = t |
302+
(t instanceof TypePath or t instanceof FilesKt) and
333303
this.getMethod().hasName("normalize")
304+
or
305+
t instanceof TypeFile and
306+
this.getMethod().hasName(["getCanonicalPath", "getCanonicalFile"])
334307
)
335-
or
336-
this.getMethod().getDeclaringType() instanceof TypeFile and
337-
this.getMethod().hasName(["getCanonicalPath", "getCanonicalFile"])
338308
}
339309
}
310+
311+
/**
312+
* Gets the qualifier of `ma` as seen in the source code.
313+
* This is a helper predicate to solve discrepancies between
314+
* what `getQualifier` actually gets in Java and Kotlin.
315+
*/
316+
private Expr getVisualQualifier(MethodAccess ma) {
317+
if getSourceMethod(ma.getMethod()) instanceof ExtensionMethod
318+
then result = ma.getArgument(0)
319+
else result = ma.getQualifier()
320+
}
321+
322+
/**
323+
* Gets the argument of `ma` at position `argPos` as seen in the source code.
324+
* This is a helper predicate to solve discrepancies between
325+
* what `getArgument` actually gets in Java and Kotlin.
326+
*/
327+
bindingset[argPos]
328+
private Argument getVisualArgument(MethodAccess ma, int argPos) {
329+
if getSourceMethod(ma.getMethod()) instanceof ExtensionMethod
330+
then result = ma.getArgument(argPos + 1)
331+
else result = ma.getArgument(argPos)
332+
}
333+
334+
/**
335+
* Gets the proxied method if `m` is a Kotlin proxy that supplies default parameter values,
336+
* Otherwise, just gets `m`.
337+
*/
338+
private Method getSourceMethod(Method m) {
339+
m = result.getKotlinParameterDefaultsProxy()
340+
or
341+
not exists(Method src | m = src.getKotlinParameterDefaultsProxy()) and
342+
result = m
343+
}

0 commit comments

Comments
 (0)