JavaScript: Lift call graph library to data flow graph.

This commit is contained in:
Max Schaefer
2018-07-09 09:07:42 +01:00
parent d91218e248
commit 9ba3d80bad
53 changed files with 761 additions and 115 deletions

View File

@@ -50,3 +50,4 @@
* HTTP header names are now always normalized to lower case to reflect the fact that they are case insensitive. In particular, the result of `HeaderDefinition.getAHeaderName`, and the first parameter of `HeaderDefinition.defines`, `ExplicitHeaderDefinition.definesExplicitly` and `RouteHandler.getAResponseHeader` is now always a lower-case string. * HTTP header names are now always normalized to lower case to reflect the fact that they are case insensitive. In particular, the result of `HeaderDefinition.getAHeaderName`, and the first parameter of `HeaderDefinition.defines`, `ExplicitHeaderDefinition.definesExplicitly` and `RouteHandler.getAResponseHeader` is now always a lower-case string.
* The class `JsonParseCall` has been deprecated. Use `JsonParserCall` instead. * The class `JsonParseCall` has been deprecated. Use `JsonParserCall` instead.
* The handling of spread arguments in the data flow library has been changed: `DataFlow::InvokeNode.getArgument(i)` is now only defined when there is no spread argument at or before argument position `i`, and similarly `InvokeNode.getNumArgument` is only defined for invocations without spread arguments.

View File

@@ -121,8 +121,7 @@ predicate noSideEffects(Expr e) {
e.isPure() e.isPure()
or or
// `new Error(...)`, `new SyntaxError(...)`, etc. // `new Error(...)`, `new SyntaxError(...)`, etc.
e instanceof NewExpr and forex (Function f | f = e.flow().(DataFlow::NewNode).getACallee() |
forex (Function f | f = e.(CallSite).getACallee() |
f.(ExternalType).getASupertype*().getName() = "Error" f.(ExternalType).getASupertype*().getName() = "Error"
) )
} }

View File

@@ -16,13 +16,13 @@ import javascript
/** /**
* Holds if call site `cs` may invoke function `callee` as specified by `how`. * Holds if call site `cs` may invoke function `callee` as specified by `how`.
*/ */
predicate calls(CallSite cs, Function callee, string how) { predicate calls(DataFlow::InvokeNode cs, Function callee, string how) {
callee = cs.getACallee() and callee = cs.getACallee() and
( (
cs instanceof CallExpr and not cs instanceof SuperCall and cs instanceof DataFlow::CallNode and not cs.asExpr() instanceof SuperCall and
how = "as a function" how = "as a function"
or or
cs instanceof NewExpr and cs instanceof DataFlow::NewNode and
how = "using 'new'" how = "using 'new'"
) )
} }
@@ -31,7 +31,7 @@ predicate calls(CallSite cs, Function callee, string how) {
* Holds if call site `cs` may illegally invoke function `callee` as specified by `how`; * Holds if call site `cs` may illegally invoke function `callee` as specified by `how`;
* `calleeDesc` describes what kind of function `callee` is. * `calleeDesc` describes what kind of function `callee` is.
*/ */
predicate illegalInvocation(CallSite cs, Function callee, string calleeDesc, string how) { predicate illegalInvocation(DataFlow::InvokeNode cs, Function callee, string calleeDesc, string how) {
calls(cs, callee, how) and calls(cs, callee, how) and
( (
how = "as a function" and how = "as a function" and
@@ -44,15 +44,16 @@ predicate illegalInvocation(CallSite cs, Function callee, string calleeDesc, str
} }
/** /**
* Holds if `ce` has at least one call target that isn't a constructor. * Holds if `ce` is a call with at least one call target that isn't a constructor.
*/ */
predicate isCallToFunction(CallExpr ce) { predicate isCallToFunction(DataFlow::InvokeNode ce) {
exists (Function f | f = ce.(CallSite).getACallee() | ce instanceof DataFlow::CallNode and
exists (Function f | f = ce.getACallee() |
not f instanceof Constructor not f instanceof Constructor
) )
} }
from CallSite cs, Function callee, string calleeDesc, string how from DataFlow::InvokeNode cs, Function callee, string calleeDesc, string how
where illegalInvocation(cs, callee, calleeDesc, how) and where illegalInvocation(cs, callee, calleeDesc, how) and
// filter out some easy cases // filter out some easy cases
not isCallToFunction(cs) and not isCallToFunction(cs) and

View File

@@ -22,8 +22,8 @@ import semmle.javascript.RestrictedLocations
* have to call itself using `new`, so that is what we look for. * have to call itself using `new`, so that is what we look for.
*/ */
predicate guardsAgainstMissingNew(Function f) { predicate guardsAgainstMissingNew(Function f) {
exists (CallSite new | exists (DataFlow::NewNode new |
new.(NewExpr).getEnclosingFunction() = f and new.asExpr().getEnclosingFunction() = f and
f = new.getACallee() f = new.getACallee()
) )
} }
@@ -34,13 +34,13 @@ predicate guardsAgainstMissingNew(Function f) {
* is only suggested as a potential callee due to imprecise analysis of global * is only suggested as a potential callee due to imprecise analysis of global
* variables and is not, in fact, a viable callee at all. * variables and is not, in fact, a viable callee at all.
*/ */
predicate calls(CallSite cs, Function callee, int imprecision) { predicate calls(DataFlow::InvokeNode cs, Function callee, int imprecision) {
callee = cs.getACallee() and callee = cs.getACallee() and
( (
// if global flow was used to derive the callee, we may be imprecise // if global flow was used to derive the callee, we may be imprecise
if cs.isIndefinite("global") then if cs.isIndefinite("global") then
// callees within the same file are probably genuine // callees within the same file are probably genuine
callee.getFile() = cs.(Locatable).getFile() and imprecision = 0 callee.getFile() = cs.getFile() and imprecision = 0
or or
// calls to global functions declared in an externs file are fairly // calls to global functions declared in an externs file are fairly
// safe as well // safe as well
@@ -58,11 +58,11 @@ predicate calls(CallSite cs, Function callee, int imprecision) {
* Gets a function that may be invoked at `cs`, preferring callees that * Gets a function that may be invoked at `cs`, preferring callees that
* are less likely to be derived due to analysis imprecision. * are less likely to be derived due to analysis imprecision.
*/ */
Function getALikelyCallee(CallSite cs) { Function getALikelyCallee(DataFlow::InvokeNode cs) {
calls(cs, result, min(int p | calls(cs, _, p))) calls(cs, result, min(int p | calls(cs, _, p)))
} }
from Function f, NewExpr new, CallExpr call from Function f, DataFlow::NewNode new, DataFlow::CallNode call
where // externs are special, so don't flag them where // externs are special, so don't flag them
not f.inExternsFile() and not f.inExternsFile() and
// illegal constructor calls are flagged by query 'Illegal invocation', // illegal constructor calls are flagged by query 'Illegal invocation',
@@ -71,11 +71,11 @@ where // externs are special, so don't flag them
f = getALikelyCallee(new) and f = getALikelyCallee(new) and
f = getALikelyCallee(call) and f = getALikelyCallee(call) and
not guardsAgainstMissingNew(f) and not guardsAgainstMissingNew(f) and
not new.(CallSite).isUncertain() and not new.isUncertain() and
not call.(CallSite).isUncertain() and not call.isUncertain() and
// super constructor calls behave more like `new`, so don't flag them // super constructor calls behave more like `new`, so don't flag them
not call instanceof SuperCall and not call.asExpr() instanceof SuperCall and
// reflective calls provide an explicit receiver object, so don't flag them either // don't flag if there is a receiver object
not call instanceof ReflectiveCallSite not exists(call.getReceiver())
select (FirstLineOf)f, capitalize(f.describe()) + " is invoked as a constructor here $@, " + select (FirstLineOf)f, capitalize(f.describe()) + " is invoked as a constructor here $@, " +
"and as a normal function here $@.", new, new.toString(), call, call.toString() "and as a normal function here $@.", new, new.toString(), call, call.toString()

View File

@@ -29,23 +29,11 @@ predicate isFixedArity(Function fn) {
* *
* This is only defined if all potential callees have a fixed arity. * This is only defined if all potential callees have a fixed arity.
*/ */
int maxArity(CallSite invk) { int maxArity(DataFlow::InvokeNode invk) {
forall (Function callee | callee = invk.getACallee() | isFixedArity(callee)) and forall (Function callee | callee = invk.getACallee() | isFixedArity(callee)) and
result = max(invk.getACallee().getNumParameter()) result = max(invk.getACallee().getNumParameter())
} }
/**
* Holds if call site `invk` has more arguments than the maximum arity
* of any function that it may invoke, and the first of those
* arguments is `arg`.
*
* This predicate is only defined for call sites where callee information is complete.
*/
predicate firstSpuriousArgument(CallSite invk, Expr arg) {
arg = invk.getArgumentNode(maxArity(invk)).asExpr() and
not invk.isIncomplete()
}
/** /**
* A list of spurious arguments passed at a call site. * A list of spurious arguments passed at a call site.
* *
@@ -54,15 +42,18 @@ predicate firstSpuriousArgument(CallSite invk, Expr arg) {
* defined to cover all subsequent arguments as well. * defined to cover all subsequent arguments as well.
*/ */
class SpuriousArguments extends Expr { class SpuriousArguments extends Expr {
DataFlow::InvokeNode invk;
SpuriousArguments() { SpuriousArguments() {
firstSpuriousArgument(_, this) this = invk.getArgument(maxArity(invk)).asExpr() and
not invk.isIncomplete()
} }
/** /**
* Gets the call site at which the spurious arguments are passed. * Gets the call site at which the spurious arguments are passed.
*/ */
CallSite getCall() { DataFlow::InvokeNode getCall() {
firstSpuriousArgument(result, this) result = invk
} }
/** /**
@@ -71,7 +62,7 @@ class SpuriousArguments extends Expr {
* expected by any potential callee. * expected by any potential callee.
*/ */
int getCount() { int getCount() {
result = getCall().(InvokeExpr).getNumArgument() - maxArity(getCall()) result = count(int i | exists(invk.getArgument(i)) and i >= maxArity(getCall()))
} }
/** /**
@@ -82,9 +73,10 @@ class SpuriousArguments extends Expr {
* [LGTM locations](https://lgtm.com/help/ql/locations). * [LGTM locations](https://lgtm.com/help/ql/locations).
*/ */
predicate hasLocationInfo(string filepath, int startline, int startcolumn, int endline, int endcolumn) { predicate hasLocationInfo(string filepath, int startline, int startcolumn, int endline, int endcolumn) {
this.getLocation().hasLocationInfo(filepath, startline, startcolumn, _, _) and getLocation().hasLocationInfo(filepath, startline, startcolumn, _, _) and
exists (Expr lastArg | lastArg = getCall().(InvokeExpr).getLastArgument() | exists (DataFlow::Node lastArg |
lastArg.getLocation().hasLocationInfo(_, _, _, endline, endcolumn) lastArg = max(DataFlow::Node arg, int i | arg = invk.getArgument(i) | arg order by i) |
lastArg.hasLocationInfo(_, _, _, endline, endcolumn)
) )
} }
} }
@@ -92,4 +84,4 @@ class SpuriousArguments extends Expr {
from SpuriousArguments args, Function f, string arguments from SpuriousArguments args, Function f, string arguments
where f = args.getCall().getACallee() and where f = args.getCall().getACallee() and
if args.getCount() = 1 then arguments = "argument" else arguments = "arguments" if args.getCount() = 1 then arguments = "argument" else arguments = "arguments"
select args, "Superfluous " + arguments + " passed to $@.", f, f.describe() select args, "Superfluous " + arguments + " passed to $@.", f, f.describe()

View File

@@ -14,17 +14,17 @@ import javascript
/** /**
* A call that invokes a method on its own receiver. * A call that invokes a method on its own receiver.
*/ */
class CallOnSelf extends CallExpr { class CallOnSelf extends DataFlow::CallNode {
CallOnSelf() { CallOnSelf() {
exists (Function binder | exists (Function binder |
binder = getEnclosingFunction().getThisBinder() | binder = getEnclosingFunction().getThisBinder() |
exists (DataFlow::ThisNode thiz | exists (DataFlow::ThisNode thiz |
this = thiz.getAMethodCall(_).asExpr() and this = thiz.getAMethodCall(_) and
thiz.getBinder().getAstNode() = binder thiz.getBinder().getAstNode() = binder
) )
or or
this.(CallSite).getACallee().(ArrowFunctionExpr).getThisBinder() = binder this.getACallee().(ArrowFunctionExpr).getThisBinder() = binder
) )
} }
@@ -32,7 +32,7 @@ class CallOnSelf extends CallExpr {
* Gets a `CallOnSelf` in the callee of this call. * Gets a `CallOnSelf` in the callee of this call.
*/ */
CallOnSelf getACalleCallOnSelf() { CallOnSelf getACalleCallOnSelf() {
result.getEnclosingFunction() = this.(CallSite).getACallee() result.getEnclosingFunction() = this.getACallee()
} }
} }
@@ -55,15 +55,15 @@ class UnconditionalCallOnSelf extends CallOnSelf {
/** /**
* Holds if `call` is guaranteed to occur in its enclosing function, unless an exception occurs. * Holds if `call` is guaranteed to occur in its enclosing function, unless an exception occurs.
*/ */
predicate isUnconditionalCall(CallExpr call) { predicate isUnconditionalCall(DataFlow::CallNode call) {
exists (ReachableBasicBlock callBlock, ReachableBasicBlock entryBlock | exists (ReachableBasicBlock callBlock, ReachableBasicBlock entryBlock |
callBlock.postDominates(entryBlock) and callBlock.postDominates(entryBlock) and
callBlock.getANode() = call and callBlock = call.getBasicBlock() and
entryBlock = call.getEnclosingFunction().getEntryBB() entryBlock = call.getEnclosingFunction().getEntryBB()
) )
} }
predicate isStateUpdateMethodCall(MethodCallExpr mce) { predicate isStateUpdateMethodCall(DataFlow::MethodCallNode mce) {
exists (string updateMethodName | exists (string updateMethodName |
updateMethodName = "setState" or updateMethodName = "setState" or
updateMethodName = "replaceState" or updateMethodName = "replaceState" or
@@ -111,7 +111,8 @@ class StateUpdateVolatileMethod extends Function {
} }
from StateUpdateVolatileMethod root, CallOnSelf initCall, MethodCallExpr stateUpdate, string callDescription from StateUpdateVolatileMethod root, CallOnSelf initCall, DataFlow::MethodCallNode stateUpdate,
string callDescription
where initCall.getEnclosingFunction() = root and where initCall.getEnclosingFunction() = root and
stateUpdate = initCall.getACalleCallOnSelf*() and stateUpdate = initCall.getACalleCallOnSelf*() and
isStateUpdateMethodCall(stateUpdate) and isStateUpdateMethodCall(stateUpdate) and

View File

@@ -55,9 +55,9 @@ int numRet(Function f) {
*/ */
predicate isDualUseConstructor(Function f) { predicate isDualUseConstructor(Function f) {
numRet(f) = 1 and numRet(f) = 1 and
exists (ReturnStmt ret, NewExpr new | ret.getContainer() = f | exists (ReturnStmt ret, DataFlow::NewNode new | ret.getContainer() = f |
new = ret.getExpr().stripParens() and new.asExpr() = ret.getExpr().stripParens() and
new.(CallSite).getACallee() = f new.getACallee() = f
) )
} }

View File

@@ -9,7 +9,7 @@ class CallToObjectDefineProperty extends DataFlow::MethodCallNode {
CallToObjectDefineProperty() { CallToObjectDefineProperty() {
exists (GlobalVariable obj | exists (GlobalVariable obj |
obj.getName() = "Object" and obj.getName() = "Object" and
astNode.calls(obj.getAnAccess(), "defineProperty") calls(DataFlow::valueNode(obj.getAnAccess()), "defineProperty")
) )
} }

View File

@@ -6,11 +6,13 @@ import javascript
private import InferredTypes private import InferredTypes
/** /**
* DEPRECATED: Use `DataFlow::InvokeNode` instead.
*
* A function call or `new` expression, with information about its potential callees. * A function call or `new` expression, with information about its potential callees.
* *
* Both direct calls and reflective calls using `call` or `apply` are modelled. * Both direct calls and reflective calls using `call` or `apply` are modelled.
*/ */
class CallSite extends @invokeexpr { deprecated class CallSite extends @invokeexpr {
InvokeExpr invk; InvokeExpr invk;
CallSite() { invk = this } CallSite() { invk = this }
@@ -120,7 +122,7 @@ class CallSite extends @invokeexpr {
/** /**
* A reflective function call using `call` or `apply`. * A reflective function call using `call` or `apply`.
*/ */
class ReflectiveCallSite extends CallSite { deprecated class ReflectiveCallSite extends CallSite {
DataFlow::AnalyzedNode callee; DataFlow::AnalyzedNode callee;
string callMode; string callMode;

View File

@@ -407,7 +407,7 @@ private predicate callInputStep(Function f, DataFlow::Node invk,
or or
exists (SsaDefinition prevDef, SsaDefinition def | exists (SsaDefinition prevDef, SsaDefinition def |
pred = DataFlow::ssaDefinitionNode(prevDef) and pred = DataFlow::ssaDefinitionNode(prevDef) and
calls(invk.asExpr(), f) and captures(f, prevDef, def) and calls(invk, f) and captures(f, prevDef, def) and
succ = DataFlow::ssaDefinitionNode(def) succ = DataFlow::ssaDefinitionNode(def)
) )
) and ) and

View File

@@ -29,6 +29,9 @@ module DataFlow {
or TDestructuringPatternNode(DestructuringPattern dp) or TDestructuringPatternNode(DestructuringPattern dp)
or TElementPatternNode(ArrayPattern ap, Expr p) { p = ap.getElement(_) } or TElementPatternNode(ArrayPattern ap, Expr p) { p = ap.getElement(_) }
or TElementNode(ArrayExpr arr, Expr e) { e = arr.getAnElement() } or TElementNode(ArrayExpr arr, Expr e) { e = arr.getAnElement() }
or TReflectiveCallNode(MethodCallExpr ce, string kind) {
ce.getMethodName() = kind and (kind = "call" or kind = "apply")
}
/** /**
* A node in the data flow graph. * A node in the data flow graph.
@@ -366,6 +369,30 @@ module DataFlow {
} }
} }
/**
* 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"
}
}
/** /**
* A data flow node that reads or writes an object property or class member. * A data flow node that reads or writes an object property or class member.
* *
@@ -643,6 +670,146 @@ module DataFlow {
override string getPropertyName() { none() } override string getPropertyName() { none() }
} }
/**
* 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 behaviour 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 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 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 string getCalleeName() {
result = astNode.getCalleeName()
}
override DataFlow::Node getCalleeNode() {
result = DataFlow::valueNode(astNode.getCallee())
}
override DataFlow::Node getArgument(int i) {
not astNode.isSpreadArgument([0..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 int getNumArgument() {
not astNode.isSpreadArgument(_) and result = astNode.getNumArgument()
}
}
/**
* A data flow node representing an explicit (that is, non-reflective) function call.
*/
private 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 string getCalleeName() { none() }
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 != getReceiver()
}
override int getNumArgument() {
result >= 0 and kind = "call" and result = originalCall.getNumArgument()-1
}
}
}
/** /**
* An array element viewed as a property write; for instance, in * An array element viewed as a property write; for instance, in
* `var arr = ["first", , "third"]`, `"first"` is a write of property 0 of `arr` * `var arr = ["first", , "third"]`, `"first"` is a write of property 0 of `arr`

View File

@@ -20,32 +20,67 @@ class ParameterNode extends DataFlow::DefaultSourceNode {
} }
/** A data flow node corresponding to a function invocation (with or without `new`). */ /** A data flow node corresponding to a function invocation (with or without `new`). */
class InvokeNode extends DataFlow::ValueNode, DataFlow::DefaultSourceNode { class InvokeNode extends DataFlow::DefaultSourceNode {
override InvokeExpr astNode; DataFlow::Impl::InvokeNodeDef impl;
InvokeNode() { this = impl }
/** Gets the name of the function or method being invoked, if it can be determined. */ /** Gets the name of the function or method being invoked, if it can be determined. */
string getCalleeName() { string getCalleeName() {
result = astNode.getCalleeName() result = impl.getCalleeName()
}
/** DEPRECATED: Use `getCalleeNode()` instead. */
deprecated
DataFlow::Node getCallee() {
result = getCalleeNode()
} }
/** Gets the data flow node specifying the function to be called. */ /** Gets the data flow node specifying the function to be called. */
DataFlow::ValueNode getCallee() { DataFlow::Node getCalleeNode() {
result = DataFlow::valueNode(astNode.getCallee()) result = impl.getCalleeNode()
} }
/** Gets the data flow node corresponding to the `i`th argument of this invocation. */ /**
DataFlow::ValueNode getArgument(int i) { * Gets the data flow node corresponding to the `i`th argument of this invocation.
result = DataFlow::valueNode(astNode.getArgument(i)) *
* For direct calls, this is the `i`th argument to the call itself: for instance,
* for a call `f(x, y)`, the 0th argument node is `x` and the first argument node is `y`.
*
* For reflective calls using `call`, the 0th argument to the call denotes the
* receiver, so argument positions are shifted by one: for instance, for a call
* `f.call(x, y, z)`, the 0th argument node is `y` and the first argument node is `z`,
* while `x` is not an argument node at all.
*
* For reflective calls using `apply` we cannot, in general, tell which argument
* occurs at which position, so this predicate is not defined for such calls.
*
* Note that this predicate is not defined for arguments following a spread
* argument: for instance, for a call `f(x, ...y, z)`, the 0th argument node is `x`,
* but the position of `z` cannot be determined, hence there are no first and second
* argument nodes.
*/
DataFlow::Node getArgument(int i) {
result = impl.getArgument(i)
} }
/** Gets the data flow node corresponding to an argument of this invocation. */ /** Gets the data flow node corresponding to an argument of this invocation. */
DataFlow::ValueNode getAnArgument() { DataFlow::Node getAnArgument() {
result = getArgument(_) result = impl.getAnArgument()
} }
/** Gets the number of arguments of this invocation. */ /** Gets the data flow node corresponding to the last argument of this invocation. */
DataFlow::Node getLastArgument() {
result = getArgument(getNumArgument()-1)
}
/** Gets the number of arguments of this invocation, if it can be determined. */
int getNumArgument() { int getNumArgument() {
result = astNode.getNumArgument() result = impl.getNumArgument()
}
Function getEnclosingFunction() {
result = getBasicBlock().getContainer()
} }
/** Gets a function passed as the `i`th argument of this invocation. */ /** Gets a function passed as the `i`th argument of this invocation. */
@@ -63,16 +98,73 @@ class InvokeNode extends DataFlow::ValueNode, DataFlow::DefaultSourceNode {
obj.hasPropertyWrite(name, result) obj.hasPropertyWrite(name, result)
) )
} }
/** Gets an abstract value representing possible callees of this call site. */
cached AbstractValue getACalleeValue() {
result = impl.getCalleeNode().analyze().getAValue()
}
/** Gets a potential callee based on dataflow analysis results. */
private Function getACalleeFromDataflow() {
result = getACalleeValue().(AbstractCallable).getFunction()
}
/** Gets a potential callee of this call site. */
Function getACallee() {
result = getACalleeFromDataflow()
or
not exists(getACalleeFromDataflow()) and
result = impl.(DataFlow::Impl::ExplicitInvokeNode).asExpr().(InvokeExpr).getResolvedCallee()
}
/**
* Holds if the approximation of possible callees for this call site is
* affected by the given analysis incompleteness `cause`.
*/
predicate isIndefinite(DataFlow::Incompleteness cause) {
getACalleeValue().isIndefinite(cause)
}
/**
* Holds if our approximation of possible callees for this call site is
* likely to be imprecise.
*
* We currently track one specific source of imprecision: call
* resolution relies on flow through global variables, and the flow
* analysis finds possible callees that are not functions.
* This usually means that a global variable is used in multiple
* independent contexts, so tracking flow through it leads to
* imprecision.
*/
predicate isImprecise() {
isIndefinite("global") and
exists (DefiniteAbstractValue v | v = getACalleeValue() |
not v instanceof AbstractCallable
)
}
/**
* Holds if our approximation of possible callees for this call site is
* likely to be incomplete.
*/
predicate isIncomplete() {
// the flow analysis identifies a source of incompleteness other than
// global flow (which usually leads to imprecision rather than incompleteness)
any (DataFlow::Incompleteness cause | isIndefinite(cause)) != "global"
}
/**
* Holds if our approximation of possible callees for this call site is
* likely to be imprecise or incomplete.
*/
predicate isUncertain() {
isImprecise() or isIncomplete()
}
} }
/** A data flow node corresponding to a function call without `new`. */ /** A data flow node corresponding to a function call without `new`. */
class CallNode extends InvokeNode { class CallNode extends InvokeNode {
override CallExpr astNode; override DataFlow::Impl::CallNodeDef impl;
}
/** A data flow node corresponding to a method call. */
class MethodCallNode extends CallNode {
override MethodCallExpr astNode;
/** /**
* Gets the data flow node corresponding to the receiver expression of this method call. * Gets the data flow node corresponding to the receiver expression of this method call.
@@ -80,11 +172,16 @@ class MethodCallNode extends CallNode {
* For example, the receiver of `x.m()` is `x`. * For example, the receiver of `x.m()` is `x`.
*/ */
DataFlow::Node getReceiver() { DataFlow::Node getReceiver() {
result = DataFlow::valueNode(astNode.getReceiver()) result = impl.getReceiver()
} }
}
/** A data flow node corresponding to a method call. */
class MethodCallNode extends CallNode {
override DataFlow::Impl::MethodCallNodeDef impl;
/** Gets the name of the invoked method, if it can be determined. */ /** Gets the name of the invoked method, if it can be determined. */
string getMethodName() { result = astNode.getMethodName() } string getMethodName() { result = impl.getMethodName() }
/** /**
* Holds if this data flow node calls method `methodName` on receiver node `receiver`. * Holds if this data flow node calls method `methodName` on receiver node `receiver`.
@@ -98,7 +195,7 @@ class MethodCallNode extends CallNode {
/** A data flow node corresponding to a `new` expression. */ /** A data flow node corresponding to a `new` expression. */
class NewNode extends InvokeNode { class NewNode extends InvokeNode {
override NewExpr astNode; override DataFlow::Impl::NewNodeDef impl;
} }
/** A data flow node corresponding to a `this` expression. */ /** A data flow node corresponding to a `this` expression. */

View File

@@ -125,7 +125,7 @@ abstract class SourceNode extends DataFlow::Node {
*/ */
DataFlow::CallNode getAMethodCall(string methodName) { DataFlow::CallNode getAMethodCall(string methodName) {
exists (PropAccess pacc | exists (PropAccess pacc |
pacc = result.getCallee().asExpr().stripParens() and pacc = result.getCalleeNode().asExpr().stripParens() and
flowsToExpr(pacc.getBase()) and flowsToExpr(pacc.getBase()) and
pacc.getPropertyName() = methodName pacc.getPropertyName() = methodName
) )
@@ -142,7 +142,7 @@ abstract class SourceNode extends DataFlow::Node {
* Gets an invocation (with our without `new`) of this node. * Gets an invocation (with our without `new`) of this node.
*/ */
DataFlow::InvokeNode getAnInvocation() { DataFlow::InvokeNode getAnInvocation() {
flowsTo(result.getCallee()) flowsTo(result.getCalleeNode())
} }
/** /**
@@ -182,7 +182,6 @@ class DefaultSourceNode extends SourceNode {
astNode instanceof PropAccess or astNode instanceof PropAccess or
astNode instanceof Function or astNode instanceof Function or
astNode instanceof ClassDefinition or astNode instanceof ClassDefinition or
astNode instanceof InvokeExpr or
astNode instanceof ObjectExpr or astNode instanceof ObjectExpr or
astNode instanceof ArrayExpr or astNode instanceof ArrayExpr or
astNode instanceof JSXNode or astNode instanceof JSXNode or
@@ -197,5 +196,7 @@ class DefaultSourceNode extends SourceNode {
) )
or or
DataFlow::parameterNode(this, _) DataFlow::parameterNode(this, _)
or
this instanceof DataFlow::Impl::InvokeNodeDef
} }
} }

View File

@@ -123,7 +123,7 @@ private module NodeTracking {
or or
exists (SsaDefinition prevDef, SsaDefinition def | exists (SsaDefinition prevDef, SsaDefinition def |
pred = DataFlow::ssaDefinitionNode(prevDef) and pred = DataFlow::ssaDefinitionNode(prevDef) and
calls(invk.asExpr(), f) and captures(f, prevDef, def) and calls(invk, f) and captures(f, prevDef, def) and
succ = DataFlow::ssaDefinitionNode(def) succ = DataFlow::ssaDefinitionNode(def)
) )
) )

View File

@@ -20,13 +20,11 @@ predicate shouldTrackProperties(AbstractValue obj) {
/** /**
* Holds if `invk` may invoke `f`. * Holds if `invk` may invoke `f`.
*/ */
predicate calls(InvokeExpr invk, Function f) { predicate calls(DataFlow::InvokeNode invk, Function f) {
exists (CallSite cs | cs = invk | if invk.isIndefinite("global") then
if cs.isIndefinite("global") then (f = invk.getACallee() and f.getFile() = invk.getFile())
(f = cs.getACallee() and f.getFile() = invk.getFile()) else
else f = invk.getACallee()
f = cs.getACallee()
)
} }
/** /**
@@ -65,11 +63,11 @@ predicate localFlowStep(DataFlow::Node pred, DataFlow::Node succ,
* Holds if `arg` is passed as an argument into parameter `parm` * Holds if `arg` is passed as an argument into parameter `parm`
* through invocation `invk` of function `f`. * through invocation `invk` of function `f`.
*/ */
predicate argumentPassing(DataFlow::ValueNode invk, DataFlow::ValueNode arg, Function f, Parameter parm) { predicate argumentPassing(DataFlow::InvokeNode invk, DataFlow::ValueNode arg, Function f, Parameter parm) {
exists (int i, CallSite cs | calls(invk, f) and
cs = invk.asExpr() and calls(cs, f) and exists (int i |
f.getParameter(i) = parm and not parm.isRestParameter() and f.getParameter(i) = parm and not parm.isRestParameter() and
arg = cs.getArgumentNode(i) arg = invk.getArgument(i)
) )
} }
@@ -92,7 +90,7 @@ predicate callStep(DataFlow::Node pred, DataFlow::Node succ) {
predicate returnStep(DataFlow::Node pred, DataFlow::Node succ) { predicate returnStep(DataFlow::Node pred, DataFlow::Node succ) {
exists (Function f | exists (Function f |
returnExpr(f, pred, _) and returnExpr(f, pred, _) and
calls(succ.asExpr(), f) calls(succ, f)
) )
} }

View File

@@ -56,7 +56,7 @@ module ConnectExpressShared {
// heuristic: does not return anything (the server will not use the return value) // heuristic: does not return anything (the server will not use the return value)
exists(astNode.getAReturnStmt().getExpr()) or exists(astNode.getAReturnStmt().getExpr()) or
// heuristic: is not invoked (the server invokes this at a call site we cannot reason precisely about) // heuristic: is not invoked (the server invokes this at a call site we cannot reason precisely about)
exists(CallSite cs | cs.getACallee() = astNode) exists(DataFlow::InvokeNode cs | cs.getACallee() = astNode)
) )
) )
} }

View File

@@ -424,7 +424,7 @@ module NodeJSLib {
// heuristic: does not return anything (Node.js will not use the return value) // heuristic: does not return anything (Node.js will not use the return value)
exists(astNode.getAReturnStmt().getExpr()) or exists(astNode.getAReturnStmt().getExpr()) or
// heuristic: is not invoked (Node.js invokes this at a call site we cannot reason precisely about) // heuristic: is not invoked (Node.js invokes this at a call site we cannot reason precisely about)
exists(CallSite cs | cs.getACallee() = astNode) exists(DataFlow::InvokeNode cs | cs.getACallee() = astNode)
) )
) )
} }

View File

@@ -192,10 +192,8 @@ abstract class ReactComponent extends ASTNode {
* constructor of this component. * constructor of this component.
*/ */
DataFlow::SourceNode getACandidatePropsSource() { DataFlow::SourceNode getACandidatePropsSource() {
exists (DataFlow::InvokeNode call | result.flowsTo(getComponentCreatorSource().getAnInvocation().getArgument(0))
getComponentCreatorSource().flowsTo(call.getCallee()) and or
result.flowsTo(call.getArgument(0))
) or
result = getADefaultPropsSource() result = getADefaultPropsSource()
} }

View File

@@ -140,7 +140,7 @@ abstract class SensitiveAction extends DataFlow::Node { }
class AuthorizationCall extends SensitiveAction, DataFlow::CallNode { class AuthorizationCall extends SensitiveAction, DataFlow::CallNode {
AuthorizationCall() { AuthorizationCall() {
exists(string s | s = astNode.getCalleeName() | exists(string s | s = getCalleeName() |
// name contains `login` or `auth`, but not as part of `loginfo` or `unauth`; // name contains `login` or `auth`, but not as part of `loginfo` or `unauth`;
// also exclude `author` // also exclude `author`
s.regexpMatch("(?i).*(login(?!fo)|(?<!un)auth(?!or\\b)).*") and s.regexpMatch("(?i).*(login(?!fo)|(?<!un)auth(?!or\\b)).*") and

View File

@@ -1,4 +0,0 @@
import javascript
from CallSite c
select c, c.getACallee()

View File

@@ -26,4 +26,11 @@ class OtherSub extends PseudoClass {
super(); super();
console.log(this.x); console.log(this.x);
} }
} }
function sum(x, y, z) {
return x+y+z;
}
sum(...[1, 2, 3]);
sum(1, ...[2, 3]);
sum(1, ...[2], 3);

View File

@@ -9,10 +9,16 @@
| es2015.js:10:5:10:22 | arguments.callee() | es2015.js:8:2:12:1 | functio ... \\n };\\n} | | es2015.js:10:5:10:22 | arguments.callee() | es2015.js:8:2:12:1 | functio ... \\n };\\n} |
| es2015.js:16:5:16:11 | super() | es2015.js:2:14:4:3 | () {\\n ... ");\\n } | | es2015.js:16:5:16:11 | super() | es2015.js:2:14:4:3 | () {\\n ... ");\\n } |
| es2015.js:26:5:26:11 | super() | es2015.js:20:1:22:1 | functio ... = 42;\\n} | | es2015.js:26:5:26:11 | super() | es2015.js:20:1:22:1 | functio ... = 42;\\n} |
| es2015.js:34:1:34:17 | sum(...[1, 2, 3]) | es2015.js:31:1:33:1 | functio ... +y+z;\\n} |
| es2015.js:35:1:35:17 | sum(1, ...[2, 3]) | es2015.js:31:1:33:1 | functio ... +y+z;\\n} |
| es2015.js:36:1:36:17 | sum(1, ...[2], 3) | es2015.js:31:1:33:1 | functio ... +y+z;\\n} |
| m.js:2:1:2:11 | exports.f() | m.js:1:13:1:25 | function() {} | | m.js:2:1:2:11 | exports.f() | m.js:1:13:1:25 | function() {} |
| m.js:3:1:3:18 | module.exports.f() | m.js:1:13:1:25 | function() {} | | m.js:3:1:3:18 | module.exports.f() | m.js:1:13:1:25 | function() {} |
| n.js:2:1:2:5 | m.f() | m.js:1:13:1:25 | function() {} | | n.js:2:1:2:5 | m.f() | m.js:1:13:1:25 | function() {} |
| n.js:5:1:5:6 | m2.f() | m2.js:2:6:2:18 | function() {} | | n.js:5:1:5:6 | m2.f() | m2.js:2:6:2:18 | function() {} |
| reflection.js:7:1:7:22 | reflective call | reflection.js:1:1:3:1 | functio ... x+y;\\n} |
| reflection.js:8:1:8:25 | add.app ... 3, 19]) | reflection.js:5:15:5:39 | functio ... n 56; } |
| reflection.js:8:1:8:25 | reflective call | reflection.js:1:1:3:1 | functio ... x+y;\\n} |
| tst.js:6:1:6:3 | f() | tst.js:1:1:1:15 | function f() {} | | tst.js:6:1:6:3 | f() | tst.js:1:1:1:15 | function f() {} |
| tst.js:7:1:7:3 | g() | tst.js:2:9:2:21 | function() {} | | tst.js:7:1:7:3 | g() | tst.js:2:9:2:21 | function() {} |
| tst.js:8:1:8:3 | h() | tst.js:3:5:3:17 | function() {} | | tst.js:8:1:8:3 | h() | tst.js:3:5:3:17 | function() {} |
@@ -38,3 +44,5 @@
| tst.js:60:15:60:21 | new A() | tst.js:44:1:44:15 | function A() {} | | tst.js:60:15:60:21 | new A() | tst.js:44:1:44:15 | function A() {} |
| tst.js:64:13:64:19 | new B() | tst.js:50:1:50:15 | function B() {} | | tst.js:64:13:64:19 | new B() | tst.js:50:1:50:15 | function B() {} |
| tst.js:66:5:66:9 | b.f() | tst.js:65:11:65:23 | function() {} | | tst.js:66:5:66:9 | b.f() | tst.js:65:11:65:23 | function() {} |
| tst.js:69:1:69:10 | globalfn() | tst3.js:1:1:1:22 | functio ... fn() {} |
| tst.js:70:1:70:11 | globalfn2() | tst3.js:2:1:2:23 | functio ... n2() {} |

View File

@@ -0,0 +1,4 @@
import javascript
from DataFlow::InvokeNode c
select c, c.getACallee()

View File

@@ -0,0 +1,18 @@
| classes.js:4:7:4:26 | console.log("Hello") | classes.js:4:19:4:25 | "Hello" |
| classes.js:14:7:14:27 | console ... ello!") | classes.js:14:19:14:26 | "Hello!" |
| es2015.js:3:5:3:21 | console.log("hi") | es2015.js:3:17:3:20 | "hi" |
| es2015.js:27:5:27:23 | console.log(this.x) | es2015.js:27:17:27:22 | this.x |
| es2015.js:35:1:35:17 | sum(1, ...[2, 3]) | es2015.js:35:5:35:5 | 1 |
| es2015.js:36:1:36:17 | sum(1, ...[2], 3) | es2015.js:36:5:36:5 | 1 |
| es2015.js:36:1:36:17 | sum(1, ...[2], 3) | es2015.js:36:16:36:16 | 3 |
| n.js:1:9:1:22 | require('./m') | n.js:1:17:1:21 | './m' |
| n.js:4:10:4:24 | require('./m2') | n.js:4:18:4:23 | './m2' |
| reflection.js:7:1:7:22 | add.cal ... 23, 19) | reflection.js:7:10:7:13 | null |
| reflection.js:7:1:7:22 | add.cal ... 23, 19) | reflection.js:7:16:7:17 | 23 |
| reflection.js:7:1:7:22 | add.cal ... 23, 19) | reflection.js:7:20:7:21 | 19 |
| reflection.js:7:1:7:22 | reflective call | reflection.js:7:16:7:17 | 23 |
| reflection.js:7:1:7:22 | reflective call | reflection.js:7:20:7:21 | 19 |
| reflection.js:8:1:8:25 | add.app ... 3, 19]) | reflection.js:8:11:8:14 | null |
| reflection.js:8:1:8:25 | add.app ... 3, 19]) | reflection.js:8:17:8:24 | [23, 19] |
| tst.js:22:1:22:4 | l(k) | tst.js:22:3:22:3 | k |
| tst.js:42:2:42:29 | functio ... x; }(o) | tst.js:42:28:42:28 | o |

View File

@@ -0,0 +1,4 @@
import javascript
from DataFlow::InvokeNode invk
select invk, invk.getAnArgument()

View File

@@ -0,0 +1,17 @@
| classes.js:4:7:4:26 | console.log("Hello") | 0 | classes.js:4:19:4:25 | "Hello" |
| classes.js:14:7:14:27 | console ... ello!") | 0 | classes.js:14:19:14:26 | "Hello!" |
| es2015.js:3:5:3:21 | console.log("hi") | 0 | es2015.js:3:17:3:20 | "hi" |
| es2015.js:27:5:27:23 | console.log(this.x) | 0 | es2015.js:27:17:27:22 | this.x |
| es2015.js:35:1:35:17 | sum(1, ...[2, 3]) | 0 | es2015.js:35:5:35:5 | 1 |
| es2015.js:36:1:36:17 | sum(1, ...[2], 3) | 0 | es2015.js:36:5:36:5 | 1 |
| n.js:1:9:1:22 | require('./m') | 0 | n.js:1:17:1:21 | './m' |
| n.js:4:10:4:24 | require('./m2') | 0 | n.js:4:18:4:23 | './m2' |
| reflection.js:7:1:7:22 | add.cal ... 23, 19) | 0 | reflection.js:7:10:7:13 | null |
| reflection.js:7:1:7:22 | add.cal ... 23, 19) | 1 | reflection.js:7:16:7:17 | 23 |
| reflection.js:7:1:7:22 | add.cal ... 23, 19) | 2 | reflection.js:7:20:7:21 | 19 |
| reflection.js:7:1:7:22 | reflective call | 0 | reflection.js:7:16:7:17 | 23 |
| reflection.js:7:1:7:22 | reflective call | 1 | reflection.js:7:20:7:21 | 19 |
| reflection.js:8:1:8:25 | add.app ... 3, 19]) | 0 | reflection.js:8:11:8:14 | null |
| reflection.js:8:1:8:25 | add.app ... 3, 19]) | 1 | reflection.js:8:17:8:24 | [23, 19] |
| tst.js:22:1:22:4 | l(k) | 0 | tst.js:22:3:22:3 | k |
| tst.js:42:2:42:29 | functio ... x; }(o) | 0 | tst.js:42:28:42:28 | o |

View File

@@ -0,0 +1,4 @@
import javascript
from DataFlow::InvokeNode invk, int i
select invk, i, invk.getArgument(i)

View File

@@ -0,0 +1,49 @@
| a.js:2:1:2:5 | foo() | foo |
| a.js:3:1:3:5 | bar() | bar |
| a.js:4:1:4:5 | qux() | qux |
| a.js:7:1:7:5 | baz() | baz |
| classes.js:4:7:4:26 | console.log("Hello") | log |
| classes.js:8:7:8:18 | this.hello() | hello |
| classes.js:14:7:14:27 | console ... ello!") | log |
| classes.js:18:3:18:9 | new B() | B |
| classes.js:18:3:18:17 | new B().hello() | hello |
| es2015.js:3:5:3:21 | console.log("hi") | log |
| es2015.js:6:1:6:18 | new ExampleClass() | ExampleClass |
| es2015.js:10:5:10:22 | arguments.callee() | callee |
| es2015.js:27:5:27:23 | console.log(this.x) | log |
| es2015.js:34:1:34:17 | sum(...[1, 2, 3]) | sum |
| es2015.js:35:1:35:17 | sum(1, ...[2, 3]) | sum |
| es2015.js:36:1:36:17 | sum(1, ...[2], 3) | sum |
| m.js:2:1:2:11 | exports.f() | f |
| m.js:3:1:3:18 | module.exports.f() | f |
| n.js:1:9:1:22 | require('./m') | require |
| n.js:2:1:2:5 | m.f() | f |
| n.js:4:10:4:24 | require('./m2') | require |
| n.js:5:1:5:6 | m2.f() | f |
| reflection.js:4:5:4:12 | sneaky() | sneaky |
| reflection.js:7:1:7:22 | add.cal ... 23, 19) | call |
| reflection.js:8:1:8:25 | add.app ... 3, 19]) | apply |
| tst.js:6:1:6:3 | f() | f |
| tst.js:7:1:7:3 | g() | g |
| tst.js:8:1:8:3 | h() | h |
| tst.js:9:1:9:3 | k() | k |
| tst.js:14:2:14:4 | m() | m |
| tst.js:15:2:15:4 | l() | l |
| tst.js:16:2:16:19 | arguments.callee() | callee |
| tst.js:17:2:17:4 | n() | n |
| tst.js:18:2:18:4 | p() | p |
| tst.js:19:2:19:4 | f() | f |
| tst.js:22:1:22:4 | l(k) | l |
| tst.js:24:1:24:7 | new f() | f |
| tst.js:25:1:25:5 | new g | g |
| tst.js:33:4:33:11 | this.g() | g |
| tst.js:41:1:41:5 | o.f() | f |
| tst.js:42:1:42:34 | (functi ... o)).f() | f |
| tst.js:46:2:46:9 | this.g() | g |
| tst.js:53:3:53:10 | this.g() | g |
| tst.js:56:17:56:29 | Math.random() | random |
| tst.js:60:15:60:21 | new A() | A |
| tst.js:64:13:64:19 | new B() | B |
| tst.js:66:5:66:9 | b.f() | f |
| tst.js:69:1:69:10 | globalfn() | globalfn |
| tst.js:70:1:70:11 | globalfn2() | globalfn2 |

View File

@@ -0,0 +1,4 @@
import javascript
from DataFlow::InvokeNode invk
select invk, invk.getCalleeName()

View File

@@ -0,0 +1,57 @@
| a.js:2:1:2:5 | foo() | a.js:2:1:2:3 | foo |
| a.js:3:1:3:5 | bar() | a.js:3:1:3:3 | bar |
| a.js:4:1:4:5 | qux() | a.js:4:1:4:3 | qux |
| a.js:7:1:7:5 | baz() | a.js:7:1:7:3 | baz |
| classes.js:4:7:4:26 | console.log("Hello") | classes.js:4:7:4:17 | console.log |
| classes.js:8:7:8:18 | this.hello() | classes.js:8:7:8:16 | this.hello |
| classes.js:12:21:12:20 | super(...args) | classes.js:12:21:12:20 | super |
| classes.js:14:7:14:27 | console ... ello!") | classes.js:14:7:14:17 | console.log |
| classes.js:18:3:18:9 | new B() | classes.js:18:7:18:7 | B |
| classes.js:18:3:18:17 | new B().hello() | classes.js:18:3:18:15 | new B().hello |
| es2015.js:3:5:3:21 | console.log("hi") | es2015.js:3:5:3:15 | console.log |
| es2015.js:6:1:6:18 | new ExampleClass() | es2015.js:6:5:6:16 | ExampleClass |
| es2015.js:10:5:10:22 | arguments.callee() | es2015.js:10:5:10:20 | arguments.callee |
| es2015.js:16:5:16:11 | super() | es2015.js:16:5:16:9 | super |
| es2015.js:26:5:26:11 | super() | es2015.js:26:5:26:9 | super |
| es2015.js:27:5:27:23 | console.log(this.x) | es2015.js:27:5:27:15 | console.log |
| es2015.js:34:1:34:17 | sum(...[1, 2, 3]) | es2015.js:34:1:34:3 | sum |
| es2015.js:35:1:35:17 | sum(1, ...[2, 3]) | es2015.js:35:1:35:3 | sum |
| es2015.js:36:1:36:17 | sum(1, ...[2], 3) | es2015.js:36:1:36:3 | sum |
| m.js:2:1:2:11 | exports.f() | m.js:2:1:2:9 | exports.f |
| m.js:3:1:3:18 | module.exports.f() | m.js:3:1:3:16 | module.exports.f |
| n.js:1:9:1:22 | require('./m') | n.js:1:9:1:15 | require |
| n.js:2:1:2:5 | m.f() | n.js:2:1:2:3 | m.f |
| n.js:4:10:4:24 | require('./m2') | n.js:4:10:4:16 | require |
| n.js:5:1:5:6 | m2.f() | n.js:5:1:5:4 | m2.f |
| reflection.js:4:5:4:12 | sneaky() | reflection.js:4:5:4:10 | sneaky |
| reflection.js:7:1:7:22 | add.cal ... 23, 19) | reflection.js:7:1:7:8 | add.call |
| reflection.js:7:1:7:22 | reflective call | reflection.js:7:1:7:3 | add |
| reflection.js:8:1:8:25 | add.app ... 3, 19]) | reflection.js:8:1:8:9 | add.apply |
| reflection.js:8:1:8:25 | reflective call | reflection.js:8:1:8:3 | add |
| tst.js:6:1:6:3 | f() | tst.js:6:1:6:1 | f |
| tst.js:7:1:7:3 | g() | tst.js:7:1:7:1 | g |
| tst.js:8:1:8:3 | h() | tst.js:8:1:8:1 | h |
| tst.js:9:1:9:3 | k() | tst.js:9:1:9:1 | k |
| tst.js:14:2:14:4 | m() | tst.js:14:2:14:2 | m |
| tst.js:15:2:15:4 | l() | tst.js:15:2:15:2 | l |
| tst.js:16:2:16:19 | arguments.callee() | tst.js:16:2:16:17 | arguments.callee |
| tst.js:17:2:17:4 | n() | tst.js:17:2:17:2 | n |
| tst.js:18:2:18:4 | p() | tst.js:18:2:18:2 | p |
| tst.js:19:2:19:4 | f() | tst.js:19:2:19:2 | f |
| tst.js:22:1:22:4 | l(k) | tst.js:22:1:22:1 | l |
| tst.js:24:1:24:7 | new f() | tst.js:24:5:24:5 | f |
| tst.js:25:1:25:5 | new g | tst.js:25:5:25:5 | g |
| tst.js:27:1:27:16 | (function(){})() | tst.js:27:1:27:14 | (function(){}) |
| tst.js:28:2:28:15 | function(){}() | tst.js:28:2:28:13 | function(){} |
| tst.js:33:4:33:11 | this.g() | tst.js:33:4:33:9 | this.g |
| tst.js:41:1:41:5 | o.f() | tst.js:41:1:41:3 | o.f |
| tst.js:42:1:42:34 | (functi ... o)).f() | tst.js:42:1:42:32 | (functi ... }(o)).f |
| tst.js:42:2:42:29 | functio ... x; }(o) | tst.js:42:2:42:26 | functio ... rn x; } |
| tst.js:46:2:46:9 | this.g() | tst.js:46:2:46:7 | this.g |
| tst.js:53:3:53:10 | this.g() | tst.js:53:3:53:8 | this.g |
| tst.js:56:17:56:29 | Math.random() | tst.js:56:17:56:27 | Math.random |
| tst.js:60:15:60:21 | new A() | tst.js:60:19:60:19 | A |
| tst.js:64:13:64:19 | new B() | tst.js:64:17:64:17 | B |
| tst.js:66:5:66:9 | b.f() | tst.js:66:5:66:7 | b.f |
| tst.js:69:1:69:10 | globalfn() | tst.js:69:1:69:8 | globalfn |
| tst.js:70:1:70:11 | globalfn2() | tst.js:70:1:70:9 | globalfn2 |

View File

@@ -0,0 +1,4 @@
import javascript
from DataFlow::InvokeNode invk
select invk, invk.getCalleeNode()

View File

@@ -0,0 +1,11 @@
| classes.js:4:7:4:26 | console.log("Hello") | classes.js:4:19:4:25 | "Hello" |
| classes.js:14:7:14:27 | console ... ello!") | classes.js:14:19:14:26 | "Hello!" |
| es2015.js:3:5:3:21 | console.log("hi") | es2015.js:3:17:3:20 | "hi" |
| es2015.js:27:5:27:23 | console.log(this.x) | es2015.js:27:17:27:22 | this.x |
| n.js:1:9:1:22 | require('./m') | n.js:1:17:1:21 | './m' |
| n.js:4:10:4:24 | require('./m2') | n.js:4:18:4:23 | './m2' |
| reflection.js:7:1:7:22 | add.cal ... 23, 19) | reflection.js:7:20:7:21 | 19 |
| reflection.js:7:1:7:22 | reflective call | reflection.js:7:20:7:21 | 19 |
| reflection.js:8:1:8:25 | add.app ... 3, 19]) | reflection.js:8:17:8:24 | [23, 19] |
| tst.js:22:1:22:4 | l(k) | tst.js:22:3:22:3 | k |
| tst.js:42:2:42:29 | functio ... x; }(o) | tst.js:42:28:42:28 | o |

View File

@@ -0,0 +1,4 @@
import javascript
from DataFlow::InvokeNode invk
select invk, invk.getLastArgument()

View File

@@ -0,0 +1,52 @@
| a.js:2:1:2:5 | foo() | 0 |
| a.js:3:1:3:5 | bar() | 0 |
| a.js:4:1:4:5 | qux() | 0 |
| a.js:7:1:7:5 | baz() | 0 |
| classes.js:4:7:4:26 | console.log("Hello") | 1 |
| classes.js:8:7:8:18 | this.hello() | 0 |
| classes.js:14:7:14:27 | console ... ello!") | 1 |
| classes.js:18:3:18:9 | new B() | 0 |
| classes.js:18:3:18:17 | new B().hello() | 0 |
| es2015.js:3:5:3:21 | console.log("hi") | 1 |
| es2015.js:6:1:6:18 | new ExampleClass() | 0 |
| es2015.js:10:5:10:22 | arguments.callee() | 0 |
| es2015.js:16:5:16:11 | super() | 0 |
| es2015.js:26:5:26:11 | super() | 0 |
| es2015.js:27:5:27:23 | console.log(this.x) | 1 |
| m.js:2:1:2:11 | exports.f() | 0 |
| m.js:3:1:3:18 | module.exports.f() | 0 |
| n.js:1:9:1:22 | require('./m') | 1 |
| n.js:2:1:2:5 | m.f() | 0 |
| n.js:4:10:4:24 | require('./m2') | 1 |
| n.js:5:1:5:6 | m2.f() | 0 |
| reflection.js:4:5:4:12 | sneaky() | 0 |
| reflection.js:7:1:7:22 | add.cal ... 23, 19) | 3 |
| reflection.js:7:1:7:22 | reflective call | 2 |
| reflection.js:8:1:8:25 | add.app ... 3, 19]) | 2 |
| tst.js:6:1:6:3 | f() | 0 |
| tst.js:7:1:7:3 | g() | 0 |
| tst.js:8:1:8:3 | h() | 0 |
| tst.js:9:1:9:3 | k() | 0 |
| tst.js:14:2:14:4 | m() | 0 |
| tst.js:15:2:15:4 | l() | 0 |
| tst.js:16:2:16:19 | arguments.callee() | 0 |
| tst.js:17:2:17:4 | n() | 0 |
| tst.js:18:2:18:4 | p() | 0 |
| tst.js:19:2:19:4 | f() | 0 |
| tst.js:22:1:22:4 | l(k) | 1 |
| tst.js:24:1:24:7 | new f() | 0 |
| tst.js:25:1:25:5 | new g | 0 |
| tst.js:27:1:27:16 | (function(){})() | 0 |
| tst.js:28:2:28:15 | function(){}() | 0 |
| tst.js:33:4:33:11 | this.g() | 0 |
| tst.js:41:1:41:5 | o.f() | 0 |
| tst.js:42:1:42:34 | (functi ... o)).f() | 0 |
| tst.js:42:2:42:29 | functio ... x; }(o) | 1 |
| tst.js:46:2:46:9 | this.g() | 0 |
| tst.js:53:3:53:10 | this.g() | 0 |
| tst.js:56:17:56:29 | Math.random() | 0 |
| tst.js:60:15:60:21 | new A() | 0 |
| tst.js:64:13:64:19 | new B() | 0 |
| tst.js:66:5:66:9 | b.f() | 0 |
| tst.js:69:1:69:10 | globalfn() | 0 |
| tst.js:70:1:70:11 | globalfn2() | 0 |

View File

@@ -0,0 +1,4 @@
import javascript
from DataFlow::InvokeNode invk
select invk, invk.getNumArgument()

View File

@@ -0,0 +1 @@
| tst.js:69:1:69:10 | globalfn() |

View File

@@ -0,0 +1,5 @@
import javascript
from DataFlow::InvokeNode invk
where invk.isImprecise()
select invk

View File

@@ -0,0 +1,30 @@
| a.js:7:1:7:5 | baz() |
| classes.js:4:7:4:26 | console.log("Hello") |
| classes.js:8:7:8:18 | this.hello() |
| classes.js:14:7:14:27 | console ... ello!") |
| classes.js:18:3:18:17 | new B().hello() |
| es2015.js:3:5:3:21 | console.log("hi") |
| es2015.js:27:5:27:23 | console.log(this.x) |
| m.js:2:1:2:11 | exports.f() |
| m.js:3:1:3:18 | module.exports.f() |
| n.js:1:9:1:22 | require('./m') |
| n.js:2:1:2:5 | m.f() |
| n.js:4:10:4:24 | require('./m2') |
| n.js:5:1:5:6 | m2.f() |
| reflection.js:7:1:7:22 | add.cal ... 23, 19) |
| reflection.js:8:1:8:25 | add.app ... 3, 19]) |
| tst.js:6:1:6:3 | f() |
| tst.js:7:1:7:3 | g() |
| tst.js:9:1:9:3 | k() |
| tst.js:14:2:14:4 | m() |
| tst.js:17:2:17:4 | n() |
| tst.js:19:2:19:4 | f() |
| tst.js:24:1:24:7 | new f() |
| tst.js:25:1:25:5 | new g |
| tst.js:33:4:33:11 | this.g() |
| tst.js:41:1:41:5 | o.f() |
| tst.js:42:1:42:34 | (functi ... o)).f() |
| tst.js:46:2:46:9 | this.g() |
| tst.js:53:3:53:10 | this.g() |
| tst.js:56:17:56:29 | Math.random() |
| tst.js:66:5:66:9 | b.f() |

View File

@@ -0,0 +1,5 @@
import javascript
from DataFlow::InvokeNode invk
where invk.isIncomplete()
select invk

View File

@@ -0,0 +1,31 @@
| a.js:7:1:7:5 | baz() |
| classes.js:4:7:4:26 | console.log("Hello") |
| classes.js:8:7:8:18 | this.hello() |
| classes.js:14:7:14:27 | console ... ello!") |
| classes.js:18:3:18:17 | new B().hello() |
| es2015.js:3:5:3:21 | console.log("hi") |
| es2015.js:27:5:27:23 | console.log(this.x) |
| m.js:2:1:2:11 | exports.f() |
| m.js:3:1:3:18 | module.exports.f() |
| n.js:1:9:1:22 | require('./m') |
| n.js:2:1:2:5 | m.f() |
| n.js:4:10:4:24 | require('./m2') |
| n.js:5:1:5:6 | m2.f() |
| reflection.js:7:1:7:22 | add.cal ... 23, 19) |
| reflection.js:8:1:8:25 | add.app ... 3, 19]) |
| tst.js:6:1:6:3 | f() |
| tst.js:7:1:7:3 | g() |
| tst.js:9:1:9:3 | k() |
| tst.js:14:2:14:4 | m() |
| tst.js:17:2:17:4 | n() |
| tst.js:19:2:19:4 | f() |
| tst.js:24:1:24:7 | new f() |
| tst.js:25:1:25:5 | new g |
| tst.js:33:4:33:11 | this.g() |
| tst.js:41:1:41:5 | o.f() |
| tst.js:42:1:42:34 | (functi ... o)).f() |
| tst.js:46:2:46:9 | this.g() |
| tst.js:53:3:53:10 | this.g() |
| tst.js:56:17:56:29 | Math.random() |
| tst.js:66:5:66:9 | b.f() |
| tst.js:69:1:69:10 | globalfn() |

View File

@@ -0,0 +1,5 @@
import javascript
from DataFlow::InvokeNode invk
where invk.isUncertain()
select invk

View File

@@ -0,0 +1,8 @@
function add(x, y) {
return x+y;
}
if (sneaky())
add.apply = function() { return 56; };
add.call(null, 23, 19);
add.apply(null, [23, 19]);

View File

@@ -64,4 +64,7 @@ C.prototype.g = function() {};
var b = new B(); var b = new B();
b.f = function() {}; b.f = function() {};
b.f(); b.f();
}); });
globalfn();
globalfn2();

View File

@@ -0,0 +1 @@
var globalfn = null;

View File

@@ -0,0 +1,2 @@
function globalfn() {}
function globalfn2() {}

View File

@@ -1,4 +1,4 @@
import javascript import javascript
from CallSite call from DataFlow::InvokeNode call
select call, call.getACallee() select call, call.getACallee()

View File

@@ -1,5 +1,5 @@
import javascript import javascript
from NewExpr new, ClassDefinition klass from DataFlow::NewNode new, ClassDefinition klass
where klass.getConstructor().getInit() = new.(CallSite).getACallee() where klass.getConstructor().getInit() = new.getACallee()
select new.getFile().getBaseName(), new.getCalleeName(), klass.getFile().getBaseName(), klass.getName() select new.getFile().getBaseName(), new.getCalleeName(), klass.getFile().getBaseName(), klass.getName()

View File

@@ -4,5 +4,5 @@
| tst.js:24:1:24:9 | new o.g() | Illegal invocation of $@ using 'new'. | tst.js:19:4:19:8 | () {} | a method | | tst.js:24:1:24:9 | new o.g() | Illegal invocation of $@ using 'new'. | tst.js:19:4:19:8 | () {} | a method |
| tst.js:42:1:42:7 | new g() | Illegal invocation of $@ using 'new'. | tst.js:39:1:39:16 | function* g() {} | a generator function | | tst.js:42:1:42:7 | new g() | Illegal invocation of $@ using 'new'. | tst.js:39:1:39:16 | function* g() {} | a generator function |
| tst.js:43:1:43:7 | new h() | Illegal invocation of $@ using 'new'. | tst.js:40:1:40:21 | async f ... h() {} | an async function | | tst.js:43:1:43:7 | new h() | Illegal invocation of $@ using 'new'. | tst.js:40:1:40:21 | async f ... h() {} | an async function |
| tst.js:45:1:45:8 | C.call() | Illegal invocation of $@ as a function. | tst.js:1:9:1:8 | () {} | a constructor | | tst.js:45:1:45:8 | reflective call | Illegal invocation of $@ as a function. | tst.js:1:9:1:8 | () {} | a constructor |
| tst.js:46:1:46:9 | C.apply() | Illegal invocation of $@ as a function. | tst.js:1:9:1:8 | () {} | a constructor | | tst.js:46:1:46:9 | reflective call | Illegal invocation of $@ as a function. | tst.js:1:9:1:8 | () {} | a constructor |

View File

@@ -1,7 +1,12 @@
| es2015.js:4:16:4:17 | 23 | Superfluous argument passed to $@. | es2015.js:2:14:2:32 | (x) { this.x = x; } | constructor of class Class1 | | es2015.js:4:16:4:17 | 23 | Superfluous argument passed to $@. | es2015.js:2:14:2:32 | (x) { this.x = x; } | constructor of class Class1 |
| es2015.js:17:11:17:12 | 42 | Superfluous argument passed to $@. | es2015.js:15:13:15:12 | () {} | default constructor of class Other | | es2015.js:17:11:17:12 | 42 | Superfluous argument passed to $@. | es2015.js:15:13:15:12 | () {} | default constructor of class Other |
| es2015.js:21:3:21:13 | 42 | Superfluous arguments passed to $@. | tst.js:1:1:4:1 | functio ... x+19;\\n} | function f | | es2015.js:21:3:21:4 | 42 | Superfluous argument passed to $@. | tst.js:1:1:4:1 | functio ... x+19;\\n} | function f |
| globals.js:15:13:15:13 | x | Superfluous argument passed to $@. | globals.js:9:1:9:25 | functio ... al() {} | function otherglobal | | globals.js:15:13:15:13 | x | Superfluous argument passed to $@. | globals.js:9:1:9:25 | functio ... al() {} | function otherglobal |
| globals.js:16:24:16:24 | x | Superfluous argument passed to $@. | globals.js:9:1:9:25 | functio ... al() {} | function otherglobal |
| globals.js:17:24:17:27 | x | Superfluous arguments passed to $@. | globals.js:9:1:9:25 | functio ... al() {} | function otherglobal |
| reflection.js:6:15:6:15 | 1 | Superfluous argument passed to $@. | reflection.js:1:1:1:16 | function f0() {} | function f0 |
| reflection.js:7:15:7:18 | 1 | Superfluous arguments passed to $@. | reflection.js:1:1:1:16 | function f0() {} | function f0 |
| reflection.js:12:18:12:18 | 2 | Superfluous argument passed to $@. | reflection.js:2:1:2:17 | function f1(x) {} | function f1 |
| thisparameter.ts:4:11:4:12 | 45 | Superfluous argument passed to $@. | thisparameter.ts:1:1:1:38 | functio ... ber) {} | function foo | | thisparameter.ts:4:11:4:12 | 45 | Superfluous argument passed to $@. | thisparameter.ts:1:1:1:38 | functio ... ber) {} | function foo |
| tst.js:11:3:11:5 | g() | Superfluous argument passed to $@. | tst.js:1:1:4:1 | functio ... x+19;\\n} | function f | | tst.js:11:3:11:5 | g() | Superfluous argument passed to $@. | tst.js:1:1:4:1 | functio ... x+19;\\n} | function f |
| tst.js:33:15:33:18 | 2 | Superfluous arguments passed to $@. | externs.js:34:1:34:27 | functio ... str) {} | function String | | tst.js:33:15:33:18 | 2 | Superfluous arguments passed to $@. | externs.js:34:1:34:27 | functio ... str) {} | function String |

View File

@@ -12,4 +12,6 @@ var o = {
otherglobal: function (x) {} otherglobal: function (x) {}
}; };
otherglobal(x); // NOT OK: can never refer to function on line 12 otherglobal(x); // NOT OK: can never refer to function on line 12
otherglobal.call(null, x); // NOT OK
otherglobal.call(null, x, y); // NOT OK

View File

@@ -0,0 +1,24 @@
function f0() {}
function f1(x) {}
f0.call();
f0.call(this);
f0.call(this, 1);
f0.call(this, 1, 2);
f1.call();
f1.call(this);
f1.call(this, 1);
f1.call(this, 1, 2);
f0.apply();
f0.apply(this);
f0.apply(this, []);
f0.apply(this, [1]);
f0.apply(this, [1, 2]);
f1.apply();
f1.apply(this);
f1.apply(this, []);
f1.apply(this, [1]);
f1.apply(this, [1, 2]);

View File

@@ -1,6 +1,6 @@
import javascript import javascript
from CallSite cs from DataFlow::InvokeNode cs
where not cs.isIncomplete() and where not cs.isIncomplete() and
not exists(cs.getACallee()) not exists(cs.getACallee())
select cs, "Unable to find a callee for this call site." select cs, "Unable to find a callee for this call site."

View File

@@ -0,0 +1,24 @@
function f0() {}
function f1(x) {}
f0.call();
f0.call(this);
f0.call(this, 1);
f0.call(this, 1, 2);
f1.call();
f1.call(this);
f1.call(this, 1);
f1.call(this, 1, 2);
f0.apply();
f0.apply(this);
f0.apply(this, []);
f0.apply(this, [1]);
f0.apply(this, [1, 2]);
f1.apply();
f1.apply(this);
f1.apply(this, []);
f1.apply(this, [1]);
f1.apply(this, [1, 2]);