JS: Avoid more bad joins due to locality

This commit is contained in:
Asger F
2025-10-20 11:57:39 +02:00
parent 269489e817
commit 4645f327a5
5 changed files with 26 additions and 7 deletions

View File

@@ -477,6 +477,8 @@ module AST {
DataFlow::AnalyzedNode analyze() { result = DataFlow::valueNode(this).analyze() }
/** Gets the data flow node associated with this program element. */
overlay[caller]
pragma[inline]
DataFlow::ValueNode flow() { result = DataFlow::valueNode(this) }
/**

View File

@@ -1016,7 +1016,7 @@ overlay[global]
pragma[inline]
private predicate isUsedAsNumber(DataFlow::LocalSourceNode value) {
any(Comparison compare)
.hasOperands(value.getALocalUse().asExpr(), any(Expr e | e.analyze().getAType() = TTNumber()))
.hasOperands(value.getALocalUse().asExpr(), any(Expr e | canBeNumber(e.analyze())))
or
value.flowsToExpr(any(ArithmeticExpr e).getAnOperand())
or
@@ -1031,6 +1031,16 @@ private predicate isUsedAsNumber(DataFlow::LocalSourceNode value) {
)
}
bindingset[node]
overlay[global]
pragma[inline_late]
private predicate canBeString(DataFlow::AnalyzedNode node) { node.getAType() = TTString() }
bindingset[node]
overlay[global]
pragma[inline_late]
private predicate canBeNumber(DataFlow::AnalyzedNode node) { node.getAType() = TTNumber() }
/**
* Holds if `source` may be interpreted as a regular expression.
*/
@@ -1038,14 +1048,14 @@ overlay[global]
cached
predicate isInterpretedAsRegExp(DataFlow::Node source) {
Stages::Taint::ref() and
source.analyze().getAType() = TTString() and
canBeString(source) and
(
// The first argument to an invocation of `RegExp` (with or without `new`).
source = DataFlow::globalVarRef("RegExp").getAnInvocation().getArgument(0)
or
// The argument of a call that coerces the argument to a regular expression.
exists(DataFlow::MethodCallNode mce, string methodName |
mce.getReceiver().analyze().getAType() = TTString() and
canBeString(mce.getReceiver()) and
mce.getMethodName() = methodName and
not exists(Function func | func = mce.getACallee() |
not isNativeStringMethod(func, methodName)

View File

@@ -1431,6 +1431,7 @@ module DataFlow {
* This predicate is only defined for expressions, properties, and for statements that declare
* a function, a class, or a TypeScript namespace or enum.
*/
pragma[nomagic]
ValueNode valueNode(AstNode nd) { result.getAstNode() = nd }
/**

View File

@@ -45,7 +45,7 @@ module LazyCache {
pragma[noopt]
override DataFlow::Node getImportedModuleNode() {
this instanceof LazyCacheImport and
result = this.flow()
result = DataFlow::valueNode(this)
or
exists(LazyCacheVariable variable, Expr base, PropAccess access, string localName |
// To avoid recursion, this should not depend on `SourceNode`.
@@ -54,7 +54,7 @@ module LazyCache {
access.getBase() = base and
localName = this.getLocalAlias() and
access.getPropertyName() = localName and
result = access.flow()
result = DataFlow::valueNode(access)
)
}
}

View File

@@ -50,12 +50,18 @@ class ForOfLoopStep extends AdditionalFlowInternal {
) {
exists(ForOfStmt stmt |
pred = getSynthesizedNode(stmt, "for-of-map-key") and
contents.asSingleton().asArrayIndex() = 0
contents = arrayIndex0()
or
pred = getSynthesizedNode(stmt, "for-of-map-value") and
contents.asSingleton().asArrayIndex() = 1
contents = arrayIndex1()
|
succ = DataFlow::lvalueNode(stmt.getLValue())
)
}
}
pragma[nomagic]
private DataFlow::ContentSet arrayIndex0() { result.asSingleton().asArrayIndex() = 0 }
pragma[nomagic]
private DataFlow::ContentSet arrayIndex1() { result.asSingleton().asArrayIndex() = 1 }