@@ -958,6 +958,27 @@ private predicate isUsedAsNonMatchObject(DataFlow::MethodCallNode call) {
958
958
)
959
959
}
960
960
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
+
961
982
/**
962
983
* Holds if `source` may be interpreted as a regular expression.
963
984
*/
@@ -985,9 +1006,9 @@ predicate isInterpretedAsRegExp(DataFlow::Node source) {
985
1006
methodName = "search" and
986
1007
source = mce .getArgument ( 0 ) and
987
1008
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 )
991
1012
)
992
1013
or
993
1014
exists ( DataFlow:: SourceNode schema | schema = JsonSchema:: getAPartOfJsonSchema ( ) |
0 commit comments