diff --git a/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/StandardEndpointFilters.qll b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/StandardEndpointFilters.qll index 38d339a8527..8b4acbee61e 100644 --- a/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/StandardEndpointFilters.qll +++ b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/StandardEndpointFilters.qll @@ -53,7 +53,11 @@ predicate isSomeModeledArgument(DataFlow::Node n) { /** * Holds if `n` appears to be a numeric value. */ -predicate isNumeric(DataFlow::Node n) { isReadFrom(n, ".*index.*") } +// Performance optimisation: This predicate operates on a large set of +// starting nodes, so use binding hints to suggest computing that set last. +predicate isNumeric(DataFlow::Node n) { + getAnAccessedName(pragma[only_bind_into](n)).regexpMatch(".*index.*") +} /** * Holds if `n` is an argument to a library without sinks. diff --git a/javascript/ql/lib/semmle/javascript/heuristics/SyntacticHeuristics.qll b/javascript/ql/lib/semmle/javascript/heuristics/SyntacticHeuristics.qll index 12356d1bf42..de7ca2a852e 100644 --- a/javascript/ql/lib/semmle/javascript/heuristics/SyntacticHeuristics.qll +++ b/javascript/ql/lib/semmle/javascript/heuristics/SyntacticHeuristics.qll @@ -16,15 +16,23 @@ import javascript */ bindingset[regexp] predicate isReadFrom(DataFlow::Node read, string regexp) { + getAnAccessedName(read).regexpMatch(regexp) +} + +/** + * Gets the "name" accessed by `read`. The "name" is one of: + * - the name of the read variable, if `read` is a variable read + * - the name of the read property, if `read` is a property read + * - the suffix of the getter-method name, if `read` is a getter invocation, for example "Number" in "getNumber" + */ +string getAnAccessedName(DataFlow::Node read) { exists(DataFlow::Node actualRead | actualRead = read.asExpr().getUnderlyingValue().(LogOrExpr).getAnOperand().flow() or // unfold `x || y` once actualRead = read | - exists(string name | name.regexpMatch(regexp) | - actualRead.asExpr().getUnderlyingValue().(VarAccess).getName() = name or - actualRead.(DataFlow::PropRead).getPropertyName() = name or - actualRead.(DataFlow::InvokeNode).getCalleeName() = "get" + name - ) + actualRead.asExpr().getUnderlyingValue().(VarAccess).getName() = result or + actualRead.(DataFlow::PropRead).getPropertyName() = result or + actualRead.(DataFlow::InvokeNode).getCalleeName() = "get" + result ) }