3
3
*/
4
4
5
5
import javascript
6
+ private import dataflow.internal.StepSummary
6
7
7
8
/**
8
9
* A definition of a `Promise` object.
@@ -121,46 +122,166 @@ class AggregateES2015PromiseDefinition extends PromiseCreationCall {
121
122
}
122
123
123
124
/**
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).
125
+ * Common predicates shared between type-tracking and data-flow for promises.
126
126
*/
127
- private module PromiseFlow {
127
+ module Promises {
128
128
/**
129
129
* Gets the pseudo-field used to describe resolved values in a promise.
130
130
*/
131
- string resolveField ( ) { result = "$PromiseResolveField$" }
131
+ string valueProp ( ) { result = "$PromiseResolveField$" }
132
132
133
133
/**
134
134
* Gets the pseudo-field used to describe rejected values in a promise.
135
135
*/
136
- string rejectField ( ) { result = "$PromiseRejectField$" }
136
+ string errorProp ( ) { result = "$PromiseRejectField$" }
137
+ }
138
+
139
+ /**
140
+ * A module for supporting promises in type-tracking predicates.
141
+ * The `PromiseTypeTracking::promiseStep` predicate is used for type tracking in and out of promises,
142
+ * and is included in the standard type-tracking steps (`SourceNode::track`).
143
+ * The `TypeTracker::startInPromise()` predicate can be used to initiate a type-tracker
144
+ * where the tracked value is a promise.
145
+ *
146
+ * The below is an example of a type-tracking predicate where the initial value is a promise:
147
+ * ```
148
+ * DataFlow::SourceNode myType(DataFlow::TypeTracker t) {
149
+ * t.startInPromise() and
150
+ * result = <the promise value> and
151
+ * or
152
+ * exists(DataFlow::TypeTracker t2 | result = myType(t2).track(t2, t))
153
+ * }
154
+ * ```
155
+ *
156
+ * The type-tracking predicate above will only end (`t = DataFlow::TypeTracker::end()`) after the tracked value has been
157
+ * extracted from the promise.
158
+ *
159
+ * The `PromiseTypeTracking::promiseStep` predicate can be used instead of `SourceNode::track`
160
+ * to get type-tracking only for promise steps.
161
+ *
162
+ * Replace `t.startInPromise()` in the above example with `t.start()` to create a type-tracking predicate
163
+ * where the value is not initially inside a promise.
164
+ */
165
+ module PromiseTypeTracking {
166
+ /**
167
+ * Gets the result from a single step through a promise, from `pred` to `result` summarized by `summary`.
168
+ * This can be loading a resolved value from a promise, storing a value in a promise, or copying a resolved value from one promise to another.
169
+ */
170
+ DataFlow:: SourceNode promiseStep ( DataFlow:: SourceNode pred , StepSummary summary ) {
171
+ exists ( PromiseFlowStep step , string field | field = Promises:: valueProp ( ) |
172
+ summary = LoadStep ( field ) and
173
+ step .load ( pred , result , field )
174
+ or
175
+ summary = StoreStep ( field ) and
176
+ step .store ( pred , result , field )
177
+ or
178
+ summary = LevelStep ( ) and
179
+ step .loadStore ( pred , result , field )
180
+ )
181
+ }
182
+
183
+ /**
184
+ * Gets the result from a single step through a promise, from `pred` with tracker `t2` to `result` with tracker `t`.
185
+ * This can be loading a resolved value from a promise, storing a value in a promise, or copying a resolved value from one promise to another.
186
+ */
187
+ DataFlow:: SourceNode promiseStep (
188
+ DataFlow:: SourceNode pred , DataFlow:: TypeTracker t , DataFlow:: TypeTracker t2
189
+ ) {
190
+ exists ( StepSummary summary |
191
+ result = PromiseTypeTracking:: promiseStep ( pred , summary ) and
192
+ t = t2 .append ( summary )
193
+ )
194
+ }
195
+
196
+ /**
197
+ * A class enabling the use of the `resolveField` as a pseudo-property in type-tracking predicates.
198
+ */
199
+ private class ResolveFieldAsTypeTrackingProperty extends TypeTrackingPseudoProperty {
200
+ ResolveFieldAsTypeTrackingProperty ( ) { this = Promises:: valueProp ( ) }
201
+ }
202
+ }
203
+
204
+ /**
205
+ * An `AdditionalFlowStep` used to model a data-flow step related to promises.
206
+ *
207
+ * The `loadStep`/`storeStep`/`loadStoreStep` methods are overloaded such that the new predicates
208
+ * `load`/`store`/`loadStore` can be used in the `PromiseTypeTracking` module.
209
+ * (Thereby avoiding conflicts with a "cousin" `AdditionalFlowStep` implementation.)
210
+ *
211
+ * The class is private and is only intended to be used inside the `PromiseTypeTracking` and `PromiseFlow` modules.
212
+ */
213
+ abstract private class PromiseFlowStep extends DataFlow:: AdditionalFlowStep {
214
+ final override predicate step ( DataFlow:: Node pred , DataFlow:: Node succ ) { none ( ) }
215
+
216
+ final override predicate step (
217
+ DataFlow:: Node p , DataFlow:: Node s , DataFlow:: FlowLabel pl , DataFlow:: FlowLabel sl
218
+ ) {
219
+ none ( )
220
+ }
221
+
222
+ /**
223
+ * Holds if the property `prop` of the object `pred` should be loaded into `succ`.
224
+ */
225
+ predicate load ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) { none ( ) }
226
+
227
+ final override predicate loadStep ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
228
+ this .load ( pred , succ , prop )
229
+ }
230
+
231
+ /**
232
+ * Holds if `pred` should be stored in the object `succ` under the property `prop`.
233
+ */
234
+ predicate store ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) { none ( ) }
235
+
236
+ final override predicate storeStep ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
237
+ this .store ( pred , succ , prop )
238
+ }
239
+
240
+ /**
241
+ * Holds if the property `prop` should be copied from the object `pred` to the object `succ`.
242
+ */
243
+ predicate loadStore ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) { none ( ) }
244
+
245
+ final override predicate loadStoreStep ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
246
+ this .loadStore ( pred , succ , prop )
247
+ }
248
+ }
249
+
250
+ /**
251
+ * This module defines how data-flow propagates into and out of a Promise.
252
+ * The data-flow is based on pseudo-properties rather than tainting the Promise object (which is what `PromiseTaintStep` does).
253
+ */
254
+ private module PromiseFlow {
255
+ private predicate valueProp = Promises:: valueProp / 0 ;
256
+
257
+ private predicate errorProp = Promises:: errorProp / 0 ;
137
258
138
259
/**
139
260
* A flow step describing a promise definition.
140
261
*
141
262
* The resolved/rejected value is written to a pseudo-field on the promise.
142
263
*/
143
- class PromiseDefitionStep extends DataFlow :: AdditionalFlowStep {
264
+ class PromiseDefitionStep extends PromiseFlowStep {
144
265
PromiseDefinition promise ;
145
266
146
267
PromiseDefitionStep ( ) { this = promise }
147
268
148
- override predicate storeStep ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
149
- prop = resolveField ( ) and
269
+ override predicate store ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
270
+ prop = valueProp ( ) and
150
271
pred = promise .getResolveParameter ( ) .getACall ( ) .getArgument ( 0 ) and
151
272
succ = this
152
273
or
153
- prop = rejectField ( ) and
274
+ prop = errorProp ( ) and
154
275
(
155
276
pred = promise .getRejectParameter ( ) .getACall ( ) .getArgument ( 0 ) or
156
277
pred = promise .getExecutor ( ) .getExceptionalReturn ( )
157
278
) and
158
279
succ = this
159
280
}
160
281
161
- override predicate loadStoreStep ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
282
+ override predicate loadStore ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
162
283
// Copy the value of a resolved promise to the value of this promise.
163
- prop = resolveField ( ) and
284
+ prop = valueProp ( ) and
164
285
pred = promise .getResolveParameter ( ) .getACall ( ) .getArgument ( 0 ) and
165
286
succ = this
166
287
}
@@ -169,20 +290,20 @@ private module PromiseFlow {
169
290
/**
170
291
* A flow step describing the a Promise.resolve (and similar) call.
171
292
*/
172
- class CreationStep extends DataFlow :: AdditionalFlowStep {
293
+ class CreationStep extends PromiseFlowStep {
173
294
PromiseCreationCall promise ;
174
295
175
296
CreationStep ( ) { this = promise }
176
297
177
- override predicate storeStep ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
178
- prop = resolveField ( ) and
298
+ override predicate store ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
299
+ prop = valueProp ( ) and
179
300
pred = promise .getValue ( ) and
180
301
succ = this
181
302
}
182
303
183
- override predicate loadStoreStep ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
304
+ override predicate loadStore ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
184
305
// Copy the value of a resolved promise to the value of this promise.
185
- prop = resolveField ( ) and
306
+ prop = valueProp ( ) and
186
307
pred = promise .getValue ( ) and
187
308
succ = this
188
309
}
@@ -192,7 +313,7 @@ private module PromiseFlow {
192
313
* A load step loading the pseudo-field describing that the promise is rejected.
193
314
* The rejected value is thrown as a exception.
194
315
*/
195
- class AwaitStep extends DataFlow :: AdditionalFlowStep {
316
+ class AwaitStep extends PromiseFlowStep {
196
317
DataFlow:: Node operand ;
197
318
AwaitExpr await ;
198
319
@@ -201,12 +322,12 @@ private module PromiseFlow {
201
322
operand .getEnclosingExpr ( ) = await .getOperand ( )
202
323
}
203
324
204
- override predicate loadStep ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
205
- prop = resolveField ( ) and
325
+ override predicate load ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
326
+ prop = valueProp ( ) and
206
327
succ = this and
207
328
pred = operand
208
329
or
209
- prop = rejectField ( ) and
330
+ prop = errorProp ( ) and
210
331
succ = await .getExceptionTarget ( ) and
211
332
pred = operand
212
333
}
@@ -215,37 +336,37 @@ private module PromiseFlow {
215
336
/**
216
337
* A flow step describing the data-flow related to the `.then` method of a promise.
217
338
*/
218
- class ThenStep extends DataFlow :: AdditionalFlowStep , DataFlow:: MethodCallNode {
339
+ class ThenStep extends PromiseFlowStep , DataFlow:: MethodCallNode {
219
340
ThenStep ( ) { this .getMethodName ( ) = "then" }
220
341
221
- override predicate loadStep ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
222
- prop = resolveField ( ) and
342
+ override predicate load ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
343
+ prop = valueProp ( ) and
223
344
pred = getReceiver ( ) and
224
345
succ = getCallback ( 0 ) .getParameter ( 0 )
225
346
or
226
- prop = rejectField ( ) and
347
+ prop = errorProp ( ) and
227
348
pred = getReceiver ( ) and
228
349
succ = getCallback ( 1 ) .getParameter ( 0 )
229
350
}
230
351
231
- override predicate loadStoreStep ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
352
+ override predicate loadStore ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
232
353
not exists ( this .getArgument ( 1 ) ) and
233
- prop = rejectField ( ) and
354
+ prop = errorProp ( ) and
234
355
pred = getReceiver ( ) and
235
356
succ = this
236
357
or
237
358
// read the value of a resolved/rejected promise that is returned
238
- ( prop = rejectField ( ) or prop = resolveField ( ) ) and
359
+ ( prop = errorProp ( ) or prop = valueProp ( ) ) and
239
360
pred = getCallback ( [ 0 .. 1 ] ) .getAReturn ( ) and
240
361
succ = this
241
362
}
242
363
243
- override predicate storeStep ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
244
- prop = resolveField ( ) and
364
+ override predicate store ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
365
+ prop = valueProp ( ) and
245
366
pred = getCallback ( [ 0 .. 1 ] ) .getAReturn ( ) and
246
367
succ = this
247
368
or
248
- prop = rejectField ( ) and
369
+ prop = errorProp ( ) and
249
370
pred = getCallback ( [ 0 .. 1 ] ) .getExceptionalReturn ( ) and
250
371
succ = this
251
372
}
@@ -254,32 +375,32 @@ private module PromiseFlow {
254
375
/**
255
376
* A flow step describing the data-flow related to the `.catch` method of a promise.
256
377
*/
257
- class CatchStep extends DataFlow :: AdditionalFlowStep , DataFlow:: MethodCallNode {
378
+ class CatchStep extends PromiseFlowStep , DataFlow:: MethodCallNode {
258
379
CatchStep ( ) { this .getMethodName ( ) = "catch" }
259
380
260
- override predicate loadStep ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
261
- prop = rejectField ( ) and
381
+ override predicate load ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
382
+ prop = errorProp ( ) and
262
383
pred = getReceiver ( ) and
263
384
succ = getCallback ( 0 ) .getParameter ( 0 )
264
385
}
265
386
266
- override predicate loadStoreStep ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
267
- prop = resolveField ( ) and
387
+ override predicate loadStore ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
388
+ prop = valueProp ( ) and
268
389
pred = getReceiver ( ) .getALocalSource ( ) and
269
390
succ = this
270
391
or
271
392
// read the value of a resolved/rejected promise that is returned
272
- ( prop = rejectField ( ) or prop = resolveField ( ) ) and
393
+ ( prop = errorProp ( ) or prop = valueProp ( ) ) and
273
394
pred = getCallback ( 0 ) .getAReturn ( ) and
274
395
succ = this
275
396
}
276
397
277
- override predicate storeStep ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
278
- prop = rejectField ( ) and
398
+ override predicate store ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
399
+ prop = errorProp ( ) and
279
400
pred = getCallback ( 0 ) .getExceptionalReturn ( ) and
280
401
succ = this
281
402
or
282
- prop = resolveField ( ) and
403
+ prop = valueProp ( ) and
283
404
pred = getCallback ( 0 ) .getAReturn ( ) and
284
405
succ = this
285
406
}
@@ -288,22 +409,22 @@ private module PromiseFlow {
288
409
/**
289
410
* A flow step describing the data-flow related to the `.finally` method of a promise.
290
411
*/
291
- class FinallyStep extends DataFlow :: AdditionalFlowStep , DataFlow:: MethodCallNode {
412
+ class FinallyStep extends PromiseFlowStep , DataFlow:: MethodCallNode {
292
413
FinallyStep ( ) { this .getMethodName ( ) = "finally" }
293
414
294
- override predicate loadStoreStep ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
295
- ( prop = resolveField ( ) or prop = rejectField ( ) ) and
415
+ override predicate loadStore ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
416
+ ( prop = valueProp ( ) or prop = errorProp ( ) ) and
296
417
pred = getReceiver ( ) and
297
418
succ = this
298
419
or
299
420
// read the value of a rejected promise that is returned
300
- prop = rejectField ( ) and
421
+ prop = errorProp ( ) and
301
422
pred = getCallback ( 0 ) .getAReturn ( ) and
302
423
succ = this
303
424
}
304
425
305
- override predicate storeStep ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
306
- prop = rejectField ( ) and
426
+ override predicate store ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
427
+ prop = errorProp ( ) and
307
428
pred = getCallback ( 0 ) .getExceptionalReturn ( ) and
308
429
succ = this
309
430
}
0 commit comments