Merge pull request #14405 from erik-krogh/tagCall

JS: recognize tagged template literals as `DataFlow::CallNode`
This commit is contained in:
Erik Krogh Kristensen
2023-10-11 11:25:34 +02:00
committed by GitHub
25 changed files with 132 additions and 61 deletions

View File

@@ -1269,6 +1269,41 @@ module DataFlow {
result >= 0 and kind = "call" and result = originalCall.getNumArgument() - 1
}
}
/**
* A data flow node representing a call with a tagged template literal.
*/
private class TaggedTemplateLiteralCallNode extends CallNodeDef, ValueNode {
override TaggedTemplateExpr astNode;
override InvokeExpr getInvokeExpr() { none() } // There is no InvokeExpr for this.
override string getCalleeName() {
result = astNode.getTag().getUnderlyingValue().(Identifier).getName()
}
override DataFlow::Node getCalleeNode() { result = DataFlow::valueNode(astNode.getTag()) }
override DataFlow::Node getArgument(int i) {
// the first argument sent to the function is the array of string parts, which we don't model.
// rank is 1-indexed, which is perfect here.
result =
DataFlow::valueNode(rank[i](Expr e, int index |
e = astNode.getTemplate().getElement(index) and not e instanceof TemplateElement
|
e order by index
))
}
override DataFlow::Node getAnArgument() { result = this.getArgument(_) }
override DataFlow::Node getASpreadArgument() { none() }
// we don't model the string constants as arguments, but we still count them.
override int getNumArgument() { result = count(this.getArgument(_)) + 1 }
override DataFlow::Node getReceiver() { none() }
}
}
/**

View File

@@ -92,13 +92,20 @@ class InvokeNode extends DataFlow::SourceNode instanceof DataFlow::Impl::InvokeN
* but the position of `z` cannot be determined, hence there are no first and second
* argument nodes.
*/
DataFlow::Node getArgument(int i) { result = super.getArgument(i) }
cached
DataFlow::Node getArgument(int i) {
result = super.getArgument(i) and Stages::DataFlowStage::ref()
}
/** Gets the data flow node corresponding to an argument of this invocation. */
DataFlow::Node getAnArgument() { result = super.getAnArgument() }
cached
DataFlow::Node getAnArgument() { result = super.getAnArgument() and Stages::DataFlowStage::ref() }
/** Gets the data flow node corresponding to the last argument of this invocation. */
DataFlow::Node getLastArgument() { result = this.getArgument(this.getNumArgument() - 1) }
cached
DataFlow::Node getLastArgument() {
result = this.getArgument(this.getNumArgument() - 1) and Stages::DataFlowStage::ref()
}
/**
* Gets a data flow node corresponding to an array of values being passed as

View File

@@ -145,6 +145,12 @@ module Stages {
exists(any(DataFlow::PropRef ref).getBase())
or
exists(any(DataFlow::ClassNode cls))
or
exists(any(DataFlow::CallNode node).getArgument(_))
or
exists(any(DataFlow::CallNode node).getAnArgument())
or
exists(any(DataFlow::CallNode node).getLastArgument())
}
}