@@ -958,6 +958,27 @@ private predicate isUsedAsNonMatchObject(DataFlow::MethodCallNode call) {
958958 )
959959}
960960
961+ /**
962+ * Holds if `call` is a call to `search` whose result is used in a way that suggests it returns a number.
963+ */
964+ pragma [ inline]
965+ private predicate isUsedAsNumber ( DataFlow:: LocalSourceNode value ) {
966+ any ( Comparison compare )
967+ .hasOperands ( value .getALocalUse ( ) .asExpr ( ) , any ( Expr e | e .analyze ( ) .getAType ( ) = TTNumber ( ) ) )
968+ or
969+ value .flowsToExpr ( any ( ArithmeticExpr e ) .getAnOperand ( ) )
970+ or
971+ value .flowsToExpr ( any ( UnaryExpr e | e .getOperator ( ) = "-" ) .getOperand ( ) )
972+ or
973+ value .flowsToExpr ( any ( IndexExpr expr ) .getPropertyNameExpr ( ) )
974+ or
975+ exists ( DataFlow:: CallNode call |
976+ call .getCalleeName ( ) =
977+ [ "substring" , "substr" , "slice" , "splice" , "charAt" , "charCodeAt" , "codePointAt" ] and
978+ value .flowsTo ( call .getAnArgument ( ) )
979+ )
980+ }
981+
961982/**
962983 * Holds if `source` may be interpreted as a regular expression.
963984 */
@@ -985,9 +1006,9 @@ predicate isInterpretedAsRegExp(DataFlow::Node source) {
9851006 methodName = "search" and
9861007 source = mce .getArgument ( 0 ) and
9871008 mce .getNumArgument ( ) = 1 and
988- // "search" is a common method name, and so we exclude chained accesses
989- // because `String.prototype.search` returns a number
990- not exists ( PropAccess p | p . getBase ( ) = mce . getEnclosingExpr ( ) )
1009+ // "search" is a common method name, and the built-in "search" method is rarely used,
1010+ // so to reduce FPs we also require that the return value appears to be used as a number.
1011+ isUsedAsNumber ( mce )
9911012 )
9921013 or
9931014 exists ( DataFlow:: SourceNode schema | schema = JsonSchema:: getAPartOfJsonSchema ( ) |
0 commit comments