Skip to content

Commit 869c6d2

Browse files
committed
JS: Add implied receiver steps
1 parent 74dbc71 commit 869c6d2

File tree

3 files changed

+34
-4
lines changed

3 files changed

+34
-4
lines changed

javascript/ql/lib/semmle/javascript/dataflow/Configuration.qll

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -806,6 +806,10 @@ private predicate basicFlowStepNoBarrier(
806806
callStep(pred, succ) and
807807
summary = PathSummary::call()
808808
or
809+
// Implied receiver flow
810+
CallGraph::impliedReceiverStep(pred, succ) and
811+
summary = PathSummary::call()
812+
or
809813
// Flow out of function
810814
returnStep(pred, succ) and
811815
summary = PathSummary::return()

javascript/ql/lib/semmle/javascript/dataflow/internal/CallGraphs.qll

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -241,22 +241,26 @@ module CallGraph {
241241
)
242242
}
243243

244-
private predicate shouldTrackObjectWithMethods(DataFlow::SourceNode node) {
244+
private DataFlow::FunctionNode getAMethodOnPlainObject(DataFlow::SourceNode node) {
245245
(
246246
(
247247
node instanceof DataFlow::ObjectLiteralNode
248248
or
249249
node instanceof DataFlow::FunctionNode
250250
) and
251-
node.getAPropertySource() instanceof DataFlow::FunctionNode
251+
result = node.getAPropertySource()
252252
or
253-
exists(node.(DataFlow::ObjectLiteralNode).getPropertyGetter(_))
253+
result = node.(DataFlow::ObjectLiteralNode).getPropertyGetter(_)
254254
or
255-
exists(node.(DataFlow::ObjectLiteralNode).getPropertySetter(_))
255+
result = node.(DataFlow::ObjectLiteralNode).getPropertySetter(_)
256256
) and
257257
not node.getTopLevel().isExterns()
258258
}
259259

260+
private predicate shouldTrackObjectWithMethods(DataFlow::SourceNode node) {
261+
exists(getAMethodOnPlainObject(node))
262+
}
263+
260264
/**
261265
* Gets a step summary for tracking object literals.
262266
*
@@ -273,4 +277,22 @@ module CallGraph {
273277
or
274278
StepSummary::step(getAnAllocationSiteRef(node), result, objectWithMethodsStep())
275279
}
280+
281+
/**
282+
* Holds if `pred` is assumed to flow to `succ` because a method is stored on an object that is assumed
283+
* to be the receiver of calls to that method.
284+
*
285+
* For example, object literal below is assumed to flow to the receiver of the `foo` function:
286+
* ```js
287+
* let obj = {};
288+
* obj.foo = function() {}
289+
* ```
290+
*/
291+
cached
292+
predicate impliedReceiverStep(DataFlow::SourceNode pred, DataFlow::SourceNode succ) {
293+
exists(DataFlow::SourceNode host |
294+
pred = getAnAllocationSiteRef(host) and
295+
succ = getAMethodOnPlainObject(host).getReceiver()
296+
)
297+
}
276298
}

javascript/ql/lib/semmle/javascript/dataflow/internal/StepSummary.qll

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,10 @@ private module Cached {
9494
DataFlow::localFieldStep(pred, succ) and
9595
summary = LevelStep()
9696
or
97+
// Implied flow of host object into 'this' of a method
98+
CallGraph::impliedReceiverStep(pred, succ) and
99+
summary = CallStep()
100+
or
97101
exists(string prop |
98102
basicStoreStep(pred, succ, prop) and
99103
summary = StoreStep(prop)

0 commit comments

Comments
 (0)