Skip to content

Commit 6db0db4

Browse files
committed
Java: Add pruning for local taint flow.
1 parent 9db65ea commit 6db0db4

File tree

2 files changed

+76
-13
lines changed

2 files changed

+76
-13
lines changed

java/ql/lib/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,57 @@ predicate localExprTaint(Expr src, Expr sink) {
3333
localTaint(DataFlow::exprNode(src), DataFlow::exprNode(sink))
3434
}
3535

36+
/** Holds if `node` is an endpoint for local taint flow. */
37+
signature predicate nodeSig(DataFlow::Node node);
38+
39+
/** Provides local taint flow restricted to a given set of sources and sinks. */
40+
module LocalTaintFlow<nodeSig/1 source, nodeSig/1 sink> {
41+
private predicate reachRev(DataFlow::Node n) {
42+
sink(n)
43+
or
44+
exists(DataFlow::Node mid |
45+
localTaintStep(n, mid) and
46+
reachRev(mid)
47+
)
48+
}
49+
50+
private predicate reachFwd(DataFlow::Node n) {
51+
reachRev(n) and
52+
(
53+
source(n)
54+
or
55+
exists(DataFlow::Node mid |
56+
localTaintStep(mid, n) and
57+
reachFwd(mid)
58+
)
59+
)
60+
}
61+
62+
private predicate step(DataFlow::Node n1, DataFlow::Node n2) {
63+
localTaintStep(n1, n2) and
64+
reachFwd(n1) and
65+
reachFwd(n2)
66+
}
67+
68+
/**
69+
* Holds if taint can flow from `n1` to `n2` in zero or more local
70+
* (intra-procedural) steps that are restricted to be part of a path between
71+
* `source` and `sink`.
72+
*/
73+
pragma[inline]
74+
predicate hasFlow(DataFlow::Node n1, DataFlow::Node n2) { step*(n1, n2) }
75+
76+
/**
77+
* Holds if taint can flow from `n1` to `n2` in zero or more local
78+
* (intra-procedural) steps that are restricted to be part of a path between
79+
* `source` and `sink`.
80+
*/
81+
pragma[inline]
82+
predicate hasExprFlow(Expr n1, Expr n2) {
83+
hasFlow(DataFlow::exprNode(n1), DataFlow::exprNode(n2))
84+
}
85+
}
86+
3687
cached
3788
private module Cached {
3889
private import DataFlowImplCommon as DataFlowImplCommon

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

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -69,13 +69,25 @@ private class ExactPathMatchSanitizer extends PathInjectionSanitizer {
6969
}
7070
}
7171

72-
private class AllowedPrefixGuard extends Guard instanceof MethodAccess {
72+
abstract private class PathGuard extends Guard {
73+
abstract Expr getCheckedExpr();
74+
}
75+
76+
private predicate anyNode(DataFlow::Node n) { any() }
77+
78+
private predicate pathGuardNode(DataFlow::Node n) { n.asExpr() = any(PathGuard g).getCheckedExpr() }
79+
80+
private predicate localTaintFlowToPathGuard(Expr e, PathGuard g) {
81+
TaintTracking::LocalTaintFlow<anyNode/1, pathGuardNode/1>::hasExprFlow(e, g.getCheckedExpr())
82+
}
83+
84+
private class AllowedPrefixGuard extends PathGuard instanceof MethodAccess {
7385
AllowedPrefixGuard() {
7486
(isStringPrefixMatch(this) or isPathPrefixMatch(this)) and
7587
not isDisallowedWord(super.getAnArgument())
7688
}
7789

78-
Expr getCheckedExpr() { result = super.getQualifier() }
90+
override Expr getCheckedExpr() { result = super.getQualifier() }
7991
}
8092

8193
/**
@@ -91,10 +103,10 @@ private predicate allowedPrefixGuard(Guard g, Expr e, boolean branch) {
91103
// String strPath = file.getCanonicalPath();
92104
// if (strPath.startsWith("/safe/dir"))
93105
// sink(file);
94-
TaintTracking::localExprTaint(e, g.(AllowedPrefixGuard).getCheckedExpr()) and
106+
g instanceof AllowedPrefixGuard and
107+
localTaintFlowToPathGuard(e, g) and
95108
exists(Expr previousGuard |
96-
TaintTracking::localExprTaint(previousGuard.(PathNormalizeSanitizer),
97-
g.(AllowedPrefixGuard).getCheckedExpr())
109+
localTaintFlowToPathGuard(previousGuard.(PathNormalizeSanitizer), g)
98110
or
99111
previousGuard
100112
.(PathTraversalGuard)
@@ -121,7 +133,7 @@ private predicate dotDotCheckGuard(Guard g, Expr e, boolean branch) {
121133
// if (!strPath.contains("..") && strPath.startsWith("/safe/dir"))
122134
// sink(path);
123135
branch = g.(PathTraversalGuard).getBranch() and
124-
TaintTracking::localExprTaint(e, g.(PathTraversalGuard).getCheckedExpr()) and
136+
localTaintFlowToPathGuard(e, g) and
125137
exists(Guard previousGuard |
126138
previousGuard.(AllowedPrefixGuard).controls(g.getBasicBlock(), true)
127139
or
@@ -136,13 +148,13 @@ private class DotDotCheckSanitizer extends PathInjectionSanitizer {
136148
}
137149
}
138150

139-
private class BlockListGuard extends Guard instanceof MethodAccess {
151+
private class BlockListGuard extends PathGuard instanceof MethodAccess {
140152
BlockListGuard() {
141153
(isStringPartialMatch(this) or isPathPrefixMatch(this)) and
142154
isDisallowedWord(super.getAnArgument())
143155
}
144156

145-
Expr getCheckedExpr() { result = super.getQualifier() }
157+
override Expr getCheckedExpr() { result = super.getQualifier() }
146158
}
147159

148160
/**
@@ -158,10 +170,10 @@ private predicate blockListGuard(Guard g, Expr e, boolean branch) {
158170
// String strPath = file.getCanonicalPath();
159171
// if (!strPath.contains("..") && !strPath.startsWith("/dangerous/dir"))
160172
// sink(file);
161-
TaintTracking::localExprTaint(e, g.(BlockListGuard).getCheckedExpr()) and
173+
g instanceof BlockListGuard and
174+
localTaintFlowToPathGuard(e, g) and
162175
exists(Expr previousGuard |
163-
TaintTracking::localExprTaint(previousGuard.(PathNormalizeSanitizer),
164-
g.(BlockListGuard).getCheckedExpr())
176+
localTaintFlowToPathGuard(previousGuard.(PathNormalizeSanitizer), g)
165177
or
166178
previousGuard
167179
.(PathTraversalGuard)
@@ -217,7 +229,7 @@ private predicate isDisallowedWord(CompileTimeConstantExpr word) {
217229
}
218230

219231
/** A complementary guard that protects against path traversal, by looking for the literal `..`. */
220-
private class PathTraversalGuard extends Guard {
232+
private class PathTraversalGuard extends PathGuard {
221233
PathTraversalGuard() {
222234
exists(MethodAccess ma |
223235
ma.getMethod().getDeclaringType() instanceof TypeString and
@@ -235,7 +247,7 @@ private class PathTraversalGuard extends Guard {
235247
)
236248
}
237249

238-
Expr getCheckedExpr() {
250+
override Expr getCheckedExpr() {
239251
exists(MethodAccess ma | ma = this.(EqualityTest).getAnOperand() or ma = this |
240252
result = ma.getQualifier()
241253
)

0 commit comments

Comments
 (0)