Skip to content

Commit 406f5b5

Browse files
committed
Go: Deprecate and replace BarrierGuard class
1 parent bbb8d29 commit 406f5b5

40 files changed

+372
-248
lines changed

go/ql/lib/semmle/go/dataflow/barrierguardutil/RedirectCheckBarrierGuard.qll

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,30 @@
44

55
import go
66

7+
private predicate redirectCheckGuard(DataFlow::Node g, Expr e, boolean outcome) {
8+
g.(DataFlow::CallNode)
9+
.getCalleeName()
10+
.regexpMatch("(?i)(is_?)?(local_?url|valid_?redir(ect)?)(ur[li])?") and
11+
// `isLocalUrl(e)` is a barrier for `e` if it evaluates to `true`
12+
g.(DataFlow::CallNode).getAnArgument().asExpr() = e and
13+
outcome = true
14+
}
15+
16+
/**
17+
* A call to a function called `isLocalUrl`, `isValidRedirect`, or similar, which is
18+
* considered a barrier guard for sanitizing untrusted URLs.
19+
*/
20+
class RedirectCheckBarrier extends DataFlow::Node {
21+
RedirectCheckBarrier() { this = DataFlow::BarrierGuard<redirectCheckGuard/3>::getABarrierNode() }
22+
}
23+
724
/**
25+
* DEPRECATED: Use `RedirectCheckBarrier` instead.
26+
*
827
* A call to a function called `isLocalUrl`, `isValidRedirect`, or similar, which is
928
* considered a barrier guard for sanitizing untrusted URLs.
1029
*/
11-
class RedirectCheckBarrierGuard extends DataFlow::BarrierGuard, DataFlow::CallNode {
30+
deprecated class RedirectCheckBarrierGuard extends DataFlow::BarrierGuard, DataFlow::CallNode {
1231
RedirectCheckBarrierGuard() {
1332
this.getCalleeName().regexpMatch("(?i)(is_?)?(local_?url|valid_?redir(ect)?)(ur[li])?")
1433
}

go/ql/lib/semmle/go/dataflow/barrierguardutil/RegexpCheck.qll

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import go
1010
* This is overapproximate: we do not attempt to reason about the correctness of the regexp.
1111
*
1212
* Use this if you want to define a derived `DataFlow::BarrierGuard` without
13-
* make the type recursive. Otherwise use `RegexpCheck`.
13+
* make the type recursive. Otherwise use `RegexpCheckBarrier`.
1414
*/
1515
predicate regexpFunctionChecksExpr(DataFlow::Node resultNode, Expr checked, boolean branch) {
1616
exists(RegexpMatchFunction matchfn, DataFlow::CallNode call |
@@ -26,7 +26,20 @@ predicate regexpFunctionChecksExpr(DataFlow::Node resultNode, Expr checked, bool
2626
*
2727
* This is overapproximate: we do not attempt to reason about the correctness of the regexp.
2828
*/
29-
class RegexpCheck extends DataFlow::BarrierGuard {
29+
class RegexpCheckBarrier extends DataFlow::Node {
30+
RegexpCheckBarrier() {
31+
this = DataFlow::BarrierGuard<regexpFunctionChecksExpr/3>::getABarrierNode()
32+
}
33+
}
34+
35+
/**
36+
* DEPRECATED: Use `RegexpCheckBarrier` instead.
37+
*
38+
* A call to a regexp match function, considered as a barrier guard for sanitizing untrusted URLs.
39+
*
40+
* This is overapproximate: we do not attempt to reason about the correctness of the regexp.
41+
*/
42+
deprecated class RegexpCheck extends DataFlow::BarrierGuard {
3043
RegexpCheck() { regexpFunctionChecksExpr(this, _, _) }
3144

3245
override predicate checks(Expr e, boolean branch) { regexpFunctionChecksExpr(this, e, branch) }

go/ql/lib/semmle/go/dataflow/barrierguardutil/UrlCheck.qll

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,44 @@
44

55
import go
66

7+
private predicate urlCheck(DataFlow::Node g, Expr e, boolean outcome) {
8+
exists(DataFlow::Node url, DataFlow::EqualityTestNode eq |
9+
g = eq and
10+
exists(eq.getAnOperand().getStringValue()) and
11+
(
12+
url = eq.getAnOperand()
13+
or
14+
exists(DataFlow::MethodCallNode mc | mc = eq.getAnOperand() |
15+
mc.getTarget().getName() = "Hostname" and
16+
url = mc.getReceiver()
17+
)
18+
) and
19+
e = url.asExpr() and
20+
outcome = eq.getPolarity()
21+
)
22+
}
23+
724
/**
825
* An equality check comparing a data-flow node against a constant string, considered as
926
* a barrier guard for sanitizing untrusted URLs.
1027
*
1128
* Additionally, a check comparing `url.Hostname()` against a constant string is also
1229
* considered a barrier guard for `url`.
1330
*/
14-
class UrlCheck extends DataFlow::BarrierGuard, DataFlow::EqualityTestNode {
31+
class UrlCheckBarrier extends DataFlow::Node {
32+
UrlCheckBarrier() { this = DataFlow::BarrierGuard<urlCheck/3>::getABarrierNode() }
33+
}
34+
35+
/**
36+
* DEPRECATED: Use `UrlCheckBarrier` instead.
37+
*
38+
* An equality check comparing a data-flow node against a constant string, considered as
39+
* a barrier guard for sanitizing untrusted URLs.
40+
*
41+
* Additionally, a check comparing `url.Hostname()` against a constant string is also
42+
* considered a barrier guard for `url`.
43+
*/
44+
deprecated class UrlCheck extends DataFlow::BarrierGuard, DataFlow::EqualityTestNode {
1545
DataFlow::Node url;
1646

1747
UrlCheck() {

go/ql/lib/semmle/go/dataflow/internal/DataFlowUtil.qll

Lines changed: 68 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -231,27 +231,39 @@ class SyntheticFieldContent extends Content, TSyntheticFieldContent {
231231
}
232232

233233
/**
234-
* A guard that validates some expression.
234+
* Holds if the guard `g` validates the expression `e` upon evaluating to `branch`.
235235
*
236-
* To use this in a configuration, extend the class and provide a
237-
* characteristic predicate precisely specifying the guard, and override
238-
* `checks` to specify what is being validated and in which branch.
236+
* The expression `e` is expected to be a syntactic part of the guard `g`.
237+
* For example, the guard `g` might be a call `isSafe(x)` and the expression `e`
238+
* the argument `x`.
239+
*/
240+
signature predicate guardChecksSig(Node g, Expr e, boolean branch);
241+
242+
/**
243+
* Provides a set of barrier nodes for a guard that validates an expression.
239244
*
240-
* When using a data-flow or taint-flow configuration `cfg`, it is important
241-
* that any classes extending BarrierGuard in scope which are not used in `cfg`
242-
* are disjoint from any classes extending BarrierGuard in scope which are used
243-
* in `cfg`.
245+
* This is expected to be used in `isBarrier`/`isSanitizer` definitions
246+
* in data flow and taint tracking.
244247
*/
245-
abstract class BarrierGuard extends Node {
246-
/** Holds if this guard validates `e` upon evaluating to `branch`. */
247-
abstract predicate checks(Expr e, boolean branch);
248+
module BarrierGuard<guardChecksSig/3 guardChecks> {
249+
/** Gets a node that is safely guarded by the given guard check. */
250+
Node getABarrierNode() {
251+
exists(Node g, ControlFlow::ConditionGuardNode guard, Node nd, SsaWithFields var |
252+
result = var.getAUse()
253+
|
254+
guards(g, guard, nd, var) and
255+
guard.dominates(result.getBasicBlock())
256+
)
257+
}
248258

249-
/** Gets a node guarded by this guard. */
250-
final Node getAGuardedNode() {
259+
/**
260+
* Gets a node that is safely guarded by the given guard check.
261+
*/
262+
Node getABarrierNodeForGuard(Node guardCheck) {
251263
exists(ControlFlow::ConditionGuardNode guard, Node nd, SsaWithFields var |
252264
result = var.getAUse()
253265
|
254-
this.guards(guard, nd, var) and
266+
guards(guardCheck, guard, nd, var) and
255267
guard.dominates(result.getBasicBlock())
256268
)
257269
}
@@ -263,36 +275,36 @@ abstract class BarrierGuard extends Node {
263275
* This predicate exists to enforce a good join order in `getAGuardedNode`.
264276
*/
265277
pragma[noinline]
266-
private predicate guards(ControlFlow::ConditionGuardNode guard, Node nd, SsaWithFields ap) {
267-
this.guards(guard, nd) and nd = ap.getAUse()
278+
private predicate guards(Node g, ControlFlow::ConditionGuardNode guard, Node nd, SsaWithFields ap) {
279+
guards(g, guard, nd) and nd = ap.getAUse()
268280
}
269281

270282
/**
271283
* Holds if `guard` markes a point in the control-flow graph where this node
272284
* is known to validate `nd`.
273285
*/
274-
private predicate guards(ControlFlow::ConditionGuardNode guard, Node nd) {
286+
private predicate guards(Node g, ControlFlow::ConditionGuardNode guard, Node nd) {
275287
exists(boolean branch |
276-
this.checks(nd.asExpr(), branch) and
277-
guard.ensures(this, branch)
288+
guardChecks(g, nd.asExpr(), branch) and
289+
guard.ensures(g, branch)
278290
)
279291
or
280292
exists(
281293
Function f, FunctionInput inp, FunctionOutput outp, DataFlow::Property p, CallNode c,
282294
Node resNode, Node check, boolean outcome
283295
|
284-
this.guardingCall(f, inp, outp, p, c, nd, resNode) and
296+
guardingCall(g, f, inp, outp, p, c, nd, resNode) and
285297
p.checkOn(check, outcome, resNode) and
286298
guard.ensures(pragma[only_bind_into](check), outcome)
287299
)
288300
}
289301

290302
pragma[noinline]
291303
private predicate guardingCall(
292-
Function f, FunctionInput inp, FunctionOutput outp, DataFlow::Property p, CallNode c, Node nd,
293-
Node resNode
304+
Node g, Function f, FunctionInput inp, FunctionOutput outp, DataFlow::Property p, CallNode c,
305+
Node nd, Node resNode
294306
) {
295-
this.guardingFunction(f, inp, outp, p) and
307+
guardingFunction(g, f, inp, outp, p) and
296308
c = f.getACall() and
297309
nd = inp.getNode(c) and
298310
localFlow(pragma[only_bind_out](outp.getNode(c)), resNode)
@@ -308,7 +320,7 @@ abstract class BarrierGuard extends Node {
308320
* `false`, `nil` or a non-`nil` value.)
309321
*/
310322
private predicate guardingFunction(
311-
Function f, FunctionInput inp, FunctionOutput outp, DataFlow::Property p
323+
Node g, Function f, FunctionInput inp, FunctionOutput outp, DataFlow::Property p
312324
) {
313325
exists(FuncDecl fd, Node arg, Node ret |
314326
fd.getFunction() = f and
@@ -317,7 +329,7 @@ abstract class BarrierGuard extends Node {
317329
(
318330
// Case: a function like "if someBarrierGuard(arg) { return true } else { return false }"
319331
exists(ControlFlow::ConditionGuardNode guard |
320-
this.guards(guard, arg) and
332+
guards(g, guard, arg) and
321333
guard.dominates(ret.getBasicBlock())
322334
|
323335
exists(boolean b |
@@ -336,12 +348,12 @@ abstract class BarrierGuard extends Node {
336348
// or "return !someBarrierGuard(arg) && otherCond(...)"
337349
exists(boolean outcome |
338350
ret = getUniqueOutputNode(fd, outp) and
339-
this.checks(arg.asExpr(), outcome) and
351+
guardChecks(g, arg.asExpr(), outcome) and
340352
// This predicate's contract is (p holds of ret ==> arg is checked),
341353
// (and we have (this has outcome ==> arg is checked))
342354
// but p.checkOn(ret, outcome, this) gives us (ret has outcome ==> p holds of this),
343355
// so we need to swap outcome and (specifically boolean) p:
344-
DataFlow::booleanProperty(outcome).checkOn(ret, p.asBoolean(), this)
356+
DataFlow::booleanProperty(outcome).checkOn(ret, p.asBoolean(), g)
345357
)
346358
or
347359
// Case: a function like "return guardProxy(arg)"
@@ -351,7 +363,7 @@ abstract class BarrierGuard extends Node {
351363
DataFlow::Property outpProp
352364
|
353365
ret = getUniqueOutputNode(fd, outp) and
354-
this.guardingFunction(f2, inp2, outp2, outpProp) and
366+
guardingFunction(g, f2, inp2, outp2, outpProp) and
355367
c = f2.getACall() and
356368
arg = inp2.getNode(c) and
357369
(
@@ -368,6 +380,34 @@ abstract class BarrierGuard extends Node {
368380
}
369381
}
370382

383+
/**
384+
* DEPRECATED: Use `BarrierGuard` module instead.
385+
*
386+
* A guard that validates some expression.
387+
*
388+
* To use this in a configuration, extend the class and provide a
389+
* characteristic predicate precisely specifying the guard, and override
390+
* `checks` to specify what is being validated and in which branch.
391+
*
392+
* When using a data-flow or taint-flow configuration `cfg`, it is important
393+
* that any classes extending BarrierGuard in scope which are not used in `cfg`
394+
* are disjoint from any classes extending BarrierGuard in scope which are used
395+
* in `cfg`.
396+
*/
397+
abstract deprecated class BarrierGuard extends Node {
398+
/** Holds if this guard validates `e` upon evaluating to `branch`. */
399+
abstract predicate checks(Expr e, boolean branch);
400+
401+
/** Gets a node guarded by this guard. */
402+
final Node getAGuardedNode() {
403+
result = BarrierGuard<barrierGuardChecks/3>::getABarrierNodeForGuard(this)
404+
}
405+
}
406+
407+
deprecated private predicate barrierGuardChecks(Node g, Expr e, boolean branch) {
408+
g.(BarrierGuard).checks(e, branch)
409+
}
410+
371411
DataFlow::Node getUniqueOutputNode(FuncDecl fd, FunctionOutput outp) {
372412
result = unique(DataFlow::Node n | n = outp.getEntryNode(fd) | n)
373413
}

go/ql/lib/semmle/go/dataflow/internal/TaintTrackingUtil.qll

Lines changed: 27 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -228,16 +228,22 @@ abstract class DefaultTaintSanitizer extends DataFlow::Node { }
228228
predicate defaultTaintSanitizer(DataFlow::Node node) { node instanceof DefaultTaintSanitizer }
229229

230230
/**
231+
* DEPRECATED: Use `DefaultTaintSanitizer` instead.
232+
*
231233
* A sanitizer guard in all global taint flow configurations but not in local taint.
232234
*/
233-
abstract class DefaultTaintSanitizerGuard extends DataFlow::BarrierGuard { }
235+
abstract deprecated class DefaultTaintSanitizerGuard extends DataFlow::BarrierGuard { }
234236

235-
/**
236-
* Holds if `guard` should be a sanitizer guard in all global taint flow configurations
237-
* but not in local taint.
238-
*/
239-
predicate defaultTaintSanitizerGuard(DataFlow::BarrierGuard guard) {
240-
guard instanceof DefaultTaintSanitizerGuard
237+
private predicate equalityTestGuard(DataFlow::Node g, Expr e, boolean outcome) {
238+
exists(DataFlow::EqualityTestNode eq, DataFlow::Node nonConstNode |
239+
eq = g and
240+
eq.getAnOperand().isConst() and
241+
nonConstNode = eq.getAnOperand() and
242+
not nonConstNode.isConst() and
243+
not eq.getAnOperand() = Builtin::nil().getARead() and
244+
e = nonConstNode.asExpr() and
245+
outcome = eq.getPolarity()
246+
)
241247
}
242248

243249
/**
@@ -247,20 +253,8 @@ predicate defaultTaintSanitizerGuard(DataFlow::BarrierGuard guard) {
247253
* Note that comparisons to `nil` are excluded. This is needed for performance
248254
* reasons.
249255
*/
250-
class EqualityTestGuard extends DefaultTaintSanitizerGuard, DataFlow::EqualityTestNode {
251-
DataFlow::Node nonConstNode;
252-
253-
EqualityTestGuard() {
254-
this.getAnOperand().isConst() and
255-
nonConstNode = this.getAnOperand() and
256-
not nonConstNode.isConst() and
257-
not this.getAnOperand() = Builtin::nil().getARead()
258-
}
259-
260-
override predicate checks(Expr e, boolean outcome) {
261-
e = nonConstNode.asExpr() and
262-
outcome = this.getPolarity()
263-
}
256+
class EqualityTestBarrier extends DefaultTaintSanitizer {
257+
EqualityTestBarrier() { this = DataFlow::BarrierGuard<equalityTestGuard/3>::getABarrierNode() }
264258
}
265259

266260
/**
@@ -398,6 +392,16 @@ predicate inputIsConstantIfOutputHasProperty(
398392
)
399393
}
400394

395+
private predicate listOfConstantsComparisonSanitizerGuard(DataFlow::Node g, Expr e, boolean outcome) {
396+
exists(DataFlow::Node guardedExpr |
397+
exists(DataFlow::Node outputNode, DataFlow::Property p |
398+
inputIsConstantIfOutputHasProperty(guardedExpr, outputNode, p) and
399+
p.checkOn(g, outcome, outputNode)
400+
) and
401+
e = guardedExpr.asExpr()
402+
)
403+
}
404+
401405
/**
402406
* A comparison against a list of constants, acting as a sanitizer guard for
403407
* `guardedExpr` by restricting it to a known value.
@@ -406,18 +410,8 @@ predicate inputIsConstantIfOutputHasProperty(
406410
* it could equally look for a check for membership of a constant map or
407411
* constant array, which does not need to be in its own function.
408412
*/
409-
class ListOfConstantsComparisonSanitizerGuard extends TaintTracking::DefaultTaintSanitizerGuard {
410-
DataFlow::Node guardedExpr;
411-
boolean outcome;
412-
413+
class ListOfConstantsComparisonSanitizerGuard extends TaintTracking::DefaultTaintSanitizer {
413414
ListOfConstantsComparisonSanitizerGuard() {
414-
exists(DataFlow::Node outputNode, DataFlow::Property p |
415-
inputIsConstantIfOutputHasProperty(guardedExpr, outputNode, p) and
416-
p.checkOn(this, outcome, outputNode)
417-
)
418-
}
419-
420-
override predicate checks(Expr e, boolean branch) {
421-
e = guardedExpr.asExpr() and branch = outcome
415+
this = DataFlow::BarrierGuard<listOfConstantsComparisonSanitizerGuard/3>::getABarrierNode()
422416
}
423417
}

go/ql/lib/semmle/go/security/AllocationSizeOverflow.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ module AllocationSizeOverflow {
2323

2424
override predicate isSink(DataFlow::Node nd) { nd = Builtin::len().getACall().getArgument(0) }
2525

26-
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
26+
deprecated override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
2727
guard instanceof SanitizerGuard
2828
}
2929

@@ -64,7 +64,7 @@ module AllocationSizeOverflow {
6464
)
6565
}
6666

67-
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
67+
deprecated override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
6868
guard instanceof SanitizerGuard
6969
}
7070

0 commit comments

Comments
 (0)