Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit f14303a

Browse files
committed
JS: Port ConditionalBypass
1 parent 2296a27 commit f14303a

3 files changed

Lines changed: 130 additions & 114 deletions

File tree

javascript/ql/lib/semmle/javascript/security/dataflow/ConditionalBypassQuery.qll

Lines changed: 85 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,28 @@ import ConditionalBypassCustomizations::ConditionalBypass
1313
/**
1414
* A taint tracking configuration for bypass of sensitive action guards.
1515
*/
16-
class Configuration extends TaintTracking::Configuration {
16+
module ConditionalBypassConfig implements DataFlow::ConfigSig {
17+
predicate isSource(DataFlow::Node source) { source instanceof Source }
18+
19+
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
20+
21+
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
22+
23+
predicate isAdditionalFlowStep(DataFlow::Node src, DataFlow::Node dst) {
24+
// comparing a tainted expression against a constant gives a tainted result
25+
dst.asExpr().(Comparison).hasOperands(src.asExpr(), any(ConstantExpr c))
26+
}
27+
}
28+
29+
/**
30+
* Taint tracking flow for bypass of sensitive action guards.
31+
*/
32+
module ConditionalBypassFlow = TaintTracking::Global<ConditionalBypassConfig>;
33+
34+
/**
35+
* DEPRECATED. Use the `ConditionalBypassFlow` module instead.
36+
*/
37+
deprecated class Configuration extends TaintTracking::Configuration {
1738
Configuration() { this = "ConditionalBypass" }
1839

1940
override predicate isSource(DataFlow::Node source) { source instanceof Source }
@@ -26,8 +47,7 @@ class Configuration extends TaintTracking::Configuration {
2647
}
2748

2849
override predicate isAdditionalTaintStep(DataFlow::Node src, DataFlow::Node dst) {
29-
// comparing a tainted expression against a constant gives a tainted result
30-
dst.asExpr().(Comparison).hasOperands(src.asExpr(), any(ConstantExpr c))
50+
ConditionalBypassConfig::isAdditionalFlowStep(src, dst)
3151
}
3252
}
3353

@@ -72,7 +92,67 @@ class SensitiveActionGuardComparisonOperand extends Sink {
7292
* If flow from `source` taints `sink`, then an attacker can
7393
* control if `action` should be executed or not.
7494
*/
75-
predicate isTaintedGuardForSensitiveAction(
95+
predicate isTaintedGuardNodeForSensitiveAction(
96+
ConditionalBypassFlow::PathNode sink, ConditionalBypassFlow::PathNode source,
97+
SensitiveAction action
98+
) {
99+
action = sink.getNode().(Sink).getAction() and
100+
// exclude the intermediary sink
101+
not sink.getNode() instanceof SensitiveActionGuardComparisonOperand and
102+
(
103+
// ordinary taint tracking to a guard
104+
ConditionalBypassFlow::flowPath(source, sink)
105+
or
106+
// taint tracking to both operands of a guard comparison
107+
exists(
108+
SensitiveActionGuardComparison cmp, ConditionalBypassFlow::PathNode lSource,
109+
ConditionalBypassFlow::PathNode rSource, ConditionalBypassFlow::PathNode lSink,
110+
ConditionalBypassFlow::PathNode rSink
111+
|
112+
sink.getNode() = cmp.getGuard() and
113+
ConditionalBypassFlow::flowPath(lSource, lSink) and
114+
lSink.getNode() = DataFlow::valueNode(cmp.getLeftOperand()) and
115+
ConditionalBypassFlow::flowPath(rSource, rSink) and
116+
rSink.getNode() = DataFlow::valueNode(cmp.getRightOperand())
117+
|
118+
source = lSource or
119+
source = rSource
120+
)
121+
)
122+
}
123+
124+
/**
125+
* Holds if `e` effectively guards access to `action` by returning or throwing early.
126+
*
127+
* Example: `if (e) return; action(x)`.
128+
*/
129+
predicate isEarlyAbortGuardNode(ConditionalBypassFlow::PathNode e, SensitiveAction action) {
130+
exists(IfStmt guard |
131+
// `e` is in the condition of an if-statement ...
132+
e.getNode().(Sink).asExpr().getParentExpr*() = guard.getCondition() and
133+
// ... where the then-branch always throws or returns
134+
exists(Stmt abort |
135+
abort instanceof ThrowStmt or
136+
abort instanceof ReturnStmt
137+
|
138+
abort.nestedIn(guard) and
139+
abort.getBasicBlock().(ReachableBasicBlock).postDominates(guard.getThen().getBasicBlock())
140+
) and
141+
// ... and the else-branch does not exist
142+
not exists(guard.getElse())
143+
|
144+
// ... and `action` is outside the if-statement
145+
not action.asExpr().getEnclosingStmt().nestedIn(guard)
146+
)
147+
}
148+
149+
/**
150+
* Holds if `sink` guards `action`, and `source` taints `sink`.
151+
*
152+
* If flow from `source` taints `sink`, then an attacker can
153+
* control if `action` should be executed or not.
154+
*/
155+
deprecated predicate isTaintedGuardForSensitiveAction(
76156
DataFlow::PathNode sink, DataFlow::PathNode source, SensitiveAction action
77157
) {
78158
action = sink.getNode().(Sink).getAction() and
@@ -104,7 +184,7 @@ predicate isTaintedGuardForSensitiveAction(
104184
*
105185
* Example: `if (e) return; action(x)`.
106186
*/
107-
predicate isEarlyAbortGuard(DataFlow::PathNode e, SensitiveAction action) {
187+
deprecated predicate isEarlyAbortGuard(DataFlow::PathNode e, SensitiveAction action) {
108188
exists(IfStmt guard |
109189
// `e` is in the condition of an if-statement ...
110190
e.getNode().(Sink).asExpr().getParentExpr*() = guard.getCondition() and

javascript/ql/src/Security/CWE-807/ConditionalBypass.ql

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,13 @@
1313

1414
import javascript
1515
import semmle.javascript.security.dataflow.ConditionalBypassQuery
16-
import DataFlow::PathGraph
16+
import ConditionalBypassFlow::PathGraph
1717

18-
from DataFlow::PathNode source, DataFlow::PathNode sink, SensitiveAction action
18+
from
19+
ConditionalBypassFlow::PathNode source, ConditionalBypassFlow::PathNode sink,
20+
SensitiveAction action
1921
where
20-
isTaintedGuardForSensitiveAction(sink, source, action) and
21-
not isEarlyAbortGuard(sink, action)
22+
isTaintedGuardNodeForSensitiveAction(sink, source, action) and
23+
not isEarlyAbortGuardNode(sink, action)
2224
select sink.getNode(), source, sink, "This condition guards a sensitive $@, but a $@ controls it.",
2325
action, "action", source.getNode(), "user-provided value"

javascript/ql/test/query-tests/Security/CWE-807/ConditionalBypass.expected

Lines changed: 39 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -1,122 +1,56 @@
1-
nodes
2-
| example_bypass.js:6:9:6:19 | req.cookies |
3-
| example_bypass.js:6:9:6:19 | req.cookies |
4-
| example_bypass.js:6:9:6:34 | req.coo ... nUserId |
5-
| example_bypass.js:6:9:6:34 | req.coo ... nUserId |
6-
| example_bypass.js:6:40:6:56 | req.params.userId |
7-
| example_bypass.js:6:40:6:56 | req.params.userId |
8-
| example_bypass.js:6:40:6:56 | req.params.userId |
9-
| example_bypass.js:17:46:17:62 | req.params.userId |
10-
| example_bypass.js:17:46:17:62 | req.params.userId |
11-
| example_bypass.js:17:46:17:62 | req.params.userId |
12-
| tst.js:9:8:9:26 | req.params.shutDown |
13-
| tst.js:9:8:9:26 | req.params.shutDown |
14-
| tst.js:9:8:9:26 | req.params.shutDown |
15-
| tst.js:13:9:13:19 | req.cookies |
16-
| tst.js:13:9:13:19 | req.cookies |
17-
| tst.js:13:9:13:30 | req.coo ... inThing |
18-
| tst.js:13:9:13:30 | req.coo ... inThing |
19-
| tst.js:27:9:27:37 | v3 |
20-
| tst.js:27:14:27:37 | id(req. ... okieId) |
21-
| tst.js:27:17:27:27 | req.cookies |
22-
| tst.js:27:17:27:27 | req.cookies |
23-
| tst.js:27:17:27:36 | req.cookies.cookieId |
24-
| tst.js:28:9:28:10 | v3 |
25-
| tst.js:28:9:28:10 | v3 |
26-
| tst.js:33:13:33:23 | req.cookies |
27-
| tst.js:33:13:33:23 | req.cookies |
28-
| tst.js:33:13:33:32 | req.cookies.cookieId |
29-
| tst.js:33:13:33:32 | req.cookies.cookieId |
30-
| tst.js:38:9:38:19 | req.cookies |
31-
| tst.js:38:9:38:19 | req.cookies |
32-
| tst.js:38:9:38:28 | req.cookies.cookieId |
33-
| tst.js:38:9:38:28 | req.cookies.cookieId |
34-
| tst.js:44:8:44:23 | req.params.login |
35-
| tst.js:44:8:44:23 | req.params.login |
36-
| tst.js:44:8:44:23 | req.params.login |
37-
| tst.js:57:8:57:23 | req.params.login |
38-
| tst.js:57:8:57:23 | req.params.login |
39-
| tst.js:57:8:57:23 | req.params.login |
40-
| tst.js:61:9:61:19 | req.cookies |
41-
| tst.js:61:9:61:19 | req.cookies |
42-
| tst.js:61:9:61:28 | req.cookies.cookieId |
43-
| tst.js:61:9:61:28 | req.cookies.cookieId |
44-
| tst.js:61:34:61:53 | req.params.requestId |
45-
| tst.js:61:34:61:53 | req.params.requestId |
46-
| tst.js:61:34:61:53 | req.params.requestId |
47-
| tst.js:65:14:65:24 | req.cookies |
48-
| tst.js:65:14:65:24 | req.cookies |
49-
| tst.js:65:14:65:33 | req.cookies.cookieId |
50-
| tst.js:65:14:65:33 | req.cookies.cookieId |
51-
| tst.js:65:39:65:58 | req.params.requestId |
52-
| tst.js:65:39:65:58 | req.params.requestId |
53-
| tst.js:65:39:65:58 | req.params.requestId |
54-
| tst.js:78:9:78:19 | req.cookies |
55-
| tst.js:78:9:78:19 | req.cookies |
56-
| tst.js:78:9:78:28 | req.cookies.cookieId |
57-
| tst.js:78:9:78:28 | req.cookies.cookieId |
58-
| tst.js:78:9:78:41 | req.coo ... secret" |
59-
| tst.js:78:9:78:41 | req.coo ... secret" |
60-
| tst.js:91:10:91:17 | req.body |
61-
| tst.js:91:10:91:17 | req.body |
62-
| tst.js:91:10:91:17 | req.body |
63-
| tst.js:98:13:98:32 | req.query.vulnerable |
64-
| tst.js:98:13:98:32 | req.query.vulnerable |
65-
| tst.js:98:13:98:32 | req.query.vulnerable |
66-
| tst.js:105:13:105:32 | req.query.vulnerable |
67-
| tst.js:105:13:105:32 | req.query.vulnerable |
68-
| tst.js:105:13:105:32 | req.query.vulnerable |
69-
| tst.js:113:13:113:32 | req.query.vulnerable |
70-
| tst.js:113:13:113:32 | req.query.vulnerable |
71-
| tst.js:113:13:113:32 | req.query.vulnerable |
721
edges
732
| example_bypass.js:6:9:6:19 | req.cookies | example_bypass.js:6:9:6:34 | req.coo ... nUserId |
74-
| example_bypass.js:6:9:6:19 | req.cookies | example_bypass.js:6:9:6:34 | req.coo ... nUserId |
75-
| example_bypass.js:6:9:6:19 | req.cookies | example_bypass.js:6:9:6:34 | req.coo ... nUserId |
76-
| example_bypass.js:6:9:6:19 | req.cookies | example_bypass.js:6:9:6:34 | req.coo ... nUserId |
77-
| example_bypass.js:6:40:6:56 | req.params.userId | example_bypass.js:6:40:6:56 | req.params.userId |
78-
| example_bypass.js:17:46:17:62 | req.params.userId | example_bypass.js:17:46:17:62 | req.params.userId |
79-
| tst.js:9:8:9:26 | req.params.shutDown | tst.js:9:8:9:26 | req.params.shutDown |
80-
| tst.js:13:9:13:19 | req.cookies | tst.js:13:9:13:30 | req.coo ... inThing |
81-
| tst.js:13:9:13:19 | req.cookies | tst.js:13:9:13:30 | req.coo ... inThing |
82-
| tst.js:13:9:13:19 | req.cookies | tst.js:13:9:13:30 | req.coo ... inThing |
833
| tst.js:13:9:13:19 | req.cookies | tst.js:13:9:13:30 | req.coo ... inThing |
84-
| tst.js:27:9:27:37 | v3 | tst.js:28:9:28:10 | v3 |
4+
| tst.js:24:17:24:17 | v | tst.js:25:16:25:16 | v |
855
| tst.js:27:9:27:37 | v3 | tst.js:28:9:28:10 | v3 |
866
| tst.js:27:14:27:37 | id(req. ... okieId) | tst.js:27:9:27:37 | v3 |
877
| tst.js:27:17:27:27 | req.cookies | tst.js:27:17:27:36 | req.cookies.cookieId |
88-
| tst.js:27:17:27:27 | req.cookies | tst.js:27:17:27:36 | req.cookies.cookieId |
8+
| tst.js:27:17:27:36 | req.cookies.cookieId | tst.js:24:17:24:17 | v |
899
| tst.js:27:17:27:36 | req.cookies.cookieId | tst.js:27:14:27:37 | id(req. ... okieId) |
9010
| tst.js:33:13:33:23 | req.cookies | tst.js:33:13:33:32 | req.cookies.cookieId |
91-
| tst.js:33:13:33:23 | req.cookies | tst.js:33:13:33:32 | req.cookies.cookieId |
92-
| tst.js:33:13:33:23 | req.cookies | tst.js:33:13:33:32 | req.cookies.cookieId |
93-
| tst.js:33:13:33:23 | req.cookies | tst.js:33:13:33:32 | req.cookies.cookieId |
94-
| tst.js:38:9:38:19 | req.cookies | tst.js:38:9:38:28 | req.cookies.cookieId |
9511
| tst.js:38:9:38:19 | req.cookies | tst.js:38:9:38:28 | req.cookies.cookieId |
96-
| tst.js:38:9:38:19 | req.cookies | tst.js:38:9:38:28 | req.cookies.cookieId |
97-
| tst.js:38:9:38:19 | req.cookies | tst.js:38:9:38:28 | req.cookies.cookieId |
98-
| tst.js:44:8:44:23 | req.params.login | tst.js:44:8:44:23 | req.params.login |
99-
| tst.js:57:8:57:23 | req.params.login | tst.js:57:8:57:23 | req.params.login |
100-
| tst.js:61:9:61:19 | req.cookies | tst.js:61:9:61:28 | req.cookies.cookieId |
10112
| tst.js:61:9:61:19 | req.cookies | tst.js:61:9:61:28 | req.cookies.cookieId |
102-
| tst.js:61:9:61:19 | req.cookies | tst.js:61:9:61:28 | req.cookies.cookieId |
103-
| tst.js:61:9:61:19 | req.cookies | tst.js:61:9:61:28 | req.cookies.cookieId |
104-
| tst.js:61:34:61:53 | req.params.requestId | tst.js:61:34:61:53 | req.params.requestId |
105-
| tst.js:65:14:65:24 | req.cookies | tst.js:65:14:65:33 | req.cookies.cookieId |
10613
| tst.js:65:14:65:24 | req.cookies | tst.js:65:14:65:33 | req.cookies.cookieId |
107-
| tst.js:65:14:65:24 | req.cookies | tst.js:65:14:65:33 | req.cookies.cookieId |
108-
| tst.js:65:14:65:24 | req.cookies | tst.js:65:14:65:33 | req.cookies.cookieId |
109-
| tst.js:65:39:65:58 | req.params.requestId | tst.js:65:39:65:58 | req.params.requestId |
110-
| tst.js:78:9:78:19 | req.cookies | tst.js:78:9:78:28 | req.cookies.cookieId |
11114
| tst.js:78:9:78:19 | req.cookies | tst.js:78:9:78:28 | req.cookies.cookieId |
11215
| tst.js:78:9:78:19 | req.cookies | tst.js:78:9:78:28 | req.cookies.cookieId |
113-
| tst.js:78:9:78:19 | req.cookies | tst.js:78:9:78:28 | req.cookies.cookieId |
114-
| tst.js:78:9:78:28 | req.cookies.cookieId | tst.js:78:9:78:41 | req.coo ... secret" |
11516
| tst.js:78:9:78:28 | req.cookies.cookieId | tst.js:78:9:78:41 | req.coo ... secret" |
116-
| tst.js:91:10:91:17 | req.body | tst.js:91:10:91:17 | req.body |
117-
| tst.js:98:13:98:32 | req.query.vulnerable | tst.js:98:13:98:32 | req.query.vulnerable |
118-
| tst.js:105:13:105:32 | req.query.vulnerable | tst.js:105:13:105:32 | req.query.vulnerable |
119-
| tst.js:113:13:113:32 | req.query.vulnerable | tst.js:113:13:113:32 | req.query.vulnerable |
17+
nodes
18+
| example_bypass.js:6:9:6:19 | req.cookies | semmle.label | req.cookies |
19+
| example_bypass.js:6:9:6:34 | req.coo ... nUserId | semmle.label | req.coo ... nUserId |
20+
| example_bypass.js:6:40:6:56 | req.params.userId | semmle.label | req.params.userId |
21+
| example_bypass.js:17:46:17:62 | req.params.userId | semmle.label | req.params.userId |
22+
| tst.js:9:8:9:26 | req.params.shutDown | semmle.label | req.params.shutDown |
23+
| tst.js:13:9:13:19 | req.cookies | semmle.label | req.cookies |
24+
| tst.js:13:9:13:30 | req.coo ... inThing | semmle.label | req.coo ... inThing |
25+
| tst.js:24:17:24:17 | v | semmle.label | v |
26+
| tst.js:25:16:25:16 | v | semmle.label | v |
27+
| tst.js:27:9:27:37 | v3 | semmle.label | v3 |
28+
| tst.js:27:14:27:37 | id(req. ... okieId) | semmle.label | id(req. ... okieId) |
29+
| tst.js:27:17:27:27 | req.cookies | semmle.label | req.cookies |
30+
| tst.js:27:17:27:36 | req.cookies.cookieId | semmle.label | req.cookies.cookieId |
31+
| tst.js:28:9:28:10 | v3 | semmle.label | v3 |
32+
| tst.js:33:13:33:23 | req.cookies | semmle.label | req.cookies |
33+
| tst.js:33:13:33:32 | req.cookies.cookieId | semmle.label | req.cookies.cookieId |
34+
| tst.js:38:9:38:19 | req.cookies | semmle.label | req.cookies |
35+
| tst.js:38:9:38:28 | req.cookies.cookieId | semmle.label | req.cookies.cookieId |
36+
| tst.js:44:8:44:23 | req.params.login | semmle.label | req.params.login |
37+
| tst.js:57:8:57:23 | req.params.login | semmle.label | req.params.login |
38+
| tst.js:61:9:61:19 | req.cookies | semmle.label | req.cookies |
39+
| tst.js:61:9:61:28 | req.cookies.cookieId | semmle.label | req.cookies.cookieId |
40+
| tst.js:61:34:61:53 | req.params.requestId | semmle.label | req.params.requestId |
41+
| tst.js:65:14:65:24 | req.cookies | semmle.label | req.cookies |
42+
| tst.js:65:14:65:33 | req.cookies.cookieId | semmle.label | req.cookies.cookieId |
43+
| tst.js:65:39:65:58 | req.params.requestId | semmle.label | req.params.requestId |
44+
| tst.js:78:9:78:19 | req.cookies | semmle.label | req.cookies |
45+
| tst.js:78:9:78:28 | req.cookies.cookieId | semmle.label | req.cookies.cookieId |
46+
| tst.js:78:9:78:28 | req.cookies.cookieId | semmle.label | req.cookies.cookieId |
47+
| tst.js:78:9:78:41 | req.coo ... secret" | semmle.label | req.coo ... secret" |
48+
| tst.js:91:10:91:17 | req.body | semmle.label | req.body |
49+
| tst.js:98:13:98:32 | req.query.vulnerable | semmle.label | req.query.vulnerable |
50+
| tst.js:105:13:105:32 | req.query.vulnerable | semmle.label | req.query.vulnerable |
51+
| tst.js:113:13:113:32 | req.query.vulnerable | semmle.label | req.query.vulnerable |
52+
subpaths
53+
| tst.js:27:17:27:36 | req.cookies.cookieId | tst.js:24:17:24:17 | v | tst.js:25:16:25:16 | v | tst.js:27:14:27:37 | id(req. ... okieId) |
12054
#select
12155
| tst.js:9:8:9:26 | req.params.shutDown | tst.js:9:8:9:26 | req.params.shutDown | tst.js:9:8:9:26 | req.params.shutDown | This condition guards a sensitive $@, but a $@ controls it. | tst.js:10:9:10:22 | process.exit() | action | tst.js:9:8:9:26 | req.params.shutDown | user-provided value |
12256
| tst.js:13:9:13:30 | req.coo ... inThing | tst.js:13:9:13:19 | req.cookies | tst.js:13:9:13:30 | req.coo ... inThing | This condition guards a sensitive $@, but a $@ controls it. | tst.js:14:9:14:17 | o.login() | action | tst.js:13:9:13:19 | req.cookies | user-provided value |

0 commit comments

Comments
 (0)