mirror of
https://github.com/github/codeql.git
synced 2026-04-30 03:05:15 +02:00
JS: Move call graph computation into CallGraphs.qll
This commit is contained in:
@@ -19,6 +19,7 @@
|
||||
*/
|
||||
|
||||
import javascript
|
||||
private import internal.CallGraphs
|
||||
|
||||
module DataFlow {
|
||||
cached
|
||||
@@ -118,57 +119,15 @@ module DataFlow {
|
||||
|
||||
/** Gets a function value that may reach this node. */
|
||||
final FunctionNode getAFunctionValue() {
|
||||
result = getAFunctionValue(_)
|
||||
CallGraph::getAFunctionReference(result, 0).flowsTo(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a function value that may reach this node.
|
||||
*
|
||||
* This predicate may be overridden to customize the call graph.
|
||||
*
|
||||
* Note that any additional values added by overiding predicates will not
|
||||
* be propagated along data flow edges.
|
||||
*
|
||||
* `imprecision` is a heuristic measure of how likely it is that `callee`
|
||||
* is only suggested as a potential callee due to
|
||||
* imprecise analysis of global variables and is not, in fact, a viable callee at all.
|
||||
* Gets a function value that may reach this node,
|
||||
* possibly derived from a partial function invocation.
|
||||
*/
|
||||
cached
|
||||
FunctionNode getAFunctionValue(int imprecision) {
|
||||
exists(Function fun |
|
||||
result = fun.flow() and
|
||||
fun = analyze().getAValue().(AbstractCallable).getFunction()
|
||||
|
|
||||
if analyze().getAValue().isIndefinite("global") then
|
||||
fun.getFile() = getFile() and imprecision = 0
|
||||
or
|
||||
fun.inExternsFile() and imprecision = 1
|
||||
or
|
||||
imprecision = 2
|
||||
else
|
||||
imprecision = 0
|
||||
)
|
||||
or
|
||||
imprecision = 0 and
|
||||
exists(string name |
|
||||
GlobalAccessPath::isAssignedInUniqueFile(name) and
|
||||
GlobalAccessPath::fromRhs(result) = name and
|
||||
GlobalAccessPath::fromReference(this) = name
|
||||
)
|
||||
or
|
||||
imprecision = 0 and
|
||||
exists(DataFlow::ClassNode cls |
|
||||
exists(string name |
|
||||
cls.getAnInstanceMemberAccess(name).flowsTo(this) and
|
||||
result = cls.getInstanceMethod(name)
|
||||
or
|
||||
cls.getAStaticMemberAccess(name).flowsTo(this) and
|
||||
result = cls.getStaticMethod(name)
|
||||
)
|
||||
or
|
||||
cls.getAClassReference().flowsTo(this) and
|
||||
result = cls.getConstructor()
|
||||
)
|
||||
final FunctionNode getABoundFunctionValue(int boundArgs) {
|
||||
CallGraph::getABoundFunctionReference(result, boundArgs).flowsTo(this)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
private import javascript
|
||||
private import semmle.javascript.dependencies.Dependencies
|
||||
private import internal.CallGraphs
|
||||
|
||||
/** A data flow node corresponding to an expression. */
|
||||
class ExprNode extends DataFlow::ValueNode {
|
||||
@@ -89,9 +90,24 @@ class InvokeNode extends DataFlow::SourceNode {
|
||||
|
||||
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.
|
||||
*
|
||||
* This predicate only performs local data flow tracking.
|
||||
* Use `getACallbackSource` to handle interprocedural flow of callback functions.
|
||||
*/
|
||||
FunctionNode getCallback(int i) { result.flowsTo(getArgument(i)) }
|
||||
|
||||
/**
|
||||
* Gets a function passed as the `i`th argument of this invocation,
|
||||
* possibly through non-local data flow and bound by partial function invocations.
|
||||
*/
|
||||
ParameterNode getABoundCallbackParameter(int callback, int param) {
|
||||
exists(int boundArgs |
|
||||
result = getArgument(callback).getABoundFunctionValue(boundArgs).getParameter(param + boundArgs)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the `i`th argument of this invocation is an object literal whose property
|
||||
* `name` is set to `result`.
|
||||
@@ -127,14 +143,14 @@ class InvokeNode extends DataFlow::SourceNode {
|
||||
*/
|
||||
cached
|
||||
Function getACallee(int imprecision) {
|
||||
result.flow() = getCalleeNode().getAFunctionValue(imprecision)
|
||||
CallGraph::getAFunctionReference(result.flow(), imprecision).flowsTo(getCalleeNode())
|
||||
or
|
||||
imprecision = 0 and
|
||||
exists(InvokeExpr expr | expr = this.(DataFlow::Impl::ExplicitInvokeNode).asExpr() |
|
||||
result = expr.getResolvedCallee()
|
||||
or
|
||||
exists(DataFlow::ClassNode cls |
|
||||
expr.(SuperCall).getBinder() = cls.getAnInstanceMethodOrConstructor().getFunction() and
|
||||
expr.(SuperCall).getBinder() = cls.getConstructor().getFunction() and
|
||||
result = cls.getADirectSuperClass().getConstructor().getFunction()
|
||||
)
|
||||
)
|
||||
@@ -726,23 +742,6 @@ class ClassNode extends DataFlow::SourceNode {
|
||||
result = getAnInstanceReference(DataFlow::TypeTracker::end())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a property read that accesses the property `name` on an instance of this class.
|
||||
*
|
||||
* Concretely, this holds when the base is an instance of this class or a subclass thereof.
|
||||
*
|
||||
* This predicate may be overridden to customize the class hierarchy analysis.
|
||||
*/
|
||||
DataFlow::PropRead getAnInstanceMemberAccess(string name) {
|
||||
result = getAnInstanceReference().getAPropertyRead(name)
|
||||
or
|
||||
exists(DataFlow::ClassNode subclass |
|
||||
result = subclass.getAnInstanceMemberAccess(name) and
|
||||
not exists(subclass.getAnInstanceMember(name)) and
|
||||
subclass = getADirectSubClass()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an access to a static member of this class.
|
||||
*/
|
||||
@@ -986,7 +985,7 @@ module ClassNode {
|
||||
* A data flow node that performs a partial function application.
|
||||
*
|
||||
* Examples:
|
||||
* ```
|
||||
* ```js
|
||||
* fn.bind(this)
|
||||
* x.method.bind(x)
|
||||
* _.partial(fn, x, y, z)
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
private import javascript
|
||||
|
||||
cached
|
||||
module CallGraph {
|
||||
/**
|
||||
* Gets a data flow node that refers to the given function.
|
||||
*/
|
||||
private
|
||||
DataFlow::SourceNode getAFunctionReference(DataFlow::FunctionNode function, int imprecision, DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
exists(Function fun |
|
||||
fun = function.getFunction() and
|
||||
fun = result.analyze().getAValue().(AbstractCallable).getFunction()
|
||||
|
|
||||
if result.analyze().getAValue().isIndefinite("global") then
|
||||
fun.getFile() = result.getFile() and imprecision = 0
|
||||
or
|
||||
fun.inExternsFile() and imprecision = 1
|
||||
or
|
||||
imprecision = 2
|
||||
else
|
||||
imprecision = 0
|
||||
)
|
||||
or
|
||||
imprecision = 0 and
|
||||
t.start() and
|
||||
exists(string name |
|
||||
GlobalAccessPath::isAssignedInUniqueFile(name) and
|
||||
GlobalAccessPath::fromRhs(function) = name and
|
||||
GlobalAccessPath::fromReference(result) = name
|
||||
)
|
||||
or
|
||||
imprecision = 0 and
|
||||
exists(DataFlow::ClassNode cls |
|
||||
exists(string name |
|
||||
function = cls.getInstanceMethod(name) and
|
||||
getAnInstanceMemberAccess(cls, name, t.continue()).flowsTo(result)
|
||||
or
|
||||
function = cls.getStaticMethod(name) and
|
||||
cls.getAClassReference(t.continue()).getAPropertyRead(name).flowsTo(result)
|
||||
)
|
||||
or
|
||||
function = cls.getConstructor() and
|
||||
cls.getAClassReference(t.continue()).flowsTo(result)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a data flow node that refers to the given function.
|
||||
*/
|
||||
cached
|
||||
DataFlow::SourceNode getAFunctionReference(DataFlow::FunctionNode function, int imprecision) {
|
||||
result = getAFunctionReference(function, imprecision, DataFlow::TypeTracker::end())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a data flow node that refers to the result of the given partial function invocation,
|
||||
* with `function` as the underlying function.
|
||||
*/
|
||||
private
|
||||
DataFlow::SourceNode getABoundFunctionReference(DataFlow::FunctionNode function, int boundArgs, DataFlow::TypeTracker t) {
|
||||
exists(DataFlow::PartialInvokeNode partial, DataFlow::Node callback |
|
||||
result = partial.getBoundFunction(callback, boundArgs) and
|
||||
getAFunctionReference(function, 0, t.continue()).flowsTo(callback)
|
||||
)
|
||||
or
|
||||
result = getABoundFunctionReferenceAux(function, boundArgs, t)
|
||||
}
|
||||
|
||||
pragma[noopt]
|
||||
private
|
||||
DataFlow::SourceNode getABoundFunctionReferenceAux(DataFlow::FunctionNode function, int boundArgs, DataFlow::TypeTracker t) {
|
||||
exists(DataFlow::TypeTracker t2, DataFlow::SourceNode prev |
|
||||
prev = getABoundFunctionReference(function, boundArgs, t2) and
|
||||
exists(DataFlow::StepSummary summary |
|
||||
DataFlow::StepSummary::step(prev, result, summary) and
|
||||
t = t2.append(summary)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a data flow node that refers to the result of the given partial function invocation,
|
||||
* with `function` as the underlying function.
|
||||
*/
|
||||
cached
|
||||
DataFlow::SourceNode getABoundFunctionReference(DataFlow::FunctionNode function, int boundArgs) {
|
||||
result = getABoundFunctionReference(function, boundArgs, DataFlow::TypeTracker::end())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a property read that accesses the property `name` on an instance of this class.
|
||||
*
|
||||
* Concretely, this holds when the base is an instance of this class or a subclass thereof.
|
||||
*
|
||||
* This predicate may be overridden to customize the class hierarchy analysis.
|
||||
*/
|
||||
private
|
||||
DataFlow::PropRead getAnInstanceMemberAccess(DataFlow::ClassNode cls, string name, DataFlow::TypeTracker t) {
|
||||
result = cls.getAnInstanceReference(t.continue()).getAPropertyRead(name)
|
||||
or
|
||||
exists(DataFlow::ClassNode subclass |
|
||||
result = getAnInstanceMemberAccess(subclass, name, t) and
|
||||
not exists(subclass.getAnInstanceMember(name)) and
|
||||
cls = subclass.getADirectSuperClass()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -109,7 +109,7 @@ private module CachedSteps {
|
||||
* This only holds for explicitly modeled partial calls.
|
||||
*/
|
||||
private predicate partiallyCalls(
|
||||
DataFlow::AdditionalPartialInvokeNode invk, DataFlow::AnalyzedNode callback, Function f
|
||||
DataFlow::PartialInvokeNode invk, DataFlow::AnalyzedNode callback, Function f
|
||||
) {
|
||||
invk.isPartialArgument(callback, _, _) and
|
||||
exists(AbstractFunction callee | callee = callback.getAValue() |
|
||||
|
||||
Reference in New Issue
Block a user