@@ -411,7 +411,7 @@ module API {
411411 any ( Type t ) .hasUnderlyingType ( moduleName , exportName )
412412 } or
413413 MkSyntheticCallbackArg ( DataFlow:: Node src , int bound , DataFlow:: InvokeNode nd ) {
414- trackUseNode ( src , true , bound ) .flowsTo ( nd .getCalleeNode ( ) )
414+ trackUseNode ( src , true , bound , "" ) .flowsTo ( nd .getCalleeNode ( ) )
415415 }
416416
417417 class TDef = MkModuleDef or TNonModuleDef ;
@@ -528,7 +528,7 @@ module API {
528528 */
529529 private predicate argumentPassing ( TApiNode base , int i , DataFlow:: Node arg ) {
530530 exists ( DataFlow:: Node use , DataFlow:: SourceNode pred , int bound |
531- use ( base , use ) and pred = trackUseNode ( use , _, bound )
531+ use ( base , use ) and pred = trackUseNode ( use , _, bound , "" )
532532 |
533533 arg = pred .getAnInvocation ( ) .getArgument ( i - bound )
534534 or
@@ -567,26 +567,37 @@ module API {
567567 base = MkRoot ( ) and
568568 ref = lbl .( EntryPoint ) .getAUse ( )
569569 or
570- exists ( DataFlow:: SourceNode src , DataFlow:: SourceNode pred |
571- use ( base , src ) and pred = trackUseNode ( src )
570+ exists ( DataFlow:: SourceNode src , DataFlow:: SourceNode pred , string prop |
571+ use ( base , src ) and pred = trackUseNode ( src , false , 0 , prop )
572572 |
573573 // `module.exports` is special: it is a use of a def-node, not a use-node,
574574 // so we want to exclude it here
575575 ( base instanceof TNonModuleDef or base instanceof TUse ) and
576576 lbl = Label:: memberFromRef ( ref ) and
577+ (
578+ lbl = Label:: member ( prop )
579+ or
580+ prop = ""
581+ ) and
577582 ref = pred .getAPropertyRead ( )
578583 or
579584 lbl = Label:: instance ( ) and
585+ prop = "" and
580586 ref = pred .getAnInstantiation ( )
581587 or
582588 lbl = Label:: return ( ) and
589+ prop = "" and
583590 ref = pred .getAnInvocation ( )
584591 or
585- lbl = Label:: promised ( ) and
586- PromiseFlow:: loadStep ( pred .getALocalUse ( ) , ref , Promises:: valueProp ( ) )
587- or
588- lbl = Label:: promisedError ( ) and
589- PromiseFlow:: loadStep ( pred .getALocalUse ( ) , ref , Promises:: errorProp ( ) )
592+ (
593+ lbl = Label:: promised ( ) and
594+ ( prop = Promises:: valueProp ( ) or prop = "" ) and
595+ PromiseFlow:: loadStep ( pred .getALocalUse ( ) , ref , Promises:: valueProp ( ) )
596+ or
597+ lbl = Label:: promisedError ( ) and
598+ ( prop = Promises:: errorProp ( ) or prop = "" ) and
599+ PromiseFlow:: loadStep ( pred .getALocalUse ( ) , ref , Promises:: errorProp ( ) )
600+ )
590601 )
591602 or
592603 exists ( DataFlow:: Node def , DataFlow:: FunctionNode fn |
@@ -680,36 +691,58 @@ module API {
680691 )
681692 }
682693
694+ private import semmle.javascript.dataflow.TypeTracking
695+
683696 /**
684697 * Gets a data-flow node to which `nd`, which is a use of an API-graph node, flows.
685698 *
686- * The flow from `nd` to that node may be inter-procedural. If `promisified` is `true`, the
687- * flow goes through a promisification, and `boundArgs` indicates how many arguments have been
688- * bound throughout the flow. (To ensure termination, we somewhat arbitrarily constrain the
689- * number of bound arguments to be at most ten.)
699+ * The flow from `nd` to that node may be inter-procedural, and is further described by three
700+ * flags:
701+ *
702+ * - `promisified`: if true `true`, the flow goes through a promisification;
703+ * - `boundArgs`: for function values, tracks how many arguments have been bound throughout
704+ * the flow. To ensure termination, we somewhat arbitrarily constrain the number of bound
705+ * arguments to be at most ten.
706+ * - `prop`: if non-empty, the flow is only guaranteed to preserve the value of this property,
707+ * and not necessarily the entire object.
690708 */
691709 private DataFlow:: SourceNode trackUseNode (
692- DataFlow:: SourceNode nd , boolean promisified , int boundArgs , DataFlow:: TypeTracker t
710+ DataFlow:: SourceNode nd , boolean promisified , int boundArgs , string prop ,
711+ DataFlow:: TypeTracker t
693712 ) {
694713 t .start ( ) and
695714 use ( _, nd ) and
696715 result = nd and
697716 promisified = false and
698- boundArgs = 0
717+ boundArgs = 0 and
718+ prop = ""
699719 or
700720 exists ( Promisify:: PromisifyCall promisify |
701- trackUseNode ( nd , false , boundArgs , t .continue ( ) ) .flowsTo ( promisify .getArgument ( 0 ) ) and
721+ trackUseNode ( nd , false , boundArgs , prop , t .continue ( ) ) .flowsTo ( promisify .getArgument ( 0 ) ) and
702722 promisified = true and
723+ prop = "" and
703724 result = promisify
704725 )
705726 or
706727 exists ( DataFlow:: PartialInvokeNode pin , DataFlow:: Node pred , int predBoundArgs |
707- trackUseNode ( nd , promisified , predBoundArgs , t .continue ( ) ) .flowsTo ( pred ) and
728+ trackUseNode ( nd , promisified , predBoundArgs , prop , t .continue ( ) ) .flowsTo ( pred ) and
729+ prop = "" and
708730 result = pin .getBoundFunction ( pred , boundArgs - predBoundArgs ) and
709731 boundArgs in [ 0 .. 10 ]
710732 )
711733 or
712- t = useStep ( nd , promisified , boundArgs , result )
734+ exists ( DataFlow:: Node pred , string preprop |
735+ trackUseNode ( nd , promisified , boundArgs , preprop , t .continue ( ) ) .flowsTo ( pred ) and
736+ promisified = false and
737+ boundArgs = 0 and
738+ SharedTypeTrackingStep:: loadStoreStep ( pred , result , prop )
739+ |
740+ prop = preprop
741+ or
742+ preprop = ""
743+ )
744+ or
745+ t = useStep ( nd , promisified , boundArgs , prop , result )
713746 }
714747
715748 private import semmle.javascript.dataflow.internal.StepSummary
@@ -723,27 +756,27 @@ module API {
723756 */
724757 pragma [ noopt]
725758 private DataFlow:: TypeTracker useStep (
726- DataFlow:: Node nd , boolean promisified , int boundArgs , DataFlow:: Node res
759+ DataFlow:: Node nd , boolean promisified , int boundArgs , string prop , DataFlow:: Node res
727760 ) {
728761 exists ( DataFlow:: TypeTracker t , StepSummary summary , DataFlow:: SourceNode prev |
729- prev = trackUseNode ( nd , promisified , boundArgs , t ) and
762+ prev = trackUseNode ( nd , promisified , boundArgs , prop , t ) and
730763 StepSummary:: step ( prev , res , summary ) and
731764 result = t .append ( summary )
732765 )
733766 }
734767
735768 private DataFlow:: SourceNode trackUseNode (
736- DataFlow:: SourceNode nd , boolean promisified , int boundArgs
769+ DataFlow:: SourceNode nd , boolean promisified , int boundArgs , string prop
737770 ) {
738- result = trackUseNode ( nd , promisified , boundArgs , DataFlow:: TypeTracker:: end ( ) )
771+ result = trackUseNode ( nd , promisified , boundArgs , prop , DataFlow:: TypeTracker:: end ( ) )
739772 }
740773
741774 /**
742775 * Gets a node that is inter-procedurally reachable from `nd`, which is a use of some node.
743776 */
744777 cached
745778 DataFlow:: SourceNode trackUseNode ( DataFlow:: SourceNode nd ) {
746- result = trackUseNode ( nd , false , 0 )
779+ result = trackUseNode ( nd , false , 0 , "" )
747780 }
748781
749782 private DataFlow:: SourceNode trackDefNode ( DataFlow:: Node nd , DataFlow:: TypeBackTracker t ) {
0 commit comments