@@ -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
@@ -556,6 +556,32 @@ module API {
556556 nd = MkDef ( rhs )
557557 }
558558
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+
559585 /**
560586 * Holds if `ref` is a use of a node that should have an incoming edge from `base` labeled
561587 * `lbl` in the API graph.
@@ -567,26 +593,25 @@ module API {
567593 base = MkRoot ( ) and
568594 ref = lbl .( EntryPoint ) .getAUse ( )
569595 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
570607 exists ( DataFlow:: SourceNode src , DataFlow:: SourceNode pred |
571608 use ( base , src ) and pred = trackUseNode ( src )
572609 |
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
579610 lbl = Label:: instance ( ) and
580611 ref = pred .getAnInstantiation ( )
581612 or
582613 lbl = Label:: return ( ) and
583614 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 ( ) )
590615 )
591616 or
592617 exists ( DataFlow:: Node def , DataFlow:: FunctionNode fn |
@@ -680,36 +705,58 @@ module API {
680705 )
681706 }
682707
708+ private import semmle.javascript.dataflow.TypeTracking
709+
683710 /**
684711 * Gets a data-flow node to which `nd`, which is a use of an API-graph node, flows.
685712 *
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.
690722 */
691723 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
693726 ) {
694727 t .start ( ) and
695728 use ( _, nd ) and
696729 result = nd and
697730 promisified = false and
698- boundArgs = 0
731+ boundArgs = 0 and
732+ prop = ""
699733 or
700734 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
702736 promisified = true and
737+ prop = "" and
703738 result = promisify
704739 )
705740 or
706741 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
708744 result = pin .getBoundFunction ( pred , boundArgs - predBoundArgs ) and
709745 boundArgs in [ 0 .. 10 ]
710746 )
711747 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 )
713760 }
714761
715762 private import semmle.javascript.dataflow.internal.StepSummary
@@ -723,27 +770,27 @@ module API {
723770 */
724771 pragma [ noopt]
725772 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
727774 ) {
728775 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
730777 StepSummary:: step ( prev , res , summary ) and
731778 result = t .append ( summary )
732779 )
733780 }
734781
735782 private DataFlow:: SourceNode trackUseNode (
736- DataFlow:: SourceNode nd , boolean promisified , int boundArgs
783+ DataFlow:: SourceNode nd , boolean promisified , int boundArgs , string prop
737784 ) {
738- result = trackUseNode ( nd , promisified , boundArgs , DataFlow:: TypeTracker:: end ( ) )
785+ result = trackUseNode ( nd , promisified , boundArgs , prop , DataFlow:: TypeTracker:: end ( ) )
739786 }
740787
741788 /**
742789 * Gets a node that is inter-procedurally reachable from `nd`, which is a use of some node.
743790 */
744791 cached
745792 DataFlow:: SourceNode trackUseNode ( DataFlow:: SourceNode nd ) {
746- result = trackUseNode ( nd , false , 0 )
793+ result = trackUseNode ( nd , false , 0 , "" )
747794 }
748795
749796 private DataFlow:: SourceNode trackDefNode ( DataFlow:: Node nd , DataFlow:: TypeBackTracker t ) {
0 commit comments