Files
codeql/javascript/ql/lib/semmle/javascript/dataflow/DataFlow.qll

1725 lines
56 KiB
Plaintext

/**
* Provides classes for working with a data flow graph-based
* program representation.
*
* We currently consider three kinds of data flow:
*
* 1. Flow within an expression, for example from the operands of a `&&`
* expression to the expression itself.
* 2. Flow through local variables, that is, from definitions to uses.
* Captured variables are treated flow-insensitively, that is, all
* definitions are considered to flow to all uses, while for non-captured
* variables only definitions that can actually reach a use are considered.
* 3. Flow into and out of immediately invoked function expressions, that is,
* flow from arguments to parameters, and from returned expressions to the
* function expression itself.
*
* Flow through global variables, object properties or function calls is not
* modeled (except for immediately invoked functions as explained above).
*/
import javascript
private import internal.CallGraphs
private import internal.FlowSteps as FlowSteps
private import internal.DataFlowNode
private import internal.AnalyzedParameters
private import internal.PreCallGraphStep
private import semmle.javascript.internal.CachedStages
module DataFlow {
/**
* A node in the data flow graph.
*/
class Node extends TNode {
/**
* Gets a data flow node from which data may flow to this node in one local step.
*/
Node getAPredecessor() { localFlowStep(result, this) }
/**
* Gets a data flow node to which data may flow from this node in one local step.
*/
Node getASuccessor() { localFlowStep(this, result) }
/**
* Gets a source node from which data may flow to this node in zero or more local steps.
*/
SourceNode getALocalSource() { result.flowsTo(this) }
/**
* Holds if the flow information for this node is incomplete.
*
* This predicate holds if there may be a source flow node from which data flows into
* this node, but that node is not a result of `getALocalSource()` due to analysis
* incompleteness. The parameter `cause` is bound to a string describing the source of
* incompleteness.
*
* For example, since this analysis is intra-procedural, data flow from actual arguments
* to formal parameters is not modeled. Hence, if `p` is an access to a parameter,
* `p.getALocalSource()` does _not_ return the corresponding argument, and
* `p.isIncomplete("call")` holds.
*/
predicate isIncomplete(Incompleteness cause) { isIncomplete(this, cause) }
/** Gets type inference results for this data flow node. */
AnalyzedNode analyze() { result = this }
/** Gets the expression corresponding to this data flow node, if any. */
Expr asExpr() { this = TValueNode(result) }
/**
* Gets the expression enclosing this data flow node.
* In most cases the result is the same as `asExpr()`, however this method
* additionally includes the `InvokeExpr` corresponding to reflective calls.
*/
Expr getEnclosingExpr() {
result = this.asExpr() or
this = DataFlow::reflectiveCallNode(result)
}
/** Gets the AST node corresponding to this data flow node, if any. */
ASTNode getAstNode() { none() }
/** Gets the basic block to which this node belongs. */
BasicBlock getBasicBlock() { none() }
/** Gets the container in which this node occurs. */
StmtContainer getContainer() { result = this.getBasicBlock().getContainer() }
/** Gets the toplevel in which this node occurs. */
TopLevel getTopLevel() { result = this.getContainer().getTopLevel() }
/**
* Holds if this data flow node accesses the global variable `g`, either directly
* or through the `window` object.
*/
predicate accessesGlobal(string g) { globalVarRef(g).flowsTo(this) }
/** Holds if this node may evaluate to the string `s`, possibly through local data flow. */
pragma[nomagic]
predicate mayHaveStringValue(string s) {
this.getAPredecessor().mayHaveStringValue(s)
or
s = this.getStringValue()
}
/** Gets the string value of this node, if it is a string literal or constant string concatenation. */
string getStringValue() { result = this.asExpr().getStringValue() }
/** Holds if this node may evaluate to the Boolean value `b`. */
predicate mayHaveBooleanValue(boolean b) {
this.getAPredecessor().mayHaveBooleanValue(b)
or
b = true and this.asExpr().(BooleanLiteral).getValue() = "true"
or
b = false and this.asExpr().(BooleanLiteral).getValue() = "false"
}
/** Gets the integer value of this node, if it is an integer constant. */
int getIntValue() { result = this.asExpr().getIntValue() }
/** Gets a function value that may reach this node. */
final FunctionNode getAFunctionValue() {
CallGraph::getAFunctionReference(result, 0).flowsTo(this)
}
/** Gets a function value that may reach this node with the given `imprecision` level. */
final FunctionNode getAFunctionValue(int imprecision) {
CallGraph::getAFunctionReference(result, imprecision).flowsTo(this)
}
/**
* Gets a function value that may reach this node,
* possibly derived from a partial function invocation.
*/
final FunctionNode getABoundFunctionValue(int boundArgs) {
result = this.getAFunctionValue() and boundArgs = 0
or
CallGraph::getABoundFunctionReference(result, boundArgs, _).flowsTo(this)
}
/**
* Holds if this expression may refer to the initial value of parameter `p`.
*/
predicate mayReferToParameter(Parameter p) { parameterNode(p).(SourceNode).flowsTo(this) }
/**
* Holds if this element is at the specified location.
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
cached
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
none()
}
/** Gets the file this data flow node comes from. */
File getFile() { none() } // overridden in subclasses
/** Gets the start line of this data flow node. */
int getStartLine() { this.hasLocationInfo(_, result, _, _, _) }
/** Gets the start column of this data flow node. */
int getStartColumn() { this.hasLocationInfo(_, _, result, _, _) }
/** Gets the end line of this data flow node. */
int getEndLine() { this.hasLocationInfo(_, _, _, result, _) }
/** Gets the end column of this data flow node. */
int getEndColumn() { this.hasLocationInfo(_, _, _, _, result) }
/** Gets a textual representation of this element. */
cached
string toString() { none() }
/**
* Gets the immediate predecessor of this node, if any.
*
* A node with an immediate predecessor can usually only have the value that flows
* into its from its immediate predecessor.
*/
cached
DataFlow::Node getImmediatePredecessor() {
lvalueFlowStep(result, this) and
not lvalueDefaultFlowStep(_, this)
or
immediateFlowStep(result, this)
or
// Refinement of variable -> original definition of variable
exists(SsaRefinementNode refinement |
this = TSsaDefNode(refinement) and
result = TSsaDefNode(refinement.getAnInput())
)
or
exists(SsaPhiNode phi |
this = TSsaDefNode(phi) and
result = TSsaDefNode(phi.getRephinedVariable())
)
or
// IIFE call -> return value of IIFE
exists(Function fun |
localCall(this.asExpr(), fun) and
result = unique(Expr ret | ret = fun.getAReturnedExpr()).flow() and
not fun.getExit().isJoin() // can only reach exit by the return statement
)
or
FlowSteps::identityFunctionStep(result, this)
}
/**
* Gets the static type of this node as determined by the TypeScript type system.
*/
private Type getType() {
exists(AST::ValueNode node |
this = TValueNode(node) and
ast_node_type(node, result)
)
or
exists(BindingPattern pattern |
this = lvalueNode(pattern) and
ast_node_type(pattern, result)
)
or
exists(MethodDefinition def |
this = TThisNode(def.getInit()) and
ast_node_type(def.getDeclaringClass(), result)
)
}
/**
* Gets the type annotation describing the type of this node,
* provided that a static type could not be found.
*
* Doesn't take field types and function return types into account.
*/
private TypeAnnotation getFallbackTypeAnnotation() {
exists(BindingPattern pattern |
this = valueNode(pattern) and
result = pattern.getTypeAnnotation()
)
or
result = this.getAPredecessor().getFallbackTypeAnnotation()
or
exists(DataFlow::ClassNode cls, string fieldName |
this = cls.getAReceiverNode().getAPropertyRead(fieldName) and
result = cls.getFieldTypeAnnotation(fieldName)
)
}
/**
* Holds if this node is annotated with the given named type,
* or is declared as a subtype thereof, or is a union or intersection containing such a type.
*/
cached
predicate hasUnderlyingType(string globalName) {
Stages::TypeTracking::ref() and
this.getType().hasUnderlyingType(globalName)
or
this.getFallbackTypeAnnotation().getAnUnderlyingType().hasQualifiedName(globalName)
}
/**
* Holds if this node is annotated with the given named type,
* or is declared as a subtype thereof, or is a union or intersection containing such a type.
*/
cached
predicate hasUnderlyingType(string moduleName, string typeName) {
Stages::TypeTracking::ref() and
this.getType().hasUnderlyingType(moduleName, typeName)
or
this.getFallbackTypeAnnotation().getAnUnderlyingType().hasQualifiedName(moduleName, typeName)
}
}
/**
* A node in the data flow graph which corresponds to an expression,
* destructuring pattern, or declaration of a function, class, namespace, or enum.
*
* Examples:
* ```js
* x + y
* Math.abs(x)
* class C {}
* function f(x, y) {}
* ```
*/
class ValueNode extends Node, TValueNode {
AST::ValueNode astNode;
ValueNode() { this = TValueNode(astNode) }
/** Gets the expression or declaration this node corresponds to. */
override AST::ValueNode getAstNode() { result = astNode }
override BasicBlock getBasicBlock() { astNode = result.getANode() }
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
Stages::DataFlowStage::ref() and
astNode.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
override File getFile() { result = astNode.getFile() }
override string toString() { Stages::DataFlowStage::ref() and result = astNode.toString() }
}
/**
* A node in the data flow graph which corresponds to an SSA variable definition.
*/
class SsaDefinitionNode extends Node, TSsaDefNode {
SsaDefinition ssa;
SsaDefinitionNode() { this = TSsaDefNode(ssa) }
/** Gets the SSA variable defined at this data flow node. */
SsaVariable getSsaVariable() { result = ssa.getVariable() }
override BasicBlock getBasicBlock() { result = ssa.getBasicBlock() }
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
ssa.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
override string toString() { result = ssa.getSourceVariable().getName() }
override File getFile() { result = ssa.getBasicBlock().getFile() }
override ASTNode getAstNode() { none() }
}
/**
* A node in the data flow graph which corresponds to a `@property`.
*/
private class PropNode extends Node, TPropNode {
@property prop;
PropNode() { this = TPropNode(prop) }
override BasicBlock getBasicBlock() { result = prop.(ControlFlowNode).getBasicBlock() }
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
prop.(Locatable)
.getLocation()
.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
override string toString() { result = prop.(ASTNode).toString() }
override File getFile() { result = prop.(ASTNode).getFile() }
override ASTNode getAstNode() { result = prop }
}
/**
* A node in the data flow graph which corresponds to the rest pattern of a
* destructuring pattern.
*/
private class RestPatternNode extends Node, TRestPatternNode {
DestructuringPattern pattern;
Expr rest;
RestPatternNode() { this = TRestPatternNode(pattern, rest) }
override BasicBlock getBasicBlock() { result = rest.getBasicBlock() }
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
rest.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
override string toString() { result = "..." + rest.toString() }
override File getFile() { result = pattern.getFile() }
override ASTNode getAstNode() { result = rest }
}
/**
* A node in the data flow graph which corresponds to an element pattern of an
* array pattern.
*/
private class ElementPatternNode extends Node, TElementPatternNode {
ArrayPattern pattern;
Expr elt;
ElementPatternNode() { this = TElementPatternNode(pattern, elt) }
override BasicBlock getBasicBlock() { result = elt.getBasicBlock() }
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
elt.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
override string toString() { result = elt.toString() }
override File getFile() { result = pattern.getFile() }
override ASTNode getAstNode() { result = elt }
}
/**
* A node in the data flow graph which represents the (implicit) write of an element
* in an array expression to the underlying array.
*
* That is, for an array expression `["first", ,"third"]`, we have two array element nodes,
* one representing the write of expression `"first"` into the 0th element of the array,
* and one representing the write of `"third"` into its second element.
*/
private class ElementNode extends Node, TElementNode {
ArrayExpr arr;
Expr elt;
ElementNode() { this = TElementNode(arr, elt) }
override BasicBlock getBasicBlock() { result = elt.getBasicBlock() }
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
elt.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
override string toString() { result = elt.toString() }
override File getFile() { result = arr.getFile() }
override ASTNode getAstNode() { result = elt }
}
/**
* A node in the data flow graph which corresponds to the reflective call performed
* by a `.call` or `.apply` invocation.
*/
private class ReflectiveCallNode extends Node, TReflectiveCallNode {
MethodCallExpr call;
string kind;
ReflectiveCallNode() { this = TReflectiveCallNode(call, kind) }
override BasicBlock getBasicBlock() { result = call.getBasicBlock() }
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
call.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
override string toString() { result = "reflective call" }
override File getFile() { result = call.getFile() }
}
/**
* A node referring to the module imported at a named or default ES2015 import declaration.
*/
private class DestructuredModuleImportNode extends Node, TDestructuredModuleImportNode {
ImportDeclaration imprt;
DestructuredModuleImportNode() { this = TDestructuredModuleImportNode(imprt) }
override BasicBlock getBasicBlock() { result = imprt.getBasicBlock() }
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
imprt.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
override string toString() { result = imprt.toString() }
override File getFile() { result = imprt.getFile() }
}
/**
* A data flow node that reads or writes an object property or class member.
*
* The default subclasses do not model global variable references or variable
* references inside `with` statements as property references.
*
* See `PropWrite` and `PropRead` for concrete syntax examples.
*/
abstract class PropRef extends Node {
/**
* Gets the data flow node corresponding to the base object
* whose property is read from or written to.
*/
cached
abstract Node getBase();
/**
* Gets the expression specifying the name of the property being
* read or written, if any.
*
* This is usually either an identifier or a literal.
*/
abstract Expr getPropertyNameExpr();
/**
* Holds if this property reference may access a property named `propName`.
*/
predicate mayHavePropertyName(string propName) {
propName = this.getPropertyName() or
this.getPropertyNameExpr().flow().mayHaveStringValue(propName)
}
/**
* Gets the name of the property being read or written,
* if it can be statically determined.
*
* By default, this predicate is undefined for dynamic property references
* such as `e[computePropertyName()]` and for spread/rest properties.
*/
abstract string getPropertyName();
/**
* Holds if this data flow node accesses property `p` on base node `base`.
*/
pragma[noinline]
predicate accesses(Node base, string p) { this.getBase() = base and this.getPropertyName() = p }
/**
* Holds if this data flow node reads or writes a private field in a class.
*/
predicate isPrivateField() {
this.getPropertyName().charAt(0) = "#" and this.getPropertyNameExpr() instanceof Label
}
/**
* Gets an accessor (`get` or `set` method) that may be invoked by this property reference.
*/
final DataFlow::FunctionNode getAnAccessorCallee() {
result = CallGraph::getAnAccessorCallee(this)
}
}
/**
* A data flow node that writes to an object property.
*
* For example, all of the following are property writes:
* ```js
* obj.f = value; // assignment to a property
* obj[e] = value; // assignment to a computed property
* { f: value } // property initializer
* { g() {} } // object literal method
* { get g() {}, set g(v) {} } // accessor methods (have no rhs value)
* class C {
* constructor(public x: number); // parameter field (TypeScript only)
* static m() {} // static method
* g() {} // instance method
* }
* Object.defineProperty(obj, 'f', { value: 5} ) // call to defineProperty
* <View width={value}/> // JSX attribute
* ```
*/
abstract class PropWrite extends PropRef {
/**
* Gets the data flow node corresponding to the value being written,
* if it can be statically determined.
*
* This predicate is undefined for spread properties, accessor
* properties, and most uses of `Object.defineProperty`.
*/
pragma[nomagic]
abstract Node getRhs();
/**
* Holds if this data flow node writes the value of `rhs` to property
* `prop` of the object that `base` evaluates to.
*/
pragma[noinline]
predicate writes(DataFlow::Node base, string prop, DataFlow::Node rhs) {
base = this.getBase() and
prop = this.getPropertyName() and
rhs = this.getRhs()
}
/**
* Gets the node where the property write happens in the control flow graph.
*/
abstract ControlFlowNode getWriteNode();
}
/**
* A property access in lvalue position, viewed as a property definition node.
*/
private class PropLValueAsPropWrite extends PropWrite, ValueNode {
override PropAccess astNode;
PropLValueAsPropWrite() { astNode instanceof LValue }
override Node getBase() {
result = valueNode(astNode.getBase()) and
Stages::DataFlowStage::ref()
}
override Expr getPropertyNameExpr() { result = astNode.getPropertyNameExpr() }
override string getPropertyName() { result = astNode.getPropertyName() }
override Node getRhs() { result = valueNode(astNode.(LValue).getRhs()) }
override ControlFlowNode getWriteNode() { result = astNode.(LValue).getDefNode() }
}
/**
* A property of an object literal, viewed as a data flow node that writes
* to the corresponding property.
*/
private class PropInitAsPropWrite extends PropWrite, PropNode {
override Property prop;
override Node getBase() { result = valueNode(prop.getObjectExpr()) }
override Expr getPropertyNameExpr() { result = prop.getNameExpr() }
override string getPropertyName() { result = prop.getName() }
override Node getRhs() { result = valueNode(prop.(ValueProperty).getInit()) }
override ControlFlowNode getWriteNode() { result = prop }
}
/**
* A call to `Object.defineProperty`, viewed as a data flow node that
* writes to the corresponding property.
*/
private class ObjectDefinePropertyAsPropWrite extends PropWrite, ValueNode {
override MethodCallExpr astNode;
ObjectDefinePropertyAsPropWrite() {
astNode.getReceiver().(GlobalVarAccess).getName() = "Object" and
astNode.getMethodName() = "defineProperty"
}
override Node getBase() { result = astNode.getArgument(0).flow() }
override Expr getPropertyNameExpr() { result = astNode.getArgument(1) }
override string getPropertyName() { result = astNode.getArgument(1).getStringValue() }
override Node getRhs() {
exists(DataFlow::SourceNode descriptor |
descriptor = astNode.getArgument(2).flow().getALocalSource()
|
result =
descriptor
.getAPropertyWrite("get")
.getRhs()
.getALocalSource()
.(DataFlow::FunctionNode)
.getAReturn()
or
result = descriptor.getAPropertyWrite("value").getRhs()
)
}
override ControlFlowNode getWriteNode() { result = astNode }
}
/**
* A static member definition, viewed as a data flow node that adds
* a property to the class.
*/
private class StaticClassMemberAsPropWrite extends PropWrite, PropNode {
override MemberDefinition prop;
StaticClassMemberAsPropWrite() { prop.isStatic() }
override Node getBase() { result = valueNode(prop.getDeclaringClass()) }
override Expr getPropertyNameExpr() { result = prop.getNameExpr() }
override string getPropertyName() { result = prop.getName() }
override Node getRhs() {
not prop instanceof AccessorMethodDefinition and
result = valueNode(prop.getInit())
}
override ControlFlowNode getWriteNode() { result = prop }
}
/**
* An instance method definition, viewed as a data flow node that adds
* a property to an unseen value.
*/
private class InstanceMethodAsPropWrite extends PropWrite, PropNode {
override MethodDefinition prop;
InstanceMethodAsPropWrite() { not prop.isStatic() }
override Node getBase() { none() } // The prototype has no DataFlow node
override Expr getPropertyNameExpr() { result = prop.getNameExpr() }
override string getPropertyName() { result = prop.getName() }
override Node getRhs() {
not prop instanceof AccessorMethodDefinition and
result = valueNode(prop.getInit())
}
override ControlFlowNode getWriteNode() { result = prop }
}
/**
* A JSX attribute definition, viewed as a data flow node that writes properties to
* the JSX element it is in.
*/
private class JsxAttributeAsPropWrite extends PropWrite, PropNode {
override JSXAttribute prop;
override Node getBase() { result = valueNode(prop.getElement()) }
override Expr getPropertyNameExpr() { result = prop.getNameExpr() }
override string getPropertyName() { result = prop.getName() }
override Node getRhs() { result = valueNode(prop.getValue()) }
override ControlFlowNode getWriteNode() { result = prop }
}
/**
* A field induced by an initializing constructor parameter, seen as a property write (TypeScript only).
*/
private class ParameterFieldAsPropWrite extends PropWrite, PropNode {
override ParameterField prop;
override Node getBase() {
thisNode(result, prop.getDeclaringClass().getConstructor().getBody())
}
override Expr getPropertyNameExpr() {
none() // The parameter value is not the name of the field
}
override string getPropertyName() { result = prop.getName() }
override Node getRhs() {
exists(Parameter param, Node paramNode |
param = prop.getParameter() and
parameterNode(paramNode, param)
|
result = paramNode
)
}
override ControlFlowNode getWriteNode() { result = prop.getParameter() }
}
/**
* An instance field with an initializer, seen as a property write.
*/
private class InstanceFieldAsPropWrite extends PropWrite, PropNode {
override FieldDefinition prop;
InstanceFieldAsPropWrite() {
not prop.isStatic() and
not prop instanceof ParameterField and
exists(prop.getInit())
}
override Node getBase() {
thisNode(result, prop.getDeclaringClass().getConstructor().getBody())
}
override Expr getPropertyNameExpr() { result = prop.getNameExpr() }
override string getPropertyName() { result = prop.getName() }
override Node getRhs() { result = valueNode(prop.getInit()) }
override ControlFlowNode getWriteNode() { result = prop }
}
/**
* A data flow node that reads an object property.
*
* For example, all the following are property reads:
* ```js
* obj.f // property access
* obj[e] // computed property access
* let { f } = obj; // destructuring object pattern
* let [x, y] = array; // destructuring array pattern
* function f({ f }) {} // destructuring pattern (in parameter)
* for (let elm of array) {} // element access in for..of loop
* import { join } from 'path'; // named import specifier
* ```
*/
abstract class PropRead extends PropRef, SourceNode { }
/**
* A property read, considered as a source node.
*
* Note that we cannot simplify the characteristic predicate to `this instanceof PropRead`,
* since `PropRead` is itself a subclass of `SourceNode`.
*/
private class PropReadAsSourceNode extends SourceNode::Range {
PropReadAsSourceNode() {
this = TPropNode(any(PropertyPattern p)) or
this instanceof RestPatternNode or
this instanceof ElementPatternNode or
this = lvalueNode(any(ForOfStmt stmt).getLValue())
}
}
/**
* A property access in rvalue position.
*/
private class PropRValueAsPropRead extends PropRead, ValueNode {
override PropAccess astNode;
PropRValueAsPropRead() { astNode instanceof RValue }
override Node getBase() { result = valueNode(astNode.getBase()) }
override Expr getPropertyNameExpr() { result = astNode.getPropertyNameExpr() }
override string getPropertyName() { result = astNode.getPropertyName() }
}
/**
* A property pattern viewed as a property read; for instance, in
* `var { p: q } = o`, `p` is a read of property `p` of `o`.
*/
private class PropPatternAsPropRead extends PropRead, PropNode {
override PropertyPattern prop;
/** Gets the value pattern of this property pattern. */
Expr getValuePattern() { result = prop.getValuePattern() }
override Node getBase() { result = TValueNode(prop.getObjectPattern()) }
override Expr getPropertyNameExpr() { result = prop.getNameExpr() }
override string getPropertyName() { result = prop.getName() }
}
/**
* A rest pattern viewed as a property read; for instance, in
* `var { ...ps } = o`, `ps` is a read of all properties of `o`, and similar
* for `[ ...elts ] = arr`.
*/
private class RestPatternAsPropRead extends PropRead, RestPatternNode {
override Node getBase() { result = TValueNode(pattern) }
override Expr getPropertyNameExpr() { none() }
override string getPropertyName() { none() }
}
/**
* An array element pattern viewed as a property read; for instance, in
* `var [ x, y ] = arr`, `x` is a read of property 0 of `arr` and similar
* for `y`.
*/
private class ElementPatternAsPropRead extends PropRead, ElementPatternNode {
override Node getBase() { result = TValueNode(pattern) }
override Expr getPropertyNameExpr() { none() }
override string getPropertyName() {
exists(int i |
elt = pattern.getElement(i) and
result = i.toString()
)
}
}
/**
* A named import specifier seen as a property read on the imported module.
*/
private class ImportSpecifierAsPropRead extends PropRead, ValueNode {
override ImportSpecifier astNode;
ImportDeclaration imprt;
ImportSpecifierAsPropRead() {
astNode = imprt.getASpecifier() and
not astNode.isTypeOnly() and
exists(astNode.getImportedName())
}
override Node getBase() { result = TDestructuredModuleImportNode(imprt) }
override Expr getPropertyNameExpr() { result = astNode.getImported() }
override string getPropertyName() { result = astNode.getImportedName() }
}
/**
* The left-hand side of a `for..of` statement, seen as a property read
* on the object being iterated over.
*/
private class ForOfLvalueAsPropRead extends PropRead {
ForOfStmt stmt;
ForOfLvalueAsPropRead() { this = lvalueNode(stmt.getLValue()) }
override Node getBase() { result = stmt.getIterationDomain().flow() }
override Expr getPropertyNameExpr() { none() }
override string getPropertyName() { none() }
}
/**
* A data flow node representing an HTML attribute.
*/
class HtmlAttributeNode extends DataFlow::Node, THtmlAttributeNode {
HTML::Attribute attr;
HtmlAttributeNode() { this = THtmlAttributeNode(attr) }
override string toString() { result = attr.toString() }
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
attr.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
/** Gets the attribute corresponding to this data flow node. */
HTML::Attribute getAttribute() { result = attr }
override File getFile() { result = attr.getFile() }
}
/**
* A data flow node representing the exceptions thrown by a function.
*/
class ExceptionalFunctionReturnNode extends DataFlow::Node, TExceptionalFunctionReturnNode {
Function function;
ExceptionalFunctionReturnNode() { this = TExceptionalFunctionReturnNode(function) }
override string toString() { result = "exceptional return of " + function.describe() }
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
function.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
override BasicBlock getBasicBlock() { result = function.getExit().getBasicBlock() }
/**
* Gets the function corresponding to this exceptional return node.
*/
Function getFunction() { result = function }
override File getFile() { result = function.getFile() }
}
/**
* A data flow node representing the values returned by a function.
*/
class FunctionReturnNode extends DataFlow::Node, TFunctionReturnNode {
Function function;
FunctionReturnNode() { this = TFunctionReturnNode(function) }
override string toString() { result = "return of " + function.describe() }
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
function.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
override BasicBlock getBasicBlock() { result = function.getExit().getBasicBlock() }
/**
* Gets the function corresponding to this return node.
*/
Function getFunction() { result = function }
override File getFile() { result = function.getFile() }
}
/**
* A data flow node representing the exceptions thrown by the callee of an invocation.
*/
class ExceptionalInvocationReturnNode extends DataFlow::Node, TExceptionalInvocationReturnNode {
InvokeExpr invoke;
ExceptionalInvocationReturnNode() { this = TExceptionalInvocationReturnNode(invoke) }
override string toString() { result = "exceptional return of " + invoke.toString() }
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
invoke.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
override BasicBlock getBasicBlock() { result = invoke.getBasicBlock() }
/**
* Gets the invocation corresponding to this exceptional return node.
*/
DataFlow::InvokeNode getInvocation() { result = invoke.flow() }
override File getFile() { result = invoke.getFile() }
}
/**
* A pseudo-node representing the root of a global access path.
*/
private class GlobalAccessPathRoot extends TGlobalAccessPathRoot, DataFlow::Node {
override string toString() { result = "global access path" }
}
/**
* INTERNAL. DO NOT USE.
*
* Gets a pseudo-node representing the root of a global access path.
*/
DataFlow::Node globalAccessPathRootPseudoNode() { result instanceof TGlobalAccessPathRoot }
/**
* Gets a data flow node representing the underlying call performed by the given
* call to `Function.prototype.call` or `Function.prototype.apply`.
*
* For example, for an expression `fn.call(x, y)`, this gets a call node with `fn` as the
* callee, `x` as the receiver, and `y` as the first argument.
*/
DataFlow::InvokeNode reflectiveCallNode(InvokeExpr expr) { result = TReflectiveCallNode(expr, _) }
/**
* Provides classes representing various kinds of calls.
*
* Subclass the classes in this module to introduce new kinds of calls. If you want to
* refine the behavior of the analysis on existing kinds of calls, subclass `InvokeNode`
* instead.
*/
module Impl {
/**
* A data flow node representing a function invocation, either explicitly or reflectively,
* and either with or without `new`.
*/
abstract class InvokeNodeDef extends DataFlow::Node {
/** Gets the syntactic invoke expression underlying this function invocation. */
abstract InvokeExpr getInvokeExpr();
/** Gets the name of the function or method being invoked, if it can be determined. */
abstract string getCalleeName();
/** Gets the data flow node specifying the function to be called. */
abstract DataFlow::Node getCalleeNode();
/** Gets the data flow node corresponding to the `i`th argument of this invocation. */
abstract DataFlow::Node getArgument(int i);
/** Gets the data flow node corresponding to an argument of this invocation. */
abstract DataFlow::Node getAnArgument();
/**
* Gets a data flow node corresponding to an array of values being passed as
* individual arguments to this invocation.
*/
abstract DataFlow::Node getASpreadArgument();
/** Gets the number of arguments of this invocation, if it can be determined. */
abstract int getNumArgument();
}
/**
* A data flow node representing a function call without `new`, either explicitly or
* reflectively.
*/
abstract class CallNodeDef extends InvokeNodeDef {
/** Gets the data flow node corresponding to the receiver of this call, if any. */
DataFlow::Node getReceiver() { none() }
}
/**
* A data flow node representing a method call.
*/
abstract class MethodCallNodeDef extends CallNodeDef {
/** Gets the name of the method being invoked, if it can be determined. */
abstract string getMethodName();
}
/**
* A data flow node representing a function invocation with `new`.
*/
abstract class NewNodeDef extends InvokeNodeDef { }
/**
* A data flow node representing an explicit (that is, non-reflective) function invocation.
*/
class ExplicitInvokeNode extends InvokeNodeDef, DataFlow::ValueNode {
override InvokeExpr astNode;
override InvokeExpr getInvokeExpr() { result = astNode }
override string getCalleeName() { result = astNode.getCalleeName() }
override DataFlow::Node getCalleeNode() { result = DataFlow::valueNode(astNode.getCallee()) }
/**
* Whether i is an index that occurs after a spread argument.
*/
pragma[nomagic]
private predicate isIndexAfterSpread(int i) {
astNode.isSpreadArgument(i)
or
exists(astNode.getArgument(i)) and
this.isIndexAfterSpread(i - 1)
}
override DataFlow::Node getArgument(int i) {
not this.isIndexAfterSpread(i) and
result = DataFlow::valueNode(astNode.getArgument(i))
}
override DataFlow::Node getAnArgument() {
exists(Expr arg | arg = astNode.getAnArgument() |
not arg instanceof SpreadElement and
result = DataFlow::valueNode(arg)
)
}
override DataFlow::Node getASpreadArgument() {
exists(SpreadElement arg | arg = astNode.getAnArgument() |
result = DataFlow::valueNode(arg.getOperand())
)
}
override int getNumArgument() {
not astNode.isSpreadArgument(_) and result = astNode.getNumArgument()
}
}
/**
* A data flow node representing an explicit (that is, non-reflective) function call.
*/
class ExplicitCallNode extends CallNodeDef, ExplicitInvokeNode {
override CallExpr astNode;
}
/**
* A data flow node representing an explicit (that is, non-reflective) method call.
*/
private class ExplicitMethodCallNode extends MethodCallNodeDef, ExplicitCallNode {
override MethodCallExpr astNode;
override DataFlow::Node getReceiver() { result = DataFlow::valueNode(astNode.getReceiver()) }
override string getMethodName() { result = astNode.getMethodName() }
}
/**
* A data flow node representing a `new` expression.
*/
private class ExplicitNewNode extends NewNodeDef, ExplicitInvokeNode {
override NewExpr astNode;
}
/**
* A data flow node representing a reflective function call.
*/
private class ReflectiveCallNodeDef extends CallNodeDef {
ExplicitMethodCallNode originalCall;
string kind;
ReflectiveCallNodeDef() { this = TReflectiveCallNode(originalCall.asExpr(), kind) }
override InvokeExpr getInvokeExpr() { result = originalCall.getInvokeExpr() }
override string getCalleeName() {
result = originalCall.getReceiver().asExpr().(PropAccess).getPropertyName()
}
override DataFlow::Node getCalleeNode() { result = originalCall.getReceiver() }
override DataFlow::Node getReceiver() { result = originalCall.getArgument(0) }
override DataFlow::Node getArgument(int i) {
i >= 0 and kind = "call" and result = originalCall.getArgument(i + 1)
}
override DataFlow::Node getAnArgument() {
kind = "call" and result = originalCall.getAnArgument() and result != this.getReceiver()
}
override DataFlow::Node getASpreadArgument() {
kind = "apply" and
result = originalCall.getArgument(1)
or
kind = "call" and
result = originalCall.getASpreadArgument()
}
override int getNumArgument() {
result >= 0 and kind = "call" and result = originalCall.getNumArgument() - 1
}
}
}
/**
* An array element viewed as a property write; for instance, in
* `var arr = ["first", , "third"]`, `"first"` is a write of property 0 of `arr`
* and `"third"` is a write of property 2 of `arr`.
*
* Note: We currently do not expose the array index as the property name,
* instead treating it as a write of an unknown property.
*/
private class ElementNodeAsPropWrite extends PropWrite, ElementNode {
override Expr getPropertyNameExpr() { none() }
override string getPropertyName() { none() }
override Node getRhs() { result = valueNode(elt) }
override Node getBase() { result = valueNode(arr) }
override ControlFlowNode getWriteNode() { result = arr }
}
/**
* A data flow node representing `this` in a function or top-level.
*/
private class ThisNodeInternal extends Node, TThisNode {
override string toString() { result = "this" }
override BasicBlock getBasicBlock() {
exists(StmtContainer container | this = TThisNode(container) | result = container.getEntry())
}
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
// Use the function entry as the location
exists(StmtContainer container | this = TThisNode(container) |
container
.getEntry()
.getLocation()
.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
)
}
override File getFile() {
exists(StmtContainer container | this = TThisNode(container) | result = container.getFile())
}
}
/**
* A data flow node representing a captured variable.
*/
private class CapturedVariableNode extends Node, TCapturedVariableNode {
LocalVariable variable;
CapturedVariableNode() { this = TCapturedVariableNode(variable) }
override BasicBlock getBasicBlock() { result = variable.getDeclaringContainer().getStartBB() }
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
variable.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
override string toString() { result = variable.getName() }
}
/** A data flow node representing the value plugged into a template tag. */
class TemplatePlaceholderTagNode extends Node, TTemplatePlaceholderTag {
/** Gets the template tag represented by this data flow node. */
Templating::TemplatePlaceholderTag getTag() { this = TTemplatePlaceholderTag(result) }
override BasicBlock getBasicBlock() { none() }
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
this.getTag()
.getLocation()
.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
override string toString() { result = this.getTag().toString() }
}
/**
* INTERNAL. DO NOT USE.
*
* Gets a data flow node representing the given captured variable.
*/
Node capturedVariableNode(LocalVariable variable) { result = TCapturedVariableNode(variable) }
/**
* Gets the data flow node corresponding to `nd`.
*
* This predicate is only defined for expressions, properties, and for statements that declare
* a function, a class, or a TypeScript namespace or enum.
*/
ValueNode valueNode(ASTNode nd) { result.getAstNode() = nd }
/**
* Gets the data flow node corresponding to `e`.
*/
pragma[inline]
ExprNode exprNode(Expr e) { result = valueNode(e) }
/** Gets the data flow node corresponding to `ssa`. */
SsaDefinitionNode ssaDefinitionNode(SsaDefinition ssa) { result = TSsaDefNode(ssa) }
/** Gets the node corresponding to the initialization of parameter `p`. */
ParameterNode parameterNode(Parameter p) { result.getParameter() = p }
/**
* INTERNAL: Use `parameterNode(Parameter)` instead.
*/
predicate parameterNode(DataFlow::Node nd, Parameter p) { nd = valueNode(p) }
/**
* INTERNAL: Use `thisNode(StmtContainer container)` instead.
*/
predicate thisNode(DataFlow::Node node, StmtContainer container) { node = TThisNode(container) }
/**
* Gets the node representing the receiver of the given function, or `this` in the given top-level.
*
* Has no result if `container` is an arrow function.
*/
DataFlow::ThisNode thisNode(StmtContainer container) { result = TThisNode(container) }
/**
* INTERNAL. DO NOT USE.
*
* Gets the data flow node holding the reference to the module being destructured at
* the given import declaration.
*/
DataFlow::Node destructuredModuleImportNode(ImportDeclaration imprt) {
result = TDestructuredModuleImportNode(imprt)
}
/**
* INTERNAL: Use `ExceptionalInvocationReturnNode` instead.
*/
predicate exceptionalInvocationReturnNode(DataFlow::Node nd, InvokeExpr invocation) {
nd = TExceptionalInvocationReturnNode(invocation)
}
/**
* INTERNAL: Use `ExceptionalFunctionReturnNode` instead.
*/
predicate exceptionalFunctionReturnNode(DataFlow::Node nd, Function function) {
nd = TExceptionalFunctionReturnNode(function)
}
/**
* INTERNAL: Use `FunctionReturnNode` instead.
*/
predicate functionReturnNode(DataFlow::Node nd, Function function) {
nd = TFunctionReturnNode(function)
}
/**
* INTERNAL: Do not use outside standard library.
*
* Gets a data flow node unique to the given field declaration.
*
* Note that this node defaults to being disconnected from the data flow
* graph, as the individual property reads and writes affecting the field are
* analyzed independently of the field declaration.
*
* Certain framework models may need this node to model the behavior of
* class and field decorators.
*/
DataFlow::Node fieldDeclarationNode(FieldDeclaration field) { result = TPropNode(field) }
/**
* Gets the data flow node corresponding the given l-value expression, if
* such a node exists.
*
* This differs from `DataFlow::valueNode()`, which represents the value
* _before_ the l-value is assigned to, whereas `DataFlow::lvalueNode()`
* represents the value _after_ the assignment.
*/
Node lvalueNode(BindingPattern lvalue) {
exists(SsaExplicitDefinition ssa |
ssa.defines(lvalue.(LValue).getDefNode(), lvalue.(VarRef).getVariable()) and
result = TSsaDefNode(ssa)
)
or
result = TValueNode(lvalue.(DestructuringPattern))
}
/**
* A classification of flows that are not modeled, or only modeled incompletely, by
* `DataFlowNode`:
*
* - `"await"`: missing flow through promises;
* - `"call"`: missing inter-procedural data flow;
* - `"eval"`: missing reflective data flow;
* - `"global"`: missing ata flow through global variables;
* - `"heap"`: missing data flow through properties;
* - `"import"`: missing data flow through module imports;
* - `"namespace"`: missing data flow through exported namespace members;
* - `"yield"`: missing data flow through generators.
*/
class Incompleteness extends string {
Incompleteness() {
this = ["await", "call", "eval", "global", "heap", "import", "namespace", "yield"]
}
}
/**
* Holds if `call` may call `callee`, and this call should be
* tracked by local data flow.
*/
private predicate localCall(InvokeExpr call, Function callee) {
call = callee.(ImmediatelyInvokedFunctionExpr).getInvocation()
}
/**
* Holds if some call passes `arg` as the value of `parm`, and this
* call should be tracked by local data flow.
*/
private predicate localArgumentPassing(Expr arg, Parameter parm) {
any(ImmediatelyInvokedFunctionExpr iife).argumentPassing(parm, arg)
}
/**
* Holds if there is a step from `pred -> succ` due to an assignment
* to an expression in l-value position.
*/
private predicate lvalueFlowStep(Node pred, Node succ) {
exists(VarDef def |
pred = valueNode(defSourceNode(def)) and
succ = lvalueNode(def.getTarget())
)
or
exists(SimpleParameter param |
pred = valueNode(param) and // The value node represents the incoming argument
succ = lvalueNode(param) // The SSA node represents the parameters's local variable
)
or
exists(Expr arg, Parameter param |
localArgumentPassing(arg, param) and
pred = valueNode(arg) and
succ = valueNode(param)
)
or
exists(PropertyPattern pattern |
pred = TPropNode(pattern) and
succ = lvalueNode(pattern.getValuePattern())
)
or
exists(Expr element |
pred = TElementPatternNode(_, element) and
succ = lvalueNode(element)
)
}
/**
* Holds if there is a step from `pred -> succ` from the default
* value of a destructuring pattern or parameter.
*/
private predicate lvalueDefaultFlowStep(Node pred, Node succ) {
exists(PropertyPattern pattern |
pred = TValueNode(pattern.getDefault()) and
succ = lvalueNode(pattern.getValuePattern())
)
or
exists(ArrayPattern array, int i |
pred = TValueNode(array.getDefault(i)) and
succ = lvalueNode(array.getElement(i))
)
or
exists(Parameter param |
pred = TValueNode(param.getDefault()) and
parameterNode(succ, param)
)
}
/**
* Flow steps shared between `getImmediatePredecessor` and `localFlowStep`.
*
* Inlining is forced because the two relations are indexed differently.
*/
pragma[inline]
private predicate immediateFlowStep(Node pred, Node succ) {
exists(SsaVariable v |
pred = TSsaDefNode(v.getDefinition()) and
succ = valueNode(v.getAUse())
)
or
exists(Expr predExpr, Expr succExpr |
pred = valueNode(predExpr) and succ = valueNode(succExpr)
|
predExpr = succExpr.(ParExpr).getExpression()
or
predExpr = succExpr.(SeqExpr).getLastOperand()
or
predExpr = succExpr.(AssignExpr).getRhs()
or
predExpr = succExpr.(TypeAssertion).getExpression()
or
predExpr = succExpr.(NonNullAssertion).getExpression()
or
predExpr = succExpr.(ExpressionWithTypeArguments).getExpression()
or
(
succExpr instanceof AssignLogOrExpr or
succExpr instanceof AssignLogAndExpr or
succExpr instanceof AssignNullishCoalescingExpr
) and
(
predExpr = succExpr.(CompoundAssignExpr).getLhs() or
predExpr = succExpr.(CompoundAssignExpr).getRhs()
)
)
or
// flow from 'this' parameter into 'this' expressions
exists(ThisExpr thiz |
pred = TThisNode(thiz.getBindingContainer()) and
succ = valueNode(thiz)
)
or
// `f.call(...)` and `f.apply(...)` evaluate to the result of the reflective call they perform
pred = TReflectiveCallNode(succ.asExpr(), _)
}
/**
* Holds if data can flow from `pred` to `succ` in one local step.
*/
cached
predicate localFlowStep(Node pred, Node succ) {
Stages::DataFlowStage::ref() and
// flow from RHS into LHS
lvalueFlowStep(pred, succ)
or
lvalueDefaultFlowStep(pred, succ)
or
immediateFlowStep(pred, succ)
or
// From an assignment or implicit initialization of a captured variable to its flow-insensitive node.
exists(SsaDefinition predDef |
pred = TSsaDefNode(predDef) and
succ = TCapturedVariableNode(predDef.getSourceVariable())
|
predDef instanceof SsaExplicitDefinition or
predDef instanceof SsaImplicitInit
)
or
// From a captured variable node to its flow-sensitive capture nodes
exists(SsaVariableCapture ssaCapture |
pred = TCapturedVariableNode(ssaCapture.getSourceVariable()) and
succ = TSsaDefNode(ssaCapture)
)
or
// Flow through implicit SSA nodes
exists(SsaImplicitDefinition ssa | succ = TSsaDefNode(ssa) |
// from the inputs of phi and pi nodes into the node itself
pred = TSsaDefNode(ssa.(SsaPseudoDefinition).getAnInput().getDefinition())
)
or
exists(Expr predExpr, Expr succExpr |
pred = valueNode(predExpr) and succ = valueNode(succExpr)
|
predExpr = succExpr.(LogicalBinaryExpr).getAnOperand()
or
predExpr = succExpr.(ConditionalExpr).getABranch()
or
exists(Function f |
predExpr = f.getAReturnedExpr() and
localCall(succExpr, f)
)
)
or
// from returned expr to the FunctionReturnNode.
exists(Function f | not f.isAsyncOrGenerator() |
DataFlow::functionReturnNode(succ, f) and pred = valueNode(f.getAReturnedExpr())
)
}
/**
* Holds if there is a step from `pred` to `succ` through a field accessed through `this` in a class.
*/
predicate localFieldStep(DataFlow::Node pred, DataFlow::Node succ) {
exists(ClassNode cls, string prop |
pred = cls.getADirectSuperClass*().getAReceiverNode().getAPropertyWrite(prop).getRhs() or
pred = cls.getInstanceMethod(prop)
|
succ = cls.getAReceiverNode().getAPropertyRead(prop)
)
}
predicate argumentPassingStep = FlowSteps::argumentPassing/4;
/**
* Gets the data flow node representing the source of definition `def`, taking
* flow through IIFE calls into account.
*/
private AST::ValueNode defSourceNode(VarDef def) {
result = def.getSource() or
result = def.getDestructuringSource()
}
/**
* Holds if the flow information for this node is incomplete.
*
* This predicate holds if there may be a source flow node from which data flows into
* this node, but that node is not a result of `getALocalSource()` due to analysis incompleteness.
* The parameter `cause` is bound to a string describing the source of incompleteness.
*
* For example, since this analysis is intra-procedural, data flow from actual arguments
* to formal parameters is not modeled. Hence, if `p` is an access to a parameter,
* `p.getALocalSource()` does _not_ return the corresponding argument, and
* `p.isIncomplete("call")` holds.
*/
predicate isIncomplete(Node nd, Incompleteness cause) {
exists(SsaVariable ssa | nd = TSsaDefNode(ssa.getDefinition()) |
defIsIncomplete(ssa.(SsaExplicitDefinition).getDef(), cause)
or
exists(Variable v | v = ssa.getSourceVariable() |
v.isNamespaceExport() and cause = "namespace"
or
any(DirectEval e).mayAffect(v) and cause = "eval"
)
)
or
exists(GlobalVarAccess va |
nd = valueNode(va.(VarUse)) and
if Closure::isClosureNamespace(va.getName()) then cause = "heap" else cause = "global"
)
or
exists(Expr e | e = nd.asExpr() and cause = "call" |
e instanceof InvokeExpr and
not localCall(e, _)
or
e instanceof ThisExpr
or
e instanceof SuperExpr
or
e instanceof NewTargetExpr
or
e instanceof ImportMetaExpr
or
e instanceof FunctionBindExpr
or
e instanceof TaggedTemplateExpr
or
e instanceof Parameter and
not localArgumentPassing(_, e) and
not isAnalyzedParameter(e) and
not e.(Parameter).isRestParameter()
)
or
nd.(AnalyzedNode).hasAdditionalIncompleteness(cause)
or
nd.asExpr() instanceof ExternalModuleReference and
cause = "import"
or
exists(Expr e | e = nd.asExpr() and cause = "heap" |
e instanceof PropAccess or
e instanceof E4X::XMLAnyName or
e instanceof E4X::XMLAttributeSelector or
e instanceof E4X::XMLDotDotExpression or
e instanceof E4X::XMLFilterExpression or
e instanceof E4X::XMLQualifiedIdentifier or
e instanceof Angular2::PipeRefExpr
)
or
exists(Expr e | e = nd.asExpr() |
(e instanceof YieldExpr or e instanceof FunctionSentExpr) and
cause = "yield"
or
(e instanceof AwaitExpr or e instanceof DynamicImportExpr) and
cause = "await"
or
e instanceof GeneratedCodeExpr and
cause = "eval" // we use 'eval' here to represent code generation more broadly
)
or
nd instanceof TExceptionalInvocationReturnNode and cause = "call"
or
nd instanceof TExceptionalFunctionReturnNode and cause = "call"
or
exists(PropertyPattern p | nd = TPropNode(p)) and cause = "heap"
or
nd instanceof TElementPatternNode and cause = "heap"
}
/**
* Holds if definition `def` cannot be completely analyzed due to `cause`.
*/
private predicate defIsIncomplete(VarDef def, Incompleteness cause) {
def instanceof ImportSpecifier and
cause = "import"
or
exists(EnhancedForLoop efl | def = efl.getIteratorExpr()) and
cause = "heap"
or
exists(ComprehensionBlock cb | def = cb.getIterator()) and
cause = "yield"
}
import Nodes
import Sources
import TypeInference
import Configuration
import TrackedNodes
import TypeTracking
import internal.FunctionWrapperSteps
predicate localTaintStep = TaintTracking::localTaintStep/2;
}