From 76e01f0055744582b3b46c5453ccf08ba056161f Mon Sep 17 00:00:00 2001 From: Max Schaefer Date: Thu, 18 Apr 2019 09:08:09 +0100 Subject: [PATCH] JavaScript: Update `TypeTracker` to align with `TypeBackTracker`. It now also has `step` and `smallstep` predicates. In the usual case, however, I think I prefer the `SourceNode::track` API, so I left the recommended style in the qldoc alone (and adjusted the one for `TypeBackTracker` to match). --- .../semmle/javascript/dataflow/Sources.qll | 5 +- .../javascript/dataflow/TypeTracking.qll | 50 +++++++++++++++++-- 2 files changed, 46 insertions(+), 9 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/dataflow/Sources.qll b/javascript/ql/src/semmle/javascript/dataflow/Sources.qll index 43c512885aad..4ea306eb80fa 100644 --- a/javascript/ql/src/semmle/javascript/dataflow/Sources.qll +++ b/javascript/ql/src/semmle/javascript/dataflow/Sources.qll @@ -164,10 +164,7 @@ class SourceNode extends DataFlow::Node { */ pragma[inline] DataFlow::SourceNode track(TypeTracker t2, TypeTracker t) { - exists(StepSummary summary | - StepSummary::step(this, result, summary) and - t = t2.append(summary) - ) + t = t2.step(this, result) } /** diff --git a/javascript/ql/src/semmle/javascript/dataflow/TypeTracking.qll b/javascript/ql/src/semmle/javascript/dataflow/TypeTracking.qll index b5637212868a..04e969fc3e90 100644 --- a/javascript/ql/src/semmle/javascript/dataflow/TypeTracking.qll +++ b/javascript/ql/src/semmle/javascript/dataflow/TypeTracking.qll @@ -101,7 +101,7 @@ private newtype TTypeTracker = MkTypeTracker(Boolean hasCall, OptionalPropertyNa * source/sink relation, that is, it may determine that a node has a given type, * but it won't determine where that type came from. * - * It is recommended that all uses of this type is written on the following form, + * It is recommended that all uses of this type are written in the following form, * for tracking some type `myType`: * ``` * DataFlow::SourceNode myType(DataFlow::TypeTracker t) { @@ -116,8 +116,12 @@ private newtype TTypeTracker = MkTypeTracker(Boolean hasCall, OptionalPropertyNa * DataFlow::SourceNode myType() { result = myType(DataFlow::TypeTracker::end()) } * ``` * - * To track values backwards, which can be useful for tracking - * the type of a callback, use the `TypeBackTracker` class instead. + * Instead of `result = myType(t2).track(t2, t)`, you can also use the equivalent + * `t = t2.step(myType(t2), result)`. If you additionally want to track individual + * intra-procedural steps, use `t = t2.smallstep(myCallback(t2), result)`. + * + * To track values backwards, which can be useful for tracking the type of a callback, + * use the `TypeBackTracker` class instead. */ class TypeTracker extends TTypeTracker { Boolean hasCall; @@ -172,6 +176,37 @@ class TypeTracker extends TTypeTracker { * This predicate is only defined if the type has not been tracked into a property. */ TypeTracker continue() { prop = "" and result = this } + + /** + * Gets the summary that corresponds to having taken a forwards + * heap and/or inter-procedural step from `pred` to `succ`. + */ + pragma[inline] + TypeTracker step(DataFlow::SourceNode pred, DataFlow::SourceNode succ) { + exists(StepSummary summary | + StepSummary::step(pred, succ, summary) and + result = this.append(summary) + ) + } + + /** + * Gets the summary that corresponds to having taken a forwards + * local, heap and/or inter-procedural step from `pred` to `succ`. + * + * Unlike `TypeTracker::step`, this predicate exposes all edges + * in the flow graph, and not just the edges between `SourceNode`s. + * It may therefore be less performant. + */ + pragma[inline] + TypeTracker smallstep(DataFlow::Node pred, DataFlow::Node succ) { + exists(StepSummary summary | + StepSummary::smallstep(pred, succ, summary) and + result = this.append(summary) + ) + or + succ = pred.getASuccessor() and + result = this + } } module TypeTracker { @@ -192,20 +227,25 @@ private newtype TTypeBackTracker = MkTypeBackTracker(Boolean hasReturn, Optional * it may determine that a node will be used in an API call somewhere, but it won't * determine exactly where that use was, or the path that led to the use. * - * It is recommended that all uses of this type is written on the following form, + * It is recommended that all uses of this type are written in the following form, * for back-tracking some callback type `myCallback`: + * * ``` * DataFlow::SourceNode myCallback(DataFlow::TypeBackTracker t) { * t.start() and * result = (< some API call >).getArgument(< n >).getALocalSource() * or * exists (DataFlow::TypeBackTracker t2 | - * t2 = t.step(result, myCallback(t2)) + * result = myCallback(t2).backtrack(t2, t) * ) * } * * DataFlow::SourceNode myCallback() { result = myCallback(DataFlow::TypeBackTracker::end()) } * ``` + * + * Instead of `result = myCallback(t2).backtrack(t2, t)`, you can also use the equivalent + * `t2 = t.step(result, myCallback(t2))`. If you additionally want to track individual + * intra-procedural steps, use `t2 = t.smallstep(result, myCallback(t2))`. */ class TypeBackTracker extends TTypeBackTracker { Boolean hasReturn;