@@ -185,18 +185,20 @@ module PromiseTypeTracking {
185185 /**
186186 * Gets the result from a single step through a promise, from `pred` to `result` summarized by `summary`.
187187 * 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.
188+ *
189+ * These type-tracking steps are already included in the default type-tracking steps (through `PreCallGraphStep`).
188190 */
189191 pragma [ inline]
190192 DataFlow:: Node promiseStep ( DataFlow:: Node pred , StepSummary summary ) {
191- exists ( PromiseFlowStep step , string field | field = Promises:: valueProp ( ) |
193+ exists ( string field | field = Promises:: valueProp ( ) |
192194 summary = LoadStep ( field ) and
193- step . load ( pred , result , field )
195+ PromiseFlow :: loadStep ( pred , result , field )
194196 or
195197 summary = StoreStep ( field ) and
196- step . store ( pred , result , field )
198+ PromiseFlow :: storeStep ( pred , result , field )
197199 or
198200 summary = CopyStep ( field ) and
199- step . loadStore ( pred , result , field )
201+ PromiseFlow :: loadStoreStep ( pred , result , field )
200202 )
201203 }
202204
@@ -221,55 +223,25 @@ module PromiseTypeTracking {
221223 }
222224}
223225
226+ private import semmle.javascript.dataflow.internal.PreCallGraphStep
227+
224228/**
225- * An `AdditionalFlowStep` used to model a data-flow step related to promises.
229+ * A step related to promises.
226230 *
227- * The `loadStep`/`storeStep`/`loadStoreStep` methods are overloaded such that the new predicates
228- * `load`/`store`/`loadStore` can be used in the `PromiseTypeTracking` module.
229- * (Thereby avoiding conflicts with a "cousin" `AdditionalFlowStep` implementation.)
230- *
231- * The class is private and is only intended to be used inside the `PromiseTypeTracking` and `PromiseFlow` modules.
231+ * These steps are for `await p`, `new Promise()`, `Promise.resolve()`,
232+ * `Promise.then()`, `Promise.catch()`, and `Promise.finally()`.
232233 */
233- abstract private class PromiseFlowStep extends DataFlow:: AdditionalFlowStep {
234- final override predicate step ( DataFlow:: Node pred , DataFlow:: Node succ ) { none ( ) }
235-
236- final override predicate step (
237- DataFlow:: Node p , DataFlow:: Node s , DataFlow:: FlowLabel pl , DataFlow:: FlowLabel sl
238- ) {
239- none ( )
234+ private class PromiseStep extends PreCallGraphStep {
235+ override predicate loadStep ( DataFlow:: Node obj , DataFlow:: Node element , string prop ) {
236+ PromiseFlow:: loadStep ( obj , element , prop )
240237 }
241238
242- /**
243- * Holds if the property `prop` of the object `pred` should be loaded into `succ`.
244- */
245- predicate load ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) { none ( ) }
246-
247- final override predicate loadStep ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
248- this .load ( pred , succ , prop )
239+ override predicate storeStep ( DataFlow:: Node element , DataFlow:: SourceNode obj , string prop ) {
240+ PromiseFlow:: storeStep ( element , obj , prop )
249241 }
250242
251- /**
252- * Holds if `pred` should be stored in the object `succ` under the property `prop`.
253- */
254- predicate store ( DataFlow:: Node pred , DataFlow:: SourceNode succ , string prop ) { none ( ) }
255-
256- final override predicate storeStep ( DataFlow:: Node pred , DataFlow:: SourceNode succ , string prop ) {
257- this .store ( pred , succ , prop )
258- }
259-
260- /**
261- * Holds if the property `prop` should be copied from the object `pred` to the object `succ`.
262- */
263- predicate loadStore ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) { none ( ) }
264-
265- final override predicate loadStoreStep ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
266- this .loadStore ( pred , succ , prop )
267- }
268-
269- final override predicate loadStoreStep (
270- DataFlow:: Node pred , DataFlow:: Node succ , string loadProp , string storeProp
271- ) {
272- none ( )
243+ override predicate loadStoreStep ( DataFlow:: Node pred , DataFlow:: SourceNode succ , string prop ) {
244+ PromiseFlow:: loadStoreStep ( pred , succ , prop )
273245 }
274246}
275247
@@ -283,188 +255,143 @@ private module PromiseFlow {
283255 private predicate errorProp = Promises:: errorProp / 0 ;
284256
285257 /**
286- * A flow step describing a promise definition.
287- *
288- * The resolved/rejected value is written to a pseudo-field on the promise.
258+ * Holds if there is a step for loading a `value` from a `promise`.
259+ * `prop` is either `valueProp()` if the value is a resolved value, or `errorProp()` if the promise has been rejected.
289260 */
290- class PromiseDefitionStep extends PromiseFlowStep {
291- PromiseDefinition promise ;
292-
293- PromiseDefitionStep ( ) { this = promise }
294-
295- override predicate store ( DataFlow:: Node pred , DataFlow:: SourceNode succ , string prop ) {
261+ predicate loadStep ( DataFlow:: Node promise , DataFlow:: Node value , string prop ) {
262+ // await promise.
263+ exists ( AwaitExpr await | await .getOperand ( ) = promise .asExpr ( ) |
296264 prop = valueProp ( ) and
297- pred = promise .getResolveParameter ( ) .getACall ( ) .getArgument ( 0 ) and
298- succ = this
265+ value .asExpr ( ) = await
299266 or
300267 prop = errorProp ( ) and
301- (
302- pred = promise .getRejectParameter ( ) .getACall ( ) .getArgument ( 0 ) or
303- pred = promise .getExecutor ( ) .getExceptionalReturn ( )
304- ) and
305- succ = this
306- }
307-
308- override predicate loadStore ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
309- // Copy the value of a resolved promise to the value of this promise.
268+ value = await .getExceptionTarget ( )
269+ )
270+ or
271+ // promise.then()
272+ exists ( DataFlow:: MethodCallNode call |
273+ call .getMethodName ( ) = "then" and promise = call .getReceiver ( )
274+ |
310275 prop = valueProp ( ) and
311- pred = promise .getResolveParameter ( ) .getACall ( ) .getArgument ( 0 ) and
312- succ = this
313- }
276+ value = call .getCallback ( 0 ) .getParameter ( 0 )
277+ or
278+ prop = errorProp ( ) and
279+ value = call .getCallback ( 1 ) .getParameter ( 0 )
280+ )
281+ or
282+ // promise.catch()
283+ exists ( DataFlow:: MethodCallNode call | call .getMethodName ( ) = "catch" |
284+ prop = errorProp ( ) and
285+ promise = call .getReceiver ( ) and
286+ value = call .getCallback ( 0 ) .getParameter ( 0 )
287+ )
314288 }
315289
316290 /**
317- * A flow step describing the a Promise.resolve (and similar) call.
291+ * Holds if there is a step for storing a `value` into a promise `obj`.
292+ * `prop` is either `valueProp()` if the value is a resolved value, or `errorProp()` if the promise has been rejected.
318293 */
319- class CreationStep extends PromiseFlowStep {
320- PromiseCreationCall promise ;
321-
322- CreationStep ( ) { this = promise }
323-
324- override predicate store ( DataFlow:: Node pred , DataFlow:: SourceNode succ , string prop ) {
325- not promise instanceof PromiseAllCreation and
294+ predicate storeStep ( DataFlow:: Node value , DataFlow:: SourceNode obj , string prop ) {
295+ // promise definition, e.g. `new Promise()`
296+ exists ( PromiseDefinition promise | obj = promise |
326297 prop = valueProp ( ) and
327- pred = promise .getValue ( ) and
328- succ = this
298+ value = promise .getResolveParameter ( ) .getACall ( ) .getArgument ( 0 )
329299 or
330- prop = valueProp ( ) and
331- pred = promise .( PromiseAllCreation ) .getArrayNode ( ) and
332- succ = this
333- }
334-
335- override predicate loadStore ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
336- // Copy the value of a resolved promise to the value of this promise.
300+ prop = errorProp ( ) and
301+ value =
302+ [ promise .getRejectParameter ( ) .getACall ( ) .getArgument ( 0 ) ,
303+ promise .getExecutor ( ) .getExceptionalReturn ( ) ]
304+ )
305+ or
306+ // promise creation call, e.g. `Promise.resolve`.
307+ exists ( PromiseCreationCall promise | obj = promise |
337308 not promise instanceof PromiseAllCreation and
338309 prop = valueProp ( ) and
339- pred = promise .getValue ( ) and
340- succ = this
310+ value = promise .getValue ( )
341311 or
342- promise instanceof PromiseAllCreation and
343312 prop = valueProp ( ) and
344- pred = promise .( PromiseAllCreation ) .getArrayNode ( ) and
345- succ = this
346- }
347- }
348-
349- /**
350- * A load step loading the pseudo-field describing that the promise is rejected.
351- * The rejected value is thrown as a exception.
352- */
353- class AwaitStep extends PromiseFlowStep {
354- DataFlow:: Node operand ;
355- AwaitExpr await ;
356-
357- AwaitStep ( ) {
358- this .getEnclosingExpr ( ) = await and
359- operand .getEnclosingExpr ( ) = await .getOperand ( )
360- }
361-
362- override predicate load ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
313+ value = promise .( PromiseAllCreation ) .getArrayNode ( )
314+ )
315+ or
316+ // promise.then()
317+ exists ( DataFlow:: MethodCallNode call | call .getMethodName ( ) = "then" and obj = call |
363318 prop = valueProp ( ) and
364- succ = this and
365- pred = operand
319+ value = call .getCallback ( [ 0 .. 1 ] ) .getAReturn ( )
366320 or
367321 prop = errorProp ( ) and
368- succ = await .getExceptionTarget ( ) and
369- pred = operand
370- }
322+ value = call .getCallback ( [ 0 .. 1 ] ) .getExceptionalReturn ( )
323+ )
324+ or
325+ // promise.catch()
326+ exists ( DataFlow:: MethodCallNode call | call .getMethodName ( ) = "catch" and obj = call |
327+ prop = errorProp ( ) and
328+ value = call .getCallback ( 0 ) .getExceptionalReturn ( )
329+ or
330+ prop = valueProp ( ) and
331+ value = call .getCallback ( 0 ) .getAReturn ( )
332+ )
333+ or
334+ // promise.finally()
335+ exists ( DataFlow:: MethodCallNode call | call .getMethodName ( ) = "finally" |
336+ prop = errorProp ( ) and
337+ value = call .getCallback ( 0 ) .getExceptionalReturn ( ) and
338+ obj = call
339+ )
371340 }
372341
373342 /**
374- * A flow step describing the data-flow related to the `.then` method of a promise.
343+ * Holds if there is a step copying a resolved/rejected promise value from promise `pred` to promise `succ`.
344+ * `prop` is either `valueProp()` if the value is a resolved value, or `errorProp()` if the promise has been rejected.
375345 */
376- class ThenStep extends PromiseFlowStep , DataFlow:: MethodCallNode {
377- ThenStep ( ) { this . getMethodName ( ) = "then" }
378-
379- override predicate load ( DataFlow :: Node pred , DataFlow :: Node succ , string prop ) {
346+ predicate loadStoreStep ( DataFlow :: Node pred , DataFlow:: Node succ , string prop ) {
347+ // promise definition, e.g. `new Promise()`
348+ exists ( PromiseDefinition promise | succ = promise |
349+ // Copy the value of a resolved promise to the value of this promise.
380350 prop = valueProp ( ) and
381- pred = getReceiver ( ) and
382- succ = getCallback ( 0 ) .getParameter ( 0 )
351+ pred = promise .getResolveParameter ( ) .getACall ( ) .getArgument ( 0 )
352+ )
353+ or
354+ // promise creation call, e.g. `Promise.resolve`.
355+ exists ( PromiseCreationCall promise | succ = promise |
356+ // Copy the value of a resolved promise to the value of this promise.
357+ not promise instanceof PromiseAllCreation and
358+ pred = promise .getValue ( ) and
359+ prop = valueProp ( )
383360 or
361+ pred = promise .( PromiseAllCreation ) .getArrayNode ( ) and
362+ prop = valueProp ( )
363+ )
364+ or
365+ // promise.then()
366+ exists ( DataFlow:: MethodCallNode call | call .getMethodName ( ) = "then" and succ = call |
367+ not exists ( call .getArgument ( 1 ) ) and
384368 prop = errorProp ( ) and
385- pred = getReceiver ( ) and
386- succ = getCallback ( 1 ) .getParameter ( 0 )
387- }
388-
389- override predicate loadStore ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
390- not exists ( this .getArgument ( 1 ) ) and
391- prop = errorProp ( ) and
392- pred = getReceiver ( ) and
393- succ = this
369+ pred = call .getReceiver ( )
394370 or
395371 // read the value of a resolved/rejected promise that is returned
396372 ( prop = errorProp ( ) or prop = valueProp ( ) ) and
397- pred = getCallback ( [ 0 .. 1 ] ) .getAReturn ( ) and
398- succ = this
399- }
400-
401- override predicate store ( DataFlow:: Node pred , DataFlow:: SourceNode succ , string prop ) {
402- prop = valueProp ( ) and
403- pred = getCallback ( [ 0 .. 1 ] ) .getAReturn ( ) and
404- succ = this
405- or
406- prop = errorProp ( ) and
407- pred = getCallback ( [ 0 .. 1 ] ) .getExceptionalReturn ( ) and
408- succ = this
409- }
410- }
411-
412- /**
413- * A flow step describing the data-flow related to the `.catch` method of a promise.
414- */
415- class CatchStep extends PromiseFlowStep , DataFlow:: MethodCallNode {
416- CatchStep ( ) { this .getMethodName ( ) = "catch" }
417-
418- override predicate load ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
419- prop = errorProp ( ) and
420- pred = getReceiver ( ) and
421- succ = getCallback ( 0 ) .getParameter ( 0 )
422- }
423-
424- override predicate loadStore ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
373+ pred = call .getCallback ( [ 0 .. 1 ] ) .getAReturn ( )
374+ )
375+ or
376+ // promise.catch()
377+ exists ( DataFlow:: MethodCallNode call | call .getMethodName ( ) = "catch" and succ = call |
425378 prop = valueProp ( ) and
426- pred = getReceiver ( ) .getALocalSource ( ) and
427- succ = this
379+ pred = call .getReceiver ( )
428380 or
429381 // read the value of a resolved/rejected promise that is returned
430382 ( prop = errorProp ( ) or prop = valueProp ( ) ) and
431- pred = getCallback ( 0 ) .getAReturn ( ) and
432- succ = this
433- }
434-
435- override predicate store ( DataFlow:: Node pred , DataFlow:: SourceNode succ , string prop ) {
436- prop = errorProp ( ) and
437- pred = getCallback ( 0 ) .getExceptionalReturn ( ) and
438- succ = this
439- or
440- prop = valueProp ( ) and
441- pred = getCallback ( 0 ) .getAReturn ( ) and
442- succ = this
443- }
444- }
445-
446- /**
447- * A flow step describing the data-flow related to the `.finally` method of a promise.
448- */
449- class FinallyStep extends PromiseFlowStep , DataFlow:: MethodCallNode {
450- FinallyStep ( ) { this .getMethodName ( ) = "finally" }
451-
452- override predicate loadStore ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
383+ pred = call .getCallback ( 0 ) .getAReturn ( )
384+ )
385+ or
386+ // promise.finally()
387+ exists ( DataFlow:: MethodCallNode call | call .getMethodName ( ) = "finally" and succ = call |
453388 ( prop = valueProp ( ) or prop = errorProp ( ) ) and
454- pred = getReceiver ( ) and
455- succ = this
389+ pred = call .getReceiver ( )
456390 or
457391 // read the value of a rejected promise that is returned
458392 prop = errorProp ( ) and
459- pred = getCallback ( 0 ) .getAReturn ( ) and
460- succ = this
461- }
462-
463- override predicate store ( DataFlow:: Node pred , DataFlow:: SourceNode succ , string prop ) {
464- prop = errorProp ( ) and
465- pred = getCallback ( 0 ) .getExceptionalReturn ( ) and
466- succ = this
467- }
393+ pred = call .getCallback ( 0 ) .getAReturn ( )
394+ )
468395 }
469396}
470397
0 commit comments