@@ -705,6 +705,68 @@ module TaintTracking {
705705 }
706706 }
707707
708+ private module RegExpCaptureSteps {
709+ /** Gets a reference to a string derived from the most recent RegExp match, such as `RegExp.$1` */
710+ private DataFlow:: PropRead getAStaticCaptureRef ( ) {
711+ result =
712+ DataFlow:: globalVarRef ( "RegExp" )
713+ .getAPropertyRead ( [ "$" + [ 1 .. 9 ] , "input" , "lastMatch" , "leftContext" , "rightContext" ,
714+ "$&" , "$^" , "$`" ] )
715+ }
716+
717+ /**
718+ * Gets a control-flow node where `input` is used in a RegExp match.
719+ */
720+ private ControlFlowNode getACaptureSetter ( DataFlow:: Node input ) {
721+ exists ( DataFlow:: MethodCallNode call | result = call .asExpr ( ) |
722+ call .getMethodName ( ) = [ "search" , "replace" , "match" ] and input = call .getReceiver ( )
723+ or
724+ call .getMethodName ( ) = [ "test" , "exec" ] and input = call .getArgument ( 0 )
725+ )
726+ }
727+
728+ /**
729+ * Gets a control-flow node that can locally reach the given static capture reference
730+ * without passing through a capture setter.
731+ *
732+ * This is essentially an intraprocedural def-use analysis that ignores potential
733+ * side effects from calls.
734+ */
735+ private ControlFlowNode getANodeReachingCaptureRef ( DataFlow:: PropRead read ) {
736+ result = read .asExpr ( ) and
737+ read = getAStaticCaptureRef ( )
738+ or
739+ exists ( ControlFlowNode mid |
740+ mid = getANodeReachingCaptureRef ( read ) and
741+ not mid = getACaptureSetter ( _) and
742+ result = mid .getAPredecessor ( )
743+ )
744+ }
745+
746+ /**
747+ * Holds if there is a step `pred -> succ` from the input of a RegExp match to
748+ * a static property of `RegExp` defined.
749+ */
750+ private predicate staticRegExpCaptureStep ( DataFlow:: Node pred , DataFlow:: Node succ ) {
751+ getACaptureSetter ( pred ) = getANodeReachingCaptureRef ( succ )
752+ or
753+ exists ( DataFlow:: MethodCallNode replace |
754+ replace .getMethodName ( ) = "replace" and
755+ getANodeReachingCaptureRef ( succ ) = replace .getCallback ( 1 ) .getFunction ( ) .getEntry ( ) and
756+ pred = replace .getReceiver ( )
757+ )
758+ }
759+
760+ private class StaticRegExpCaptureStep extends AdditionalTaintStep {
761+ StaticRegExpCaptureStep ( ) { staticRegExpCaptureStep ( this , _) }
762+
763+ override predicate step ( DataFlow:: Node pred , DataFlow:: Node succ ) {
764+ pred = this and
765+ staticRegExpCaptureStep ( this , succ )
766+ }
767+ }
768+ }
769+
708770 /**
709771 * A conditional checking a tainted string against a regular expression, which is
710772 * considered to be a sanitizer for all configurations.
0 commit comments