From 759e1dfe45e36433ec5ed80a6e5816aedee6c90e Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Wed, 19 Feb 2020 22:13:24 +0000 Subject: [PATCH 01/10] JS: Add helper library for call graph exploration --- .../semmle/javascript/explore/CallGraph.qll | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 javascript/ql/src/semmle/javascript/explore/CallGraph.qll diff --git a/javascript/ql/src/semmle/javascript/explore/CallGraph.qll b/javascript/ql/src/semmle/javascript/explore/CallGraph.qll new file mode 100644 index 000000000000..0ad15b6c582c --- /dev/null +++ b/javascript/ql/src/semmle/javascript/explore/CallGraph.qll @@ -0,0 +1,85 @@ +/** + * 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'" + * ``` + */ +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. + */ +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 contains no calls to other functions. */ +private FunctionNode leafFunction() { + not callEdge(result, _) +} + +/** + * Holds if `invoke` should be used as the starting 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()) +} From 4c9ef8c5701f5e8f2731546c2d70bbe3afc8a0b2 Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 20 Feb 2020 12:32:26 +0000 Subject: [PATCH 02/10] Update javascript/ql/src/semmle/javascript/explore/CallGraph.qll Co-Authored-By: Max Schaefer <54907921+max-schaefer@users.noreply.github.com> --- javascript/ql/src/semmle/javascript/explore/CallGraph.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/src/semmle/javascript/explore/CallGraph.qll b/javascript/ql/src/semmle/javascript/explore/CallGraph.qll index 0ad15b6c582c..17dd9a421eb3 100644 --- a/javascript/ql/src/semmle/javascript/explore/CallGraph.qll +++ b/javascript/ql/src/semmle/javascript/explore/CallGraph.qll @@ -71,7 +71,7 @@ predicate isStartOfCallPath(InvokeNode invoke) { not callEdge+(rootCall(), invoke) } -/** Gets a function contains no calls to other functions. */ +/** Gets a function that contains no calls to other functions. */ private FunctionNode leafFunction() { not callEdge(result, _) } From 291ebccfeff6eb58d2faa27e348a17ece444f71a Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 20 Feb 2020 12:32:33 +0000 Subject: [PATCH 03/10] Update javascript/ql/src/semmle/javascript/explore/CallGraph.qll Co-Authored-By: Max Schaefer <54907921+max-schaefer@users.noreply.github.com> --- javascript/ql/src/semmle/javascript/explore/CallGraph.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/src/semmle/javascript/explore/CallGraph.qll b/javascript/ql/src/semmle/javascript/explore/CallGraph.qll index 17dd9a421eb3..2095e4fd3fa2 100644 --- a/javascript/ql/src/semmle/javascript/explore/CallGraph.qll +++ b/javascript/ql/src/semmle/javascript/explore/CallGraph.qll @@ -77,7 +77,7 @@ private FunctionNode leafFunction() { } /** - * Holds if `invoke` should be used as the starting point of a call path. + * Holds if `invoke` 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. From 066549f682411a699d4280f094013b5743bdecc7 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Thu, 20 Feb 2020 12:34:00 +0000 Subject: [PATCH 04/10] JS: Fix typo in qldoc --- javascript/ql/src/semmle/javascript/explore/CallGraph.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/src/semmle/javascript/explore/CallGraph.qll b/javascript/ql/src/semmle/javascript/explore/CallGraph.qll index 2095e4fd3fa2..6bb04698110c 100644 --- a/javascript/ql/src/semmle/javascript/explore/CallGraph.qll +++ b/javascript/ql/src/semmle/javascript/explore/CallGraph.qll @@ -77,7 +77,7 @@ private FunctionNode leafFunction() { } /** - * Holds if `invoke` should be used as the end point of a call path. + * 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. From ffeda7f45a3b5ead6d616a593ec3ee6a884d39c3 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Mon, 24 Feb 2020 11:46:42 +0000 Subject: [PATCH 05/10] JS: Expand on doc a bit --- javascript/ql/src/semmle/javascript/explore/CallGraph.qll | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/javascript/ql/src/semmle/javascript/explore/CallGraph.qll b/javascript/ql/src/semmle/javascript/explore/CallGraph.qll index 6bb04698110c..faa853e0b57a 100644 --- a/javascript/ql/src/semmle/javascript/explore/CallGraph.qll +++ b/javascript/ql/src/semmle/javascript/explore/CallGraph.qll @@ -27,7 +27,8 @@ private import DataFlow * * There are edges from calls to their callees, * and from functions to their contained calls and in some cases - * their inner functions. + * 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 | From 647a3d3a60b9541f31eabe3a2aa8fb1af4be7711 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Mon, 24 Feb 2020 11:49:54 +0000 Subject: [PATCH 06/10] JS: Add note and debugging and exploration --- .../ql/src/semmle/javascript/dataflow/BackwardExploration.qll | 4 ++-- .../ql/src/semmle/javascript/dataflow/ForwardExploration.qll | 2 +- javascript/ql/src/semmle/javascript/explore/CallGraph.qll | 2 ++ 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/dataflow/BackwardExploration.qll b/javascript/ql/src/semmle/javascript/dataflow/BackwardExploration.qll index 29ed748dc689..4d08d21d0bf1 100644 --- a/javascript/ql/src/semmle/javascript/dataflow/BackwardExploration.qll +++ b/javascript/ql/src/semmle/javascript/dataflow/BackwardExploration.qll @@ -8,8 +8,8 @@ * * Data-flow exploration cannot be used with configurations depending on other configurations. * - * NOTE: This library should only be used for debugging, not in production code. Backward - * exploration in particular does not scale on non-trivial code bases and hence is of limited + * NOTE: This library should only be used for debugging and exploration, not in production code. + * Backward exploration in particular does not scale on non-trivial code bases and hence is of limited * usefulness as it stands. */ diff --git a/javascript/ql/src/semmle/javascript/dataflow/ForwardExploration.qll b/javascript/ql/src/semmle/javascript/dataflow/ForwardExploration.qll index 080152d30946..20f6b67c6e41 100644 --- a/javascript/ql/src/semmle/javascript/dataflow/ForwardExploration.qll +++ b/javascript/ql/src/semmle/javascript/dataflow/ForwardExploration.qll @@ -8,7 +8,7 @@ * * Data-flow exploration cannot be used with configurations depending on other configurations. * - * NOTE: This library should only be used for debugging, not in production code. + * NOTE: This library should only be used for debugging and exploration, not in production code. */ import javascript diff --git a/javascript/ql/src/semmle/javascript/explore/CallGraph.qll b/javascript/ql/src/semmle/javascript/explore/CallGraph.qll index faa853e0b57a..ab95530dad9b 100644 --- a/javascript/ql/src/semmle/javascript/explore/CallGraph.qll +++ b/javascript/ql/src/semmle/javascript/explore/CallGraph.qll @@ -18,6 +18,8 @@ * 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 From 9e4709148bc1f75deff840e2d8856462d226738c Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Mon, 24 Feb 2020 12:28:12 +0000 Subject: [PATCH 07/10] JS: Move Forward/Backward exploration to explore folder --- .../BackwardExploration.qll => explore/BackwardDataFlow.qll} | 0 .../ForwardExploration.qll => explore/ForwardDataFlow.qll} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename javascript/ql/src/semmle/javascript/{dataflow/BackwardExploration.qll => explore/BackwardDataFlow.qll} (100%) rename javascript/ql/src/semmle/javascript/{dataflow/ForwardExploration.qll => explore/ForwardDataFlow.qll} (100%) diff --git a/javascript/ql/src/semmle/javascript/dataflow/BackwardExploration.qll b/javascript/ql/src/semmle/javascript/explore/BackwardDataFlow.qll similarity index 100% rename from javascript/ql/src/semmle/javascript/dataflow/BackwardExploration.qll rename to javascript/ql/src/semmle/javascript/explore/BackwardDataFlow.qll diff --git a/javascript/ql/src/semmle/javascript/dataflow/ForwardExploration.qll b/javascript/ql/src/semmle/javascript/explore/ForwardDataFlow.qll similarity index 100% rename from javascript/ql/src/semmle/javascript/dataflow/ForwardExploration.qll rename to javascript/ql/src/semmle/javascript/explore/ForwardDataFlow.qll From 39920c1b08b45fb941d92b9723d84c03f7901dfd Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Mon, 24 Feb 2020 12:28:48 +0000 Subject: [PATCH 08/10] JS: Add forwarding libraries in old locations --- .../src/semmle/javascript/dataflow/BackwardExploration.qll | 5 +++++ .../ql/src/semmle/javascript/dataflow/ForwardExploration.qll | 5 +++++ 2 files changed, 10 insertions(+) create mode 100644 javascript/ql/src/semmle/javascript/dataflow/BackwardExploration.qll create mode 100644 javascript/ql/src/semmle/javascript/dataflow/ForwardExploration.qll diff --git a/javascript/ql/src/semmle/javascript/dataflow/BackwardExploration.qll b/javascript/ql/src/semmle/javascript/dataflow/BackwardExploration.qll new file mode 100644 index 000000000000..54d7927a7f63 --- /dev/null +++ b/javascript/ql/src/semmle/javascript/dataflow/BackwardExploration.qll @@ -0,0 +1,5 @@ +/** + * Alias for the library `semmle.javascript.explore.BackwardDataFlow`. + */ + +import semmle.javascript.explore.BackwardDataFlow diff --git a/javascript/ql/src/semmle/javascript/dataflow/ForwardExploration.qll b/javascript/ql/src/semmle/javascript/dataflow/ForwardExploration.qll new file mode 100644 index 000000000000..44667581eab3 --- /dev/null +++ b/javascript/ql/src/semmle/javascript/dataflow/ForwardExploration.qll @@ -0,0 +1,5 @@ +/** + * Alias for the library `semmle.javascript.explore.ForwardDataFlow`. + */ + +import semmle.javascript.explore.ForwardDataFlow From c04ba91a90d449cd5e8b8c8a4640e900e0fde117 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Sat, 7 Mar 2020 15:28:25 +0000 Subject: [PATCH 09/10] JS: Autoformat --- .../semmle/javascript/explore/CallGraph.qll | 43 +++++++++---------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/explore/CallGraph.qll b/javascript/ql/src/semmle/javascript/explore/CallGraph.qll index ab95530dad9b..4b7853245603 100644 --- a/javascript/ql/src/semmle/javascript/explore/CallGraph.qll +++ b/javascript/ql/src/semmle/javascript/explore/CallGraph.qll @@ -21,6 +21,7 @@ * * NOTE: This library should only be used for debugging and exploration, not in production code. */ + import javascript private import DataFlow @@ -33,22 +34,22 @@ private import DataFlow * 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 - ) + exists(InvokeNode invoke, Function f | + invoke.getACallee() = f and + pred = invoke and + succ = f.flow() or - exists(Function inner, Function outer | - inner.getEnclosingContainer() = outer and - not inner = outer.getAReturnedExpr() and - pred = outer.flow() and - succ = inner.flow() - ) + 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. */ @@ -56,14 +57,12 @@ 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 + 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() -} +private InvokeNode rootCall() { not any(InvokeNode i).getACallee() = result.getContainer() } /** * Holds if `invoke` should be used as the starting point of a call path. @@ -75,9 +74,7 @@ predicate isStartOfCallPath(InvokeNode invoke) { } /** Gets a function that contains no calls to other functions. */ -private FunctionNode leafFunction() { - not callEdge(result, _) -} +private FunctionNode leafFunction() { not callEdge(result, _) } /** * Holds if `fun` should be used as the end point of a call path. From 18188b659ca48dfee0bb7dab49d83e1354eb5aac Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Tue, 21 Apr 2020 10:53:37 +0100 Subject: [PATCH 10/10] JS: Add 1.25 change note --- change-notes/1.25/analysis-javascript.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/change-notes/1.25/analysis-javascript.md b/change-notes/1.25/analysis-javascript.md index a49aa9230b16..123f9bbb64ef 100644 --- a/change-notes/1.25/analysis-javascript.md +++ b/change-notes/1.25/analysis-javascript.md @@ -19,4 +19,4 @@ ## Changes to libraries - +* A library `semmle.javascript.explore.CallGraph` has been added to help write queries for exploring the call graph.