JS: Improve performance of StandardEndpointFilters::isNumeric

Factor the regex-independent logic of `isReadFrom` into its own predicate.

Call this predicate directly from `isNumeric`, which doesn't have much
restrictive context on the set of starting nodes.
Use a binding hint to discourage starting with all expr nodes in this case.

Other callers may have more restrictive context on the set of nodes,
so they are not changed.
This commit is contained in:
Aditya Sharad
2021-12-22 11:16:32 -08:00
committed by Henry Mercer
parent 1b7088abde
commit 1d507f1993
2 changed files with 18 additions and 6 deletions

View File

@@ -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.

View File

@@ -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
)
}