mirror of
https://github.com/github/codeql.git
synced 2026-02-05 01:31:08 +01:00
86 lines
2.7 KiB
Plaintext
86 lines
2.7 KiB
Plaintext
/**
|
|
* Provides predicates for visualizing the call paths leading to or from a specific function.
|
|
*
|
|
* It defines three predicates: `callEdge`, `isStartOfCallPath` and `isEndOfCallPath`,
|
|
* as well as the `nodes` and `edges` predicates needed for a path problem query.
|
|
*
|
|
* To use this library, make sure the query has `@kind path-problem`
|
|
* and selects columns appropriate for a path problem query.
|
|
* For example:
|
|
* ```
|
|
* import javascript
|
|
* import semmle.javascript.explore.CallGraph
|
|
* import DataFlow
|
|
*
|
|
* from InvokeNode invoke, FunctionNode function
|
|
* where callEdge*(invoke, function)
|
|
* and isStartOfCallPath(invoke)
|
|
* and function.getName() = "targetFunction"
|
|
* select invoke, invoke, function, "Call path to 'targetFunction'"
|
|
* ```
|
|
*
|
|
* NOTE: This library should only be used for debugging and exploration, not in production code.
|
|
*/
|
|
|
|
import javascript
|
|
private import DataFlow
|
|
|
|
/**
|
|
* Holds if `pred -> succ` is an edge in the call graph.
|
|
*
|
|
* There are edges from calls to their callees,
|
|
* and from functions to their contained calls and in some cases
|
|
* their inner functions to model functions invoked indirectly
|
|
* by being passed to another call.
|
|
*/
|
|
predicate callEdge(Node pred, Node succ) {
|
|
exists(InvokeNode invoke, Function f |
|
|
invoke.getACallee() = f and
|
|
pred = invoke and
|
|
succ = f.flow()
|
|
or
|
|
invoke.getContainer() = f and
|
|
pred = f.flow() and
|
|
succ = invoke
|
|
)
|
|
or
|
|
exists(Function inner, Function outer |
|
|
inner.getEnclosingContainer() = outer and
|
|
not inner = outer.getAReturnedExpr() and
|
|
pred = outer.flow() and
|
|
succ = inner.flow()
|
|
)
|
|
}
|
|
|
|
/** Holds if `pred -> succ` is an edge in the call graph. */
|
|
query predicate edges = callEdge/2;
|
|
|
|
/** Holds if `node` is part of the call graph. */
|
|
query predicate nodes(Node node) {
|
|
node instanceof InvokeNode or
|
|
node instanceof FunctionNode
|
|
}
|
|
|
|
/** Gets a call in a function that has no known call sites. */
|
|
private InvokeNode rootCall() { not any(InvokeNode i).getACallee() = result.getContainer() }
|
|
|
|
/**
|
|
* Holds if `invoke` should be used as the starting point of a call path.
|
|
*/
|
|
predicate isStartOfCallPath(InvokeNode invoke) {
|
|
// `invoke` should either be a root call or be part of a cycle with no root.
|
|
// An equivalent requirement is that `invoke` is not reachable from a root.
|
|
not callEdge+(rootCall(), invoke)
|
|
}
|
|
|
|
/** Gets a function that contains no calls to other functions. */
|
|
private FunctionNode leafFunction() { not callEdge(result, _) }
|
|
|
|
/**
|
|
* Holds if `fun` should be used as the end point of a call path.
|
|
*/
|
|
predicate isEndOfCallPath(FunctionNode fun) {
|
|
// `fun` should either be a leaf function or part of a cycle with no leaves.
|
|
not callEdge+(fun, leafFunction())
|
|
}
|