@@ -411,7 +411,7 @@ module API {
411
411
any ( Type t ) .hasUnderlyingType ( moduleName , exportName )
412
412
} or
413
413
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 ( ) )
415
415
}
416
416
417
417
class TDef = MkModuleDef or TNonModuleDef ;
@@ -528,7 +528,7 @@ module API {
528
528
*/
529
529
private predicate argumentPassing ( TApiNode base , int i , DataFlow:: Node arg ) {
530
530
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 , "" )
532
532
|
533
533
arg = pred .getAnInvocation ( ) .getArgument ( i - bound )
534
534
or
@@ -556,6 +556,32 @@ module API {
556
556
nd = MkDef ( rhs )
557
557
}
558
558
559
+ /**
560
+ * Holds if `ref` is a read of a property described by `lbl` on `pred`, and
561
+ * `propDesc` is compatible with that property, meaning it is either the
562
+ * name of the property itself or the empty string.
563
+ */
564
+ pragma [ noinline]
565
+ private predicate propertyRead (
566
+ DataFlow:: SourceNode pred , string propDesc , string lbl , DataFlow:: Node ref
567
+ ) {
568
+ ref = pred .getAPropertyRead ( ) and
569
+ lbl = Label:: memberFromRef ( ref ) and
570
+ (
571
+ lbl = Label:: member ( propDesc )
572
+ or
573
+ propDesc = ""
574
+ )
575
+ or
576
+ PromiseFlow:: loadStep ( pred .getALocalUse ( ) , ref , Promises:: valueProp ( ) ) and
577
+ lbl = Label:: promised ( ) and
578
+ ( propDesc = Promises:: valueProp ( ) or propDesc = "" )
579
+ or
580
+ PromiseFlow:: loadStep ( pred .getALocalUse ( ) , ref , Promises:: errorProp ( ) ) and
581
+ lbl = Label:: promisedError ( ) and
582
+ ( propDesc = Promises:: errorProp ( ) or propDesc = "" )
583
+ }
584
+
559
585
/**
560
586
* Holds if `ref` is a use of a node that should have an incoming edge from `base` labeled
561
587
* `lbl` in the API graph.
@@ -567,26 +593,25 @@ module API {
567
593
base = MkRoot ( ) and
568
594
ref = lbl .( EntryPoint ) .getAUse ( )
569
595
or
596
+ // property reads
597
+ exists ( DataFlow:: SourceNode src , DataFlow:: SourceNode pred , string propDesc |
598
+ use ( base , src ) and
599
+ pred = trackUseNode ( src , false , 0 , propDesc ) and
600
+ propertyRead ( pred , propDesc , lbl , ref ) and
601
+ // `module.exports` is special: it is a use of a def-node, not a use-node,
602
+ // so we want to exclude it here
603
+ ( base instanceof TNonModuleDef or base instanceof TUse )
604
+ )
605
+ or
606
+ // invocations
570
607
exists ( DataFlow:: SourceNode src , DataFlow:: SourceNode pred |
571
608
use ( base , src ) and pred = trackUseNode ( src )
572
609
|
573
- // `module.exports` is special: it is a use of a def-node, not a use-node,
574
- // so we want to exclude it here
575
- ( base instanceof TNonModuleDef or base instanceof TUse ) and
576
- lbl = Label:: memberFromRef ( ref ) and
577
- ref = pred .getAPropertyRead ( )
578
- or
579
610
lbl = Label:: instance ( ) and
580
611
ref = pred .getAnInstantiation ( )
581
612
or
582
613
lbl = Label:: return ( ) and
583
614
ref = pred .getAnInvocation ( )
584
- 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 ( ) )
590
615
)
591
616
or
592
617
exists ( DataFlow:: Node def , DataFlow:: FunctionNode fn |
@@ -680,36 +705,58 @@ module API {
680
705
)
681
706
}
682
707
708
+ private import semmle.javascript.dataflow.TypeTracking
709
+
683
710
/**
684
711
* Gets a data-flow node to which `nd`, which is a use of an API-graph node, flows.
685
712
*
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.)
713
+ * The flow from `nd` to that node may be inter-procedural, and is further described by three
714
+ * flags:
715
+ *
716
+ * - `promisified`: if true `true`, the flow goes through a promisification;
717
+ * - `boundArgs`: for function values, tracks how many arguments have been bound throughout
718
+ * the flow. To ensure termination, we somewhat arbitrarily constrain the number of bound
719
+ * arguments to be at most ten.
720
+ * - `prop`: if non-empty, the flow is only guaranteed to preserve the value of this property,
721
+ * and not necessarily the entire object.
690
722
*/
691
723
private DataFlow:: SourceNode trackUseNode (
692
- DataFlow:: SourceNode nd , boolean promisified , int boundArgs , DataFlow:: TypeTracker t
724
+ DataFlow:: SourceNode nd , boolean promisified , int boundArgs , string prop ,
725
+ DataFlow:: TypeTracker t
693
726
) {
694
727
t .start ( ) and
695
728
use ( _, nd ) and
696
729
result = nd and
697
730
promisified = false and
698
- boundArgs = 0
731
+ boundArgs = 0 and
732
+ prop = ""
699
733
or
700
734
exists ( Promisify:: PromisifyCall promisify |
701
- trackUseNode ( nd , false , boundArgs , t .continue ( ) ) .flowsTo ( promisify .getArgument ( 0 ) ) and
735
+ trackUseNode ( nd , false , boundArgs , prop , t .continue ( ) ) .flowsTo ( promisify .getArgument ( 0 ) ) and
702
736
promisified = true and
737
+ prop = "" and
703
738
result = promisify
704
739
)
705
740
or
706
741
exists ( DataFlow:: PartialInvokeNode pin , DataFlow:: Node pred , int predBoundArgs |
707
- trackUseNode ( nd , promisified , predBoundArgs , t .continue ( ) ) .flowsTo ( pred ) and
742
+ trackUseNode ( nd , promisified , predBoundArgs , prop , t .continue ( ) ) .flowsTo ( pred ) and
743
+ prop = "" and
708
744
result = pin .getBoundFunction ( pred , boundArgs - predBoundArgs ) and
709
745
boundArgs in [ 0 .. 10 ]
710
746
)
711
747
or
712
- t = useStep ( nd , promisified , boundArgs , result )
748
+ exists ( DataFlow:: Node pred , string preprop |
749
+ trackUseNode ( nd , promisified , boundArgs , preprop , t .continue ( ) ) .flowsTo ( pred ) and
750
+ promisified = false and
751
+ boundArgs = 0 and
752
+ SharedTypeTrackingStep:: loadStoreStep ( pred , result , prop )
753
+ |
754
+ prop = preprop
755
+ or
756
+ preprop = ""
757
+ )
758
+ or
759
+ t = useStep ( nd , promisified , boundArgs , prop , result )
713
760
}
714
761
715
762
private import semmle.javascript.dataflow.internal.StepSummary
@@ -723,27 +770,27 @@ module API {
723
770
*/
724
771
pragma [ noopt]
725
772
private DataFlow:: TypeTracker useStep (
726
- DataFlow:: Node nd , boolean promisified , int boundArgs , DataFlow:: Node res
773
+ DataFlow:: Node nd , boolean promisified , int boundArgs , string prop , DataFlow:: Node res
727
774
) {
728
775
exists ( DataFlow:: TypeTracker t , StepSummary summary , DataFlow:: SourceNode prev |
729
- prev = trackUseNode ( nd , promisified , boundArgs , t ) and
776
+ prev = trackUseNode ( nd , promisified , boundArgs , prop , t ) and
730
777
StepSummary:: step ( prev , res , summary ) and
731
778
result = t .append ( summary )
732
779
)
733
780
}
734
781
735
782
private DataFlow:: SourceNode trackUseNode (
736
- DataFlow:: SourceNode nd , boolean promisified , int boundArgs
783
+ DataFlow:: SourceNode nd , boolean promisified , int boundArgs , string prop
737
784
) {
738
- result = trackUseNode ( nd , promisified , boundArgs , DataFlow:: TypeTracker:: end ( ) )
785
+ result = trackUseNode ( nd , promisified , boundArgs , prop , DataFlow:: TypeTracker:: end ( ) )
739
786
}
740
787
741
788
/**
742
789
* Gets a node that is inter-procedurally reachable from `nd`, which is a use of some node.
743
790
*/
744
791
cached
745
792
DataFlow:: SourceNode trackUseNode ( DataFlow:: SourceNode nd ) {
746
- result = trackUseNode ( nd , false , 0 )
793
+ result = trackUseNode ( nd , false , 0 , "" )
747
794
}
748
795
749
796
private DataFlow:: SourceNode trackDefNode ( DataFlow:: Node nd , DataFlow:: TypeBackTracker t ) {
0 commit comments