@@ -602,45 +602,89 @@ module TaintTracking {
602602 }
603603
604604 /**
605- * Holds if `params` is a `URLSearchParams` object providing access to
606- * the parameters encoded in `input`.
605+ * Holds if `params` is a construction of a `URLSearchParams` that parses
606+ * the parameters in `input`.
607607 */
608- predicate isUrlSearchParams ( DataFlow:: SourceNode params , DataFlow:: Node input ) {
608+ private predicate isUrlSearchParams ( DataFlow:: SourceNode params , DataFlow:: Node input ) {
609609 exists ( DataFlow:: GlobalVarRefNode urlSearchParams , NewExpr newUrlSearchParams |
610610 urlSearchParams .getName ( ) = "URLSearchParams" and
611611 newUrlSearchParams = urlSearchParams .getAnInstantiation ( ) .asExpr ( ) and
612612 params .asExpr ( ) = newUrlSearchParams and
613613 input .asExpr ( ) = newUrlSearchParams .getArgument ( 0 )
614614 )
615- or
616- exists ( DataFlow:: NewNode newUrl |
617- newUrl = DataFlow:: globalVarRef ( "URL" ) .getAnInstantiation ( ) and
618- params = newUrl .getAPropertyRead ( "searchParams" ) and
619- input = newUrl .getArgument ( 0 )
620- )
621615 }
622616
623617 /**
624- * A taint propagating data flow edge arising from URL parameter parsing.
618+ * A pseudo-property used to store a value on a `URLSearchParams` that
619+ * can be obtained with a `get` or `getAll` call.
625620 */
626- private class UrlSearchParamsTaintStep extends AdditionalTaintStep , DataFlow:: ValueNode {
627- DataFlow:: Node source ;
621+ private string hiddenUrlPseudoProperty ( ) {
622+ result = "$hiddenSearchPararms"
623+ }
628624
625+ /**
626+ * A taint propagating data flow edge arising from URL parameter parsing.
627+ */
628+ private class UrlSearchParamsTaintStep extends DataFlow:: AdditionalFlowStep {
629629 UrlSearchParamsTaintStep ( ) {
630- // either this is itself an `URLSearchParams` object
631- isUrlSearchParams ( this , source )
632- or
633- // or this is a call to `get` or `getAll` on a `URLSearchParams` object
634- exists ( DataFlow:: SourceNode searchParams , string m |
635- isUrlSearchParams ( searchParams , source ) and
636- this = searchParams .getAMethodCall ( m ) and
637- m .matches ( "get%" )
638- )
630+ this = DataFlow:: globalVarRef ( "URL" ) or
631+ this = DataFlow:: globalVarRef ( "URLSearchParams" )
639632 }
640633
634+ /**
635+ * Holds if `succ` is a `URLSearchParams` providing access to the
636+ * parameters encoded in `pred`.
637+ */
641638 override predicate step ( DataFlow:: Node pred , DataFlow:: Node succ ) {
642- pred = source and succ = this
639+ isUrlSearchParams ( succ , pred )
640+ }
641+
642+ /**
643+ * Holds if `pred` should be stored in the object `succ` under the property `prop`.
644+ *
645+ * This step is used to model 2 facts:
646+ * 1) A `URL` constructed using `url = new URL(input)` transfers taint from `input` to `url.searchParams`.
647+ * 2) A `URLSearchParams` (either `url.searchParams` or `new URLSearchParams(input)`) has a tainted value,
648+ * which is stored in a pseudo-property, that can later be access using a `get` or `getAll` call.
649+ */
650+ override predicate storeStep ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
651+ ( prop = "searchParams" or prop = hiddenUrlPseudoProperty ( ) ) and
652+ exists ( DataFlow:: NewNode newUrl | succ = newUrl |
653+ newUrl = DataFlow:: globalVarRef ( "URL" ) .getAnInstantiation ( ) and
654+ pred = newUrl .getArgument ( 0 )
655+ )
656+ or
657+ prop = hiddenUrlPseudoProperty ( ) and
658+ isUrlSearchParams ( succ , pred )
643659 }
660+
661+ /**
662+ * Holds if the property `prop` should be copied from the object `pred` to the object `succ`.
663+ *
664+ * This step is used to copy a value the value of our pseudo-property that can later be accessed using a `get` or `getAll` call.
665+ */
666+ override predicate loadStoreStep ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
667+ prop = hiddenUrlPseudoProperty ( ) and
668+ exists ( DataFlow:: PropRead write | write = succ |
669+ write .getPropertyName ( ) = "searchParams" and
670+ write .getBase ( ) = pred
671+ )
672+ }
673+
674+ /**
675+ * Holds if the property `prop` of the object `pred` should be loaded into `succ`.
676+ *
677+ * This step is used to load the value stored in the hidden pseudo-property.
678+ */
679+ override predicate loadStep ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
680+ prop = hiddenUrlPseudoProperty ( ) and
681+ // this is a call to `get` or `getAll` on a `URLSearchParams` object
682+ exists ( string m , DataFlow:: MethodCallNode call | call = succ |
683+ call .getMethodName ( ) = m and
684+ call .getReceiver ( ) = pred and
685+ m .matches ( "get%" )
686+ )
687+ }
644688 }
645689
646690 /**
0 commit comments