@@ -206,7 +206,8 @@ module API {
206206 this = Impl:: MkClassInstance ( result ) or
207207 this = Impl:: MkUse ( result ) or
208208 this = Impl:: MkDef ( result ) or
209- this = Impl:: MkAsyncFuncResult ( result )
209+ this = Impl:: MkAsyncFuncResult ( result ) or
210+ this = Impl:: MkSyntheticCallbackArg ( _, _, result )
210211 }
211212
212213 /**
@@ -389,12 +390,16 @@ module API {
389390 MkCanonicalNameUse ( CanonicalName n ) {
390391 not n .isRoot ( ) and
391392 isUsed ( n )
393+ } or
394+ MkSyntheticCallbackArg ( DataFlow:: Node src , int bound , DataFlow:: InvokeNode nd ) {
395+ trackUseNode ( src , true , bound ) .flowsTo ( nd .getCalleeNode ( ) )
392396 }
393397
394398 class TDef = MkModuleDef or TNonModuleDef ;
395399
396400 class TNonModuleDef =
397- MkModuleExport or MkClassInstance or MkAsyncFuncResult or MkDef or MkCanonicalNameDef ;
401+ MkModuleExport or MkClassInstance or MkAsyncFuncResult or MkDef or MkCanonicalNameDef or
402+ MkSyntheticCallbackArg ;
398403
399404 class TUse = MkModuleUse or MkModuleImport or MkUse or MkCanonicalNameUse ;
400405
@@ -523,18 +528,20 @@ module API {
523528 * The receiver is considered to be argument -1.
524529 */
525530 private predicate argumentPassing ( TApiNode base , int i , DataFlow:: Node arg ) {
526- exists ( DataFlow:: SourceNode use , DataFlow:: SourceNode pred |
527- use ( base , use ) and pred = trackUseNode ( use )
531+ exists ( DataFlow:: Node use , DataFlow:: SourceNode pred , int bound |
532+ use ( base , use ) and pred = trackUseNode ( use , _ , bound )
528533 |
529- arg = pred .getAnInvocation ( ) .getArgument ( i )
534+ arg = pred .getAnInvocation ( ) .getArgument ( i - bound )
530535 or
531536 arg = pred .getACall ( ) .getReceiver ( ) and
537+ bound = 0 and
532538 i = - 1
533539 or
534540 exists ( DataFlow:: PartialInvokeNode pin , DataFlow:: Node callback | pred .flowsTo ( callback ) |
535- pin .isPartialArgument ( callback , arg , i )
541+ pin .isPartialArgument ( callback , arg , i - bound )
536542 or
537543 arg = pin .getBoundReceiver ( callback ) and
544+ bound = 0 and
538545 i = - 1
539546 )
540547 )
@@ -609,6 +616,12 @@ module API {
609616 lbl = Label:: instance ( ) and
610617 ref = getANodeWithType ( tn )
611618 )
619+ or
620+ exists ( DataFlow:: InvokeNode call |
621+ base = MkSyntheticCallbackArg ( _, _, call ) and
622+ lbl = Label:: parameter ( 1 ) and
623+ ref = awaited ( call )
624+ )
612625 )
613626 }
614627
@@ -672,20 +685,70 @@ module API {
672685 )
673686 }
674687
675- private DataFlow:: SourceNode trackUseNode ( DataFlow:: SourceNode nd , DataFlow:: TypeTracker t ) {
688+ /**
689+ * Gets a data-flow node to which `nd`, which is a use of an API-graph node, flows.
690+ *
691+ * The flow from `nd` to that node may be inter-procedural. If `promisified` is `true`, the
692+ * flow goes through a promisification, and `boundArgs` indicates how many arguments have been
693+ * bound throughout the flow. (To ensure termination, we somewhat arbitrarily constrain the
694+ * number of bound arguments to be at most ten.)
695+ */
696+ private DataFlow:: SourceNode trackUseNode (
697+ DataFlow:: SourceNode nd , boolean promisified , int boundArgs , DataFlow:: TypeTracker t
698+ ) {
676699 t .start ( ) and
677700 use ( _, nd ) and
678- result = nd
701+ result = nd and
702+ promisified = false and
703+ boundArgs = 0
704+ or
705+ exists ( DataFlow:: CallNode promisify |
706+ promisify = DataFlow:: moduleImport ( [ "util" , "bluebird" ] ) .getAMemberCall ( "promisify" )
707+ |
708+ trackUseNode ( nd , false , boundArgs , t .continue ( ) ) .flowsTo ( promisify .getArgument ( 0 ) ) and
709+ promisified = true and
710+ result = promisify
711+ )
712+ or
713+ exists ( DataFlow:: PartialInvokeNode pin , DataFlow:: Node pred , int predBoundArgs |
714+ trackUseNode ( nd , promisified , predBoundArgs , t .continue ( ) ) .flowsTo ( pred ) and
715+ result = pin .getBoundFunction ( pred , boundArgs - predBoundArgs ) and
716+ boundArgs in [ 0 .. 10 ]
717+ )
679718 or
680- exists ( DataFlow:: TypeTracker t2 | result = trackUseNode ( nd , t2 ) .track ( t2 , t ) )
719+ exists ( StepSummary summary |
720+ t = useStep ( nd , promisified , boundArgs , result , summary ) .append ( summary )
721+ )
722+ }
723+
724+ private import semmle.javascript.dataflow.internal.StepSummary
725+
726+ /**
727+ * Holds if `nd`, which is a use of an API-graph node, flows in zero or more potentially
728+ * inter-procedural steps to some intermediate node, and then from that intermediate node to
729+ * `res` in one step described by `summary`.
730+ *
731+ * This predicate exists solely to enforce a better join order in `trackUseNode` above.
732+ */
733+ pragma [ noinline]
734+ private DataFlow:: TypeTracker useStep (
735+ DataFlow:: Node nd , boolean promisified , int boundArgs , DataFlow:: Node res , StepSummary summary
736+ ) {
737+ StepSummary:: step ( trackUseNode ( nd , promisified , boundArgs , result ) , res , summary )
738+ }
739+
740+ private DataFlow:: SourceNode trackUseNode (
741+ DataFlow:: SourceNode nd , boolean promisified , int boundArgs
742+ ) {
743+ result = trackUseNode ( nd , promisified , boundArgs , DataFlow:: TypeTracker:: end ( ) )
681744 }
682745
683746 /**
684747 * Gets a node that is inter-procedurally reachable from `nd`, which is a use of some node.
685748 */
686749 cached
687750 DataFlow:: SourceNode trackUseNode ( DataFlow:: SourceNode nd ) {
688- result = trackUseNode ( nd , DataFlow :: TypeTracker :: end ( ) )
751+ result = trackUseNode ( nd , false , 0 )
689752 }
690753
691754 private DataFlow:: SourceNode trackDefNode ( DataFlow:: Node nd , DataFlow:: TypeBackTracker t ) {
@@ -714,6 +777,21 @@ module API {
714777 result = trackDefNode ( nd , DataFlow:: TypeBackTracker:: end ( ) )
715778 }
716779
780+ private DataFlow:: SourceNode awaited ( DataFlow:: InvokeNode call , DataFlow:: TypeTracker t ) {
781+ t .startInPromise ( ) and
782+ exists ( MkSyntheticCallbackArg ( _, _, call ) ) and
783+ result = call
784+ or
785+ exists ( DataFlow:: TypeTracker t2 | result = awaited ( call , t2 ) .track ( t2 , t ) )
786+ }
787+
788+ /**
789+ * Gets a node holding the resolved value of promise `call`.
790+ */
791+ private DataFlow:: Node awaited ( DataFlow:: InvokeNode call ) {
792+ result = awaited ( call , DataFlow:: TypeTracker:: end ( ) )
793+ }
794+
717795 private DataFlow:: SourceNode getANodeWithType ( TypeName tn ) {
718796 exists ( string moduleName , string typeName |
719797 tn .hasQualifiedName ( moduleName , typeName ) and
@@ -774,6 +852,12 @@ module API {
774852 lbl = Label:: return ( ) and
775853 succ = MkAsyncFuncResult ( f )
776854 )
855+ or
856+ exists ( DataFlow:: SourceNode src , int bound , DataFlow:: InvokeNode call |
857+ use ( pred , src ) and
858+ lbl = Label:: parameter ( bound + call .getNumArgument ( ) ) and
859+ succ = MkSyntheticCallbackArg ( src , bound , call )
860+ )
777861 }
778862
779863 /**
0 commit comments