forked from github/codeql
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathConditionalBypass.ql
More file actions
115 lines (104 loc) · 3.8 KB
/
ConditionalBypass.ql
File metadata and controls
115 lines (104 loc) · 3.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
/**
* @name User-controlled bypass of security check
* @description Conditions that the user controls are not suited for making security-related decisions.
* @kind path-problem
* @problem.severity error
* @precision medium
* @id js/user-controlled-bypass
* @tags security
* external/cwe/cwe-807
* external/cwe/cwe-290
*/
import javascript
import semmle.javascript.security.dataflow.ConditionalBypass::ConditionalBypass
import DataFlow::PathGraph
/**
* Holds if the value of `nd` flows into `guard`.
*/
predicate flowsToGuardExpr(DataFlow::Node nd, SensitiveActionGuardConditional guard) {
nd = guard or
flowsToGuardExpr(nd.getASuccessor(), guard)
}
/**
* A comparison that guards a sensitive action, e.g. the comparison in:
* `var ok = x == y; if (ok) login()`.
*/
class SensitiveActionGuardComparison extends Comparison {
SensitiveActionGuardConditional guard;
SensitiveActionGuardComparison() { flowsToGuardExpr(DataFlow::valueNode(this), guard) }
/**
* Gets the guard that uses this comparison.
*/
SensitiveActionGuardConditional getGuard() { result = guard }
}
/**
* An intermediary sink to enable reuse of the taint configuration.
* This sink should not be presented to the client of this query.
*/
class SensitiveActionGuardComparisonOperand extends Sink {
SensitiveActionGuardComparison comparison;
SensitiveActionGuardComparisonOperand() { asExpr() = comparison.getAnOperand() }
override SensitiveAction getAction() { result = comparison.getGuard().getAction() }
}
/**
* Holds if `sink` guards `action`, and `source` taints `sink`.
*
* If flow from `source` taints `sink`, then an attacker can
* control if `action` should be executed or not.
*/
predicate isTaintedGuardForSensitiveAction(
DataFlow::PathNode sink, DataFlow::PathNode source, SensitiveAction action
) {
action = sink.getNode().(Sink).getAction() and
// exclude the intermediary sink
not sink.getNode() instanceof SensitiveActionGuardComparisonOperand and
exists(Configuration cfg |
// ordinary taint tracking to a guard
cfg.hasFlowPath(source, sink)
or
// taint tracking to both operands of a guard comparison
exists(
SensitiveActionGuardComparison cmp, DataFlow::PathNode lSource, DataFlow::PathNode rSource,
DataFlow::PathNode lSink, DataFlow::PathNode rSink
|
sink.getNode() = cmp.getGuard() and
cfg.hasFlowPath(lSource, lSink) and
lSink.getNode() = DataFlow::valueNode(cmp.getLeftOperand()) and
cfg.hasFlowPath(rSource, rSink) and
rSink.getNode() = DataFlow::valueNode(cmp.getRightOperand())
|
source = lSource or
source = rSource
)
)
}
/**
* Holds if `e` effectively guards access to `action` by returning or throwing early.
*
* Example: `if (e) return; action(x)`.
*/
predicate isEarlyAbortGuard(DataFlow::PathNode e, SensitiveAction action) {
exists(IfStmt guard |
// `e` is in the condition of an if-statement ...
e.getNode().(Sink).asExpr().getParentExpr*() = guard.getCondition() and
// ... where the then-branch always throws or returns
exists(Stmt abort |
abort instanceof ThrowStmt or
abort instanceof ReturnStmt
|
abort.nestedIn(guard) and
abort.getBasicBlock().(ReachableBasicBlock).postDominates(guard.getThen().getBasicBlock())
) and
// ... and the else-branch does not exist
not exists(guard.getElse())
|
// ... and `action` is outside the if-statement
not action.asExpr().getEnclosingStmt().nestedIn(guard)
)
}
from DataFlow::PathNode source, DataFlow::PathNode sink, SensitiveAction action
where
isTaintedGuardForSensitiveAction(sink, source, action) and
not isEarlyAbortGuard(sink, action)
select sink.getNode(), source, sink, "This condition guards a sensitive $@, but $@ controls it.",
action, "action", source.getNode(), "a user-provided value"