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

Skip to content

Commit d8b25ef

Browse files
committed
add data-flow steps for resolved promises using pseudo-properties
1 parent 6648e27 commit d8b25ef

3 files changed

Lines changed: 87 additions & 6 deletions

File tree

javascript/ql/src/semmle/javascript/Promises.qll

Lines changed: 68 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -121,9 +121,17 @@ class AggregateES2015PromiseDefinition extends PromiseCreationCall {
121121
}
122122

123123
/**
124-
* This module defines how exceptional data-flow propagates into and out a Promise.
124+
* This module defines how data-flow propagates into and out of a Promise.
125+
* The data-flow is based on pseudo-properties rather than tainting the Promise object (which is what `PromiseTaintStep` does).
125126
*/
126-
private module ExceptionalPromiseFlow {
127+
private module PromiseFlow {
128+
/**
129+
* Gets the pseudo-field used to describe resolved values in a promise.
130+
*/
131+
string resolveField() {
132+
result = "$PromiseResolveField$"
133+
}
134+
127135
/**
128136
* Gets the pseudo-field used to describe rejected values in a promise.
129137
*/
@@ -134,7 +142,7 @@ private module ExceptionalPromiseFlow {
134142
/**
135143
* A flow step describing a promise definition.
136144
*
137-
* The rejected value is written to a pseudo-field on the promise.
145+
* The resolved/rejected value is written to a pseudo-field on the promise.
138146
*/
139147
class PromiseDefitionStep extends DataFlow::AdditionalFlowStep {
140148
PromiseDefinition promise;
@@ -143,6 +151,10 @@ private module ExceptionalPromiseFlow {
143151
}
144152

145153
override predicate store(DataFlow::Node pred, DataFlow::Node succ, string prop) {
154+
prop = resolveField() and
155+
pred = promise.getResolveParameter().getACall().getArgument(0) and
156+
succ = this
157+
or
146158
prop = rejectField() and
147159
(
148160
pred = promise.getRejectParameter().getACall().getArgument(0) or
@@ -152,6 +164,23 @@ private module ExceptionalPromiseFlow {
152164
}
153165
}
154166

167+
/**
168+
* A flow step describing the a Promise.resolve (and similar) call.
169+
*/
170+
class CreationStep extends DataFlow::AdditionalFlowStep {
171+
PromiseCreationCall promise;
172+
CreationStep() {
173+
this = promise
174+
}
175+
176+
override predicate store(DataFlow::Node pred, DataFlow::Node succ, string prop) {
177+
prop = resolveField() and
178+
pred = promise.getValue() and
179+
succ = this
180+
}
181+
}
182+
183+
155184
/**
156185
* A load step loading the pseudo-field describing that the promise is rejected.
157186
* The rejected value is thrown as a exception.
@@ -165,6 +194,10 @@ private module ExceptionalPromiseFlow {
165194
}
166195

167196
override predicate load(DataFlow::Node pred, DataFlow::Node succ, string prop) {
197+
prop = resolveField() and
198+
succ = this and
199+
pred = operand
200+
or
168201
prop = rejectField() and
169202
succ = await.getExceptionTarget() and
170203
pred = operand
@@ -180,6 +213,10 @@ private module ExceptionalPromiseFlow {
180213
}
181214

182215
override predicate load(DataFlow::Node pred, DataFlow::Node succ, string prop) {
216+
prop = resolveField() and
217+
pred = getReceiver() and
218+
succ = getCallback(0).getParameter(0)
219+
or
183220
prop = rejectField() and
184221
pred = getReceiver() and
185222
succ = getCallback(1).getParameter(0)
@@ -193,12 +230,16 @@ private module ExceptionalPromiseFlow {
193230
}
194231

195232
override predicate store(DataFlow::Node pred, DataFlow::Node succ, string prop) {
233+
prop = resolveField() and
234+
pred = getCallback([0..1]).getAReturn() and
235+
succ = this
236+
or
196237
prop = rejectField() and
197238
pred = getCallback([0..1]).getExceptionalReturn() and
198239
succ = this
199240
}
200241
}
201-
242+
202243
/**
203244
* A flow step describing the data-flow related to the `.catch` method of a promise.
204245
*/
@@ -213,10 +254,20 @@ private module ExceptionalPromiseFlow {
213254
succ = getCallback(0).getParameter(0)
214255
}
215256

257+
override predicate copyProperty(DataFlow::Node pred, DataFlow::Node succ, string prop) {
258+
prop = resolveField() and
259+
pred = getReceiver().getALocalSource() and
260+
succ = this
261+
}
262+
216263
override predicate store(DataFlow::Node pred, DataFlow::Node succ, string prop) {
217264
prop = rejectField() and
218265
pred = getCallback(0).getExceptionalReturn() and
219266
succ = this
267+
or
268+
prop = resolveField() and
269+
pred = getCallback(0).getAReturn() and
270+
succ = this
220271
}
221272
}
222273

@@ -229,10 +280,17 @@ private module ExceptionalPromiseFlow {
229280
}
230281

231282
override predicate copyProperty(DataFlow::Node pred, DataFlow::Node succ, string prop) {
232-
prop = rejectField() and
283+
(prop = resolveField() or prop = rejectField()) and
233284
pred = getReceiver() and
234285
succ = this
235286
}
287+
288+
// a similar thing can also happen if a rejected promise is returned.
289+
override predicate store(DataFlow::Node pred, DataFlow::Node succ, string prop) {
290+
prop = rejectField() and
291+
pred = getCallback(0).getExceptionalReturn() and
292+
succ = this
293+
}
236294
}
237295
}
238296

@@ -258,10 +316,14 @@ predicate promiseTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
258316
succ = thn
259317
)
260318
or
261-
// from `p` to `p.catch(..)`
262319
exists(DataFlow::MethodCallNode catch | catch.getMethodName() = "catch" |
320+
// from `p` to `p.catch(..)`
263321
pred = catch.getReceiver() and
264322
succ = catch
323+
or
324+
// from `v` to `p.catch(x => return v)`
325+
pred = catch.getCallback(0).getAReturn() and
326+
succ = catch
265327
)
266328
or
267329
// from `p` to `p.finally(..)`

javascript/ql/test/library-tests/Promises/flow.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,4 +99,8 @@
9999
}
100100
var foo = returnsRejected(new Promise((resolve, reject) => reject(source)));
101101
sink(foo); // NOT OK!
102+
103+
new Promise((resolve, reject) => reject("BLA")).catch(x => {return source}).then(x => sink(x)); // NOT OK
104+
105+
new Promise((resolve, reject) => reject("BLA")).finally(x => {throw source}).catch(x => sink(x)); // NOT OK
102106
})();

javascript/ql/test/library-tests/Promises/tests.expected

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ test_PromiseDefinition_getARejectHandler
1818
| flow.js:32:2:32:49 | new Pro ... ource)) | flow.js:32:57:32:68 | x => sink(x) |
1919
| flow.js:42:2:42:49 | new Pro ... ource)) | flow.js:42:67:42:75 | () => { } |
2020
| flow.js:48:2:48:36 | new Pro ... urce }) | flow.js:48:44:48:55 | x => sink(x) |
21+
| flow.js:103:2:103:48 | new Pro ... "BLA")) | flow.js:103:56:103:75 | x => {return source} |
22+
| flow.js:105:2:105:48 | new Pro ... "BLA")) | flow.js:105:58:105:76 | x => {throw source} |
2123
| promises.js:10:18:17:4 | new Pro ... );\\n }) | promises.js:20:6:22:3 | (v) => ... v;\\n } |
2224
| promises.js:10:18:17:4 | new Pro ... );\\n }) | promises.js:23:18:25:3 | (v) => ... v;\\n } |
2325
| promises.js:10:18:17:4 | new Pro ... );\\n }) | promises.js:26:20:28:3 | (v) => ... v;\\n } |
@@ -38,12 +40,15 @@ test_PromiseDefinition_getExecutor
3840
| flow.js:86:23:86:70 | new Pro ... ource)) | flow.js:86:35:86:69 | (resolv ... source) |
3941
| flow.js:91:21:91:68 | new Pro ... ource)) | flow.js:91:33:91:67 | (resolv ... source) |
4042
| flow.js:100:28:100:75 | new Pro ... ource)) | flow.js:100:40:100:74 | (resolv ... source) |
43+
| flow.js:103:2:103:48 | new Pro ... "BLA")) | flow.js:103:14:103:47 | (resolv ... ("BLA") |
44+
| flow.js:105:2:105:48 | new Pro ... "BLA")) | flow.js:105:14:105:47 | (resolv ... ("BLA") |
4145
| interflow.js:11:12:15:6 | new Pro ... \\n }) | interflow.js:11:24:15:5 | functio ... ;\\n } |
4246
| promises.js:3:17:5:4 | new Pro ... );\\n }) | promises.js:3:29:5:3 | functio ... e);\\n } |
4347
| promises.js:10:18:17:4 | new Pro ... );\\n }) | promises.js:10:30:17:3 | (res, r ... e);\\n } |
4448
| promises.js:33:19:35:6 | new Pro ... \\n }) | promises.js:33:31:35:5 | functio ... ;\\n } |
4549
| promises.js:43:19:45:6 | Q.Promi ... \\n }) | promises.js:43:29:45:5 | functio ... ;\\n } |
4650
test_PromiseDefinition_getAFinallyHandler
51+
| flow.js:105:2:105:48 | new Pro ... "BLA")) | flow.js:105:58:105:76 | x => {throw source} |
4752
| promises.js:10:18:17:4 | new Pro ... );\\n }) | promises.js:26:20:28:3 | (v) => ... v;\\n } |
4853
test_PromiseDefinition
4954
| flow.js:7:11:7:59 | new Pro ... ource)) |
@@ -62,6 +67,8 @@ test_PromiseDefinition
6267
| flow.js:86:23:86:70 | new Pro ... ource)) |
6368
| flow.js:91:21:91:68 | new Pro ... ource)) |
6469
| flow.js:100:28:100:75 | new Pro ... ource)) |
70+
| flow.js:103:2:103:48 | new Pro ... "BLA")) |
71+
| flow.js:105:2:105:48 | new Pro ... "BLA")) |
6572
| interflow.js:11:12:15:6 | new Pro ... \\n }) |
6673
| promises.js:3:17:5:4 | new Pro ... );\\n }) |
6774
| promises.js:10:18:17:4 | new Pro ... );\\n }) |
@@ -76,6 +83,7 @@ test_PromiseDefinition_getAResolveHandler
7683
| flow.js:60:12:60:59 | new Pro ... ource)) | flow.js:61:21:61:28 | () => {} |
7784
| flow.js:74:10:74:57 | new Pro ... ource)) | flow.js:74:64:74:71 | () => {} |
7885
| flow.js:91:21:91:68 | new Pro ... ource)) | flow.js:91:75:91:82 | () => {} |
86+
| flow.js:105:2:105:48 | new Pro ... "BLA")) | flow.js:105:58:105:76 | x => {throw source} |
7987
| promises.js:3:17:5:4 | new Pro ... );\\n }) | promises.js:6:16:8:3 | functio ... al;\\n } |
8088
| promises.js:10:18:17:4 | new Pro ... );\\n }) | promises.js:18:17:20:3 | (v) => ... v;\\n } |
8189
| promises.js:10:18:17:4 | new Pro ... );\\n }) | promises.js:26:20:28:3 | (v) => ... v;\\n } |
@@ -97,6 +105,8 @@ test_PromiseDefinition_getRejectParameter
97105
| flow.js:86:23:86:70 | new Pro ... ource)) | flow.js:86:45:86:50 | reject |
98106
| flow.js:91:21:91:68 | new Pro ... ource)) | flow.js:91:43:91:48 | reject |
99107
| flow.js:100:28:100:75 | new Pro ... ource)) | flow.js:100:50:100:55 | reject |
108+
| flow.js:103:2:103:48 | new Pro ... "BLA")) | flow.js:103:24:103:29 | reject |
109+
| flow.js:105:2:105:48 | new Pro ... "BLA")) | flow.js:105:24:105:29 | reject |
100110
| interflow.js:11:12:15:6 | new Pro ... \\n }) | interflow.js:11:43:11:48 | reject |
101111
| promises.js:3:17:5:4 | new Pro ... );\\n }) | promises.js:3:48:3:53 | reject |
102112
| promises.js:10:18:17:4 | new Pro ... );\\n }) | promises.js:10:36:10:38 | rej |
@@ -118,6 +128,8 @@ test_PromiseDefinition_getResolveParameter
118128
| flow.js:86:23:86:70 | new Pro ... ource)) | flow.js:86:36:86:42 | resolve |
119129
| flow.js:91:21:91:68 | new Pro ... ource)) | flow.js:91:34:91:40 | resolve |
120130
| flow.js:100:28:100:75 | new Pro ... ource)) | flow.js:100:41:100:47 | resolve |
131+
| flow.js:103:2:103:48 | new Pro ... "BLA")) | flow.js:103:15:103:21 | resolve |
132+
| flow.js:105:2:105:48 | new Pro ... "BLA")) | flow.js:105:15:105:21 | resolve |
121133
| interflow.js:11:12:15:6 | new Pro ... \\n }) | interflow.js:11:34:11:40 | resolve |
122134
| promises.js:3:17:5:4 | new Pro ... );\\n }) | promises.js:3:39:3:45 | resolve |
123135
| promises.js:10:18:17:4 | new Pro ... );\\n }) | promises.js:10:31:10:33 | res |
@@ -126,6 +138,7 @@ test_PromiseDefinition_getResolveParameter
126138
test_PromiseDefinition_getACatchHandler
127139
| flow.js:32:2:32:49 | new Pro ... ource)) | flow.js:32:57:32:68 | x => sink(x) |
128140
| flow.js:48:2:48:36 | new Pro ... urce }) | flow.js:48:44:48:55 | x => sink(x) |
141+
| flow.js:103:2:103:48 | new Pro ... "BLA")) | flow.js:103:56:103:75 | x => {return source} |
129142
| promises.js:10:18:17:4 | new Pro ... );\\n }) | promises.js:23:18:25:3 | (v) => ... v;\\n } |
130143
flow
131144
| flow.js:2:15:2:22 | "source" | flow.js:5:7:5:14 | await p1 |
@@ -150,4 +163,6 @@ flow
150163
| flow.js:2:15:2:22 | "source" | flow.js:84:21:84:21 | e |
151164
| flow.js:2:15:2:22 | "source" | flow.js:89:45:89:45 | e |
152165
| flow.js:2:15:2:22 | "source" | flow.js:101:7:101:9 | foo |
166+
| flow.js:2:15:2:22 | "source" | flow.js:103:93:103:93 | x |
167+
| flow.js:2:15:2:22 | "source" | flow.js:105:95:105:95 | x |
153168
| interflow.js:3:18:3:25 | "source" | interflow.js:18:10:18:14 | error |

0 commit comments

Comments
 (0)