JS: Add PostUpdateNode

This commit is contained in:
Asger F
2023-10-10 10:25:26 +02:00
parent 01952f17bf
commit c839822eb9
3 changed files with 106 additions and 0 deletions

View File

@@ -25,6 +25,7 @@ private import internal.DataFlowNode
private import internal.AnalyzedParameters
private import internal.PreCallGraphStep
private import semmle.javascript.internal.CachedStages
private import semmle.javascript.dataflow.internal.DataFlowPrivate as Private
module DataFlow {
/**
@@ -247,6 +248,11 @@ module DataFlow {
or
this.getFallbackTypeAnnotation().getAnUnderlyingType().hasQualifiedName(moduleName, typeName)
}
/**
* Gets the post-update node corresponding to this node, if any.
*/
final PostUpdateNode getPostUpdateNode() { result.getPreUpdateNode() = this }
}
/**
@@ -1087,6 +1093,28 @@ module DataFlow {
expr.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
}
/**
* A node representing the post-update node corresponding to implicit uses of `this` in a constructor.
*/
private class ConstructorThisPostUpdateNode extends TConstructorThisPostUpdate, DataFlow::Node {
private Function constructor;
ConstructorThisPostUpdateNode() { this = TConstructorThisPostUpdate(constructor) }
override string toString() { result = "[post-update] 'this' parameter of " + constructor }
override StmtContainer getContainer() { result = constructor }
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
constructor
.getLocation()
.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
}
/**
* INTERNAL. DO NOT USE.
*
@@ -1404,6 +1432,43 @@ module DataFlow {
override StmtContainer getContainer() { result = this.getTag().getInnerTopLevel() }
}
/**
* A post-update node whose pre-node corresponds to an expression. See `DataFlow::PostUpdateNode` for more details.
*/
class ExprPostUpdateNode extends DataFlow::Node, TExprPostUpdateNode, Private::PostUpdateNode {
private AST::ValueNode expr;
ExprPostUpdateNode() { this = TExprPostUpdateNode(expr) }
/** Gets the expression for which this is the post-update node. */
AST::ValueNode getExpr() { result = expr }
override StmtContainer getContainer() { result = expr.getContainer() }
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
expr.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
override string toString() { result = "[post update] " + expr.toString() }
}
/**
* A post-update node.
*
* This is a data-flow node that represents the new state of an object after its contents have been mutated.
* Most notably such nodes exist for arguments to a call and for the base of a property reference.
*/
class PostUpdateNode extends DataFlow::Node {
PostUpdateNode() { Private::postUpdatePair(_, this) }
/**
* Gets the corresponding pre-update node, which is usually the argument to a call or the base of a property reference.
*/
final DataFlow::Node getPreUpdateNode() { Private::postUpdatePair(result, this) }
}
/**
* INTERNAL. DO NOT USE.
*

View File

@@ -34,7 +34,19 @@ private module Cached {
TGlobalAccessPathRoot() or
TTemplatePlaceholderTag(Templating::TemplatePlaceholderTag tag) or
TReflectiveParametersNode(Function f) or
TExprPostUpdateNode(AST::ValueNode e) {
e = any(InvokeExpr invoke).getAnArgument() or
e = any(PropAccess access).getBase() or
e = any(DestructuringPattern pattern) or
e = any(InvokeExpr invoke).getCallee() or
// We have read steps out of the await operand, so it technically needs a post-update
e = any(AwaitExpr a).getOperand() or
e = any(Function f) or // functions are passed as their own self-reference argument
// RHS of a setter call is an argument, so it needs a post-update node
e = any(Assignment asn | asn.getTarget() instanceof PropAccess).getRhs()
} or
TConstructorThisArgumentNode(InvokeExpr e) { e instanceof NewExpr or e instanceof SuperCall } or
TConstructorThisPostUpdate(Constructor ctor) or
}
import Cached

View File

@@ -0,0 +1,29 @@
private import javascript
private import semmle.javascript.dataflow.internal.DataFlowNode
class Node = DataFlow::Node;
class PostUpdateNode = DataFlow::PostUpdateNode;
cached
predicate postUpdatePair(Node pre, Node post) {
exists(AST::ValueNode expr |
pre = TValueNode(expr) and
post = TExprPostUpdateNode(expr)
)
or
exists(NewExpr expr |
pre = TConstructorThisArgumentNode(expr) and
post = TValueNode(expr)
)
or
exists(SuperCall expr |
pre = TConstructorThisArgumentNode(expr) and
post = TConstructorThisPostUpdate(expr.getBinder())
)
or
exists(Function constructor |
pre = TThisNode(constructor) and
post = TConstructorThisPostUpdate(constructor)
)
}