Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions javascript/ql/src/semmle/javascript/dataflow/Sources.qll
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

/**
Expand Down
50 changes: 45 additions & 5 deletions javascript/ql/src/semmle/javascript/dataflow/TypeTracking.qll
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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;
Expand Down Expand Up @@ -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 {
Expand All @@ -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;
Expand Down