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.
* 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()
or
// `new Error(...)`, `new SyntaxError(...)`, etc.
e instanceof NewExpr and
forex (Function f | f = e.(CallSite).getACallee() |
forex (Function f | f = e.flow().(DataFlow::NewNode).getACallee() |
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`.
*/
predicate calls(CallSite cs, Function callee, string how) {
predicate calls(DataFlow::InvokeNode cs, Function callee, string how) {
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"
or
cs instanceof NewExpr and
cs instanceof DataFlow::NewNode and
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`;
* `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
(
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) {
exists (Function f | f = ce.(CallSite).getACallee() |
predicate isCallToFunction(DataFlow::InvokeNode ce) {
ce instanceof DataFlow::CallNode and
exists (Function f | f = ce.getACallee() |
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
// filter out some easy cases
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.
*/
predicate guardsAgainstMissingNew(Function f) {
exists (CallSite new |
new.(NewExpr).getEnclosingFunction() = f and
exists (DataFlow::NewNode new |
new.asExpr().getEnclosingFunction() = f and
f = new.getACallee()
)
}
@@ -34,13 +34,13 @@ predicate guardsAgainstMissingNew(Function f) {
* is only suggested as a potential callee due to imprecise analysis of global
* 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
(
// if global flow was used to derive the callee, we may be imprecise
if cs.isIndefinite("global") then
// callees within the same file are probably genuine
callee.getFile() = cs.(Locatable).getFile() and imprecision = 0
callee.getFile() = cs.getFile() and imprecision = 0
or
// calls to global functions declared in an externs file are fairly
// 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
* 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)))
}
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
not f.inExternsFile() and
// 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(call) and
not guardsAgainstMissingNew(f) and
not new.(CallSite).isUncertain() and
not call.(CallSite).isUncertain() and
not new.isUncertain() and
not call.isUncertain() and
// super constructor calls behave more like `new`, so don't flag them
not call instanceof SuperCall and
// reflective calls provide an explicit receiver object, so don't flag them either
not call instanceof ReflectiveCallSite
not call.asExpr() instanceof SuperCall and
// don't flag if there is a receiver object
not exists(call.getReceiver())
select (FirstLineOf)f, capitalize(f.describe()) + " is invoked as a constructor here $@, " +
"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.
*/
int maxArity(CallSite invk) {
int maxArity(DataFlow::InvokeNode invk) {
forall (Function callee | callee = invk.getACallee() | isFixedArity(callee)) and
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.
*
@@ -54,15 +42,18 @@ predicate firstSpuriousArgument(CallSite invk, Expr arg) {
* defined to cover all subsequent arguments as well.
*/
class SpuriousArguments extends Expr {
DataFlow::InvokeNode invk;
SpuriousArguments() {
firstSpuriousArgument(_, this)
this = invk.getArgument(maxArity(invk)).asExpr() and
not invk.isIncomplete()
}
/**
* Gets the call site at which the spurious arguments are passed.
*/
CallSite getCall() {
firstSpuriousArgument(result, this)
DataFlow::InvokeNode getCall() {
result = invk
}
/**
@@ -71,7 +62,7 @@ class SpuriousArguments extends Expr {
* expected by any potential callee.
*/
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).
*/
predicate hasLocationInfo(string filepath, int startline, int startcolumn, int endline, int endcolumn) {
this.getLocation().hasLocationInfo(filepath, startline, startcolumn, _, _) and
exists (Expr lastArg | lastArg = getCall().(InvokeExpr).getLastArgument() |
lastArg.getLocation().hasLocationInfo(_, _, _, endline, endcolumn)
getLocation().hasLocationInfo(filepath, startline, startcolumn, _, _) and
exists (DataFlow::Node lastArg |
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
where f = args.getCall().getACallee() and
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.
*/
class CallOnSelf extends CallExpr {
class CallOnSelf extends DataFlow::CallNode {
CallOnSelf() {
exists (Function binder |
binder = getEnclosingFunction().getThisBinder() |
exists (DataFlow::ThisNode thiz |
this = thiz.getAMethodCall(_).asExpr() and
this = thiz.getAMethodCall(_) and
thiz.getBinder().getAstNode() = binder
)
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.
*/
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.
*/
predicate isUnconditionalCall(CallExpr call) {
predicate isUnconditionalCall(DataFlow::CallNode call) {
exists (ReachableBasicBlock callBlock, ReachableBasicBlock entryBlock |
callBlock.postDominates(entryBlock) and
callBlock.getANode() = call and
callBlock = call.getBasicBlock() and
entryBlock = call.getEnclosingFunction().getEntryBB()
)
}
predicate isStateUpdateMethodCall(MethodCallExpr mce) {
predicate isStateUpdateMethodCall(DataFlow::MethodCallNode mce) {
exists (string updateMethodName |
updateMethodName = "setState" 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
stateUpdate = initCall.getACalleCallOnSelf*() and
isStateUpdateMethodCall(stateUpdate) and

View File

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

View File

@@ -9,7 +9,7 @@ class CallToObjectDefineProperty extends DataFlow::MethodCallNode {
CallToObjectDefineProperty() {
exists (GlobalVariable obj |
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
/**
* DEPRECATED: Use `DataFlow::InvokeNode` instead.
*
* A function call or `new` expression, with information about its potential callees.
*
* Both direct calls and reflective calls using `call` or `apply` are modelled.
*/
class CallSite extends @invokeexpr {
deprecated class CallSite extends @invokeexpr {
InvokeExpr invk;
CallSite() { invk = this }
@@ -120,7 +122,7 @@ class CallSite extends @invokeexpr {
/**
* A reflective function call using `call` or `apply`.
*/
class ReflectiveCallSite extends CallSite {
deprecated class ReflectiveCallSite extends CallSite {
DataFlow::AnalyzedNode callee;
string callMode;

View File

@@ -407,7 +407,7 @@ private predicate callInputStep(Function f, DataFlow::Node invk,
or
exists (SsaDefinition prevDef, SsaDefinition def |
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)
)
) and

View File

@@ -29,6 +29,9 @@ module DataFlow {
or TDestructuringPatternNode(DestructuringPattern dp)
or TElementPatternNode(ArrayPattern ap, Expr p) { p = ap.getElement(_) }
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.
@@ -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.
*
@@ -643,6 +670,146 @@ module DataFlow {
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
* `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`). */
class InvokeNode extends DataFlow::ValueNode, DataFlow::DefaultSourceNode {
override InvokeExpr astNode;
class InvokeNode extends DataFlow::DefaultSourceNode {
DataFlow::Impl::InvokeNodeDef impl;
InvokeNode() { this = impl }
/** Gets the name of the function or method being invoked, if it can be determined. */
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. */
DataFlow::ValueNode getCallee() {
result = DataFlow::valueNode(astNode.getCallee())
DataFlow::Node getCalleeNode() {
result = impl.getCalleeNode()
}
/** Gets the data flow node corresponding to the `i`th argument of this invocation. */
DataFlow::ValueNode getArgument(int i) {
result = DataFlow::valueNode(astNode.getArgument(i))
/**
* Gets the data flow node corresponding to the `i`th argument of this invocation.
*
* 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. */
DataFlow::ValueNode getAnArgument() {
result = getArgument(_)
DataFlow::Node getAnArgument() {
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() {
result = astNode.getNumArgument()
result = impl.getNumArgument()
}
Function getEnclosingFunction() {
result = getBasicBlock().getContainer()
}
/** 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)
)
}
/** 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`. */
class CallNode extends InvokeNode {
override CallExpr astNode;
}
/** A data flow node corresponding to a method call. */
class MethodCallNode extends CallNode {
override MethodCallExpr astNode;
override DataFlow::Impl::CallNodeDef impl;
/**
* 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`.
*/
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. */
string getMethodName() { result = astNode.getMethodName() }
string getMethodName() { result = impl.getMethodName() }
/**
* 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. */
class NewNode extends InvokeNode {
override NewExpr astNode;
override DataFlow::Impl::NewNodeDef impl;
}
/** 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) {
exists (PropAccess pacc |
pacc = result.getCallee().asExpr().stripParens() and
pacc = result.getCalleeNode().asExpr().stripParens() and
flowsToExpr(pacc.getBase()) and
pacc.getPropertyName() = methodName
)
@@ -142,7 +142,7 @@ abstract class SourceNode extends DataFlow::Node {
* Gets an invocation (with our without `new`) of this node.
*/
DataFlow::InvokeNode getAnInvocation() {
flowsTo(result.getCallee())
flowsTo(result.getCalleeNode())
}
/**
@@ -182,7 +182,6 @@ class DefaultSourceNode extends SourceNode {
astNode instanceof PropAccess or
astNode instanceof Function or
astNode instanceof ClassDefinition or
astNode instanceof InvokeExpr or
astNode instanceof ObjectExpr or
astNode instanceof ArrayExpr or
astNode instanceof JSXNode or
@@ -197,5 +196,7 @@ class DefaultSourceNode extends SourceNode {
)
or
DataFlow::parameterNode(this, _)
or
this instanceof DataFlow::Impl::InvokeNodeDef
}
}

View File

@@ -123,7 +123,7 @@ private module NodeTracking {
or
exists (SsaDefinition prevDef, SsaDefinition def |
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)
)
)

View File

@@ -20,13 +20,11 @@ predicate shouldTrackProperties(AbstractValue obj) {
/**
* Holds if `invk` may invoke `f`.
*/
predicate calls(InvokeExpr invk, Function f) {
exists (CallSite cs | cs = invk |
if cs.isIndefinite("global") then
(f = cs.getACallee() and f.getFile() = invk.getFile())
else
f = cs.getACallee()
)
predicate calls(DataFlow::InvokeNode invk, Function f) {
if invk.isIndefinite("global") then
(f = invk.getACallee() and f.getFile() = invk.getFile())
else
f = invk.getACallee()
}
/**
@@ -65,11 +63,11 @@ predicate localFlowStep(DataFlow::Node pred, DataFlow::Node succ,
* Holds if `arg` is passed as an argument into parameter `parm`
* through invocation `invk` of function `f`.
*/
predicate argumentPassing(DataFlow::ValueNode invk, DataFlow::ValueNode arg, Function f, Parameter parm) {
exists (int i, CallSite cs |
cs = invk.asExpr() and calls(cs, f) and
predicate argumentPassing(DataFlow::InvokeNode invk, DataFlow::ValueNode arg, Function f, Parameter parm) {
calls(invk, f) and
exists (int i |
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) {
exists (Function f |
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)
exists(astNode.getAReturnStmt().getExpr()) or
// 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)
exists(astNode.getAReturnStmt().getExpr()) or
// 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.
*/
DataFlow::SourceNode getACandidatePropsSource() {
exists (DataFlow::InvokeNode call |
getComponentCreatorSource().flowsTo(call.getCallee()) and
result.flowsTo(call.getArgument(0))
) or
result.flowsTo(getComponentCreatorSource().getAnInvocation().getArgument(0))
or
result = getADefaultPropsSource()
}

View File

@@ -140,7 +140,7 @@ abstract class SensitiveAction extends DataFlow::Node { }
class AuthorizationCall extends SensitiveAction, DataFlow::CallNode {
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`;
// also exclude `author`
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();
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: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: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: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: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: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() {} |
@@ -38,3 +44,5 @@
| 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: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();
b.f = function() {};
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
from CallSite call
from DataFlow::InvokeNode call
select call, call.getACallee()

View File

@@ -1,5 +1,5 @@
import javascript
from NewExpr new, ClassDefinition klass
where klass.getConstructor().getInit() = new.(CallSite).getACallee()
from DataFlow::NewNode new, ClassDefinition klass
where klass.getConstructor().getInit() = new.getACallee()
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: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:45:1:45:8 | C.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: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 | 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: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: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 |
| 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 |

View File

@@ -12,4 +12,6 @@ var o = {
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
from CallSite cs
from DataFlow::InvokeNode cs
where not cs.isIncomplete() and
not exists(cs.getACallee())
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]);