Skip to content

Commit f942e69

Browse files
committed
JS: Improve flow through partial invokes
1 parent 3c8aeb9 commit f942e69

File tree

5 files changed

+57
-5
lines changed

5 files changed

+57
-5
lines changed

javascript/ql/src/semmle/javascript/Closure.qll

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,9 @@ module Closure {
267267
result = this
268268
}
269269

270-
override DataFlow::Node getBoundReceiver() { result = getArgument(1) }
270+
override DataFlow::Node getBoundReceiver(DataFlow::Node callback) {
271+
callback = getArgument(0) and
272+
result = getArgument(1)
273+
}
271274
}
272275
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1311,6 +1311,9 @@ class MidPathNode extends PathNode, MkMidNode {
13111311
or
13121312
// Skip the exceptional return on functions, as this highlights the entire function.
13131313
nd = any(DataFlow::FunctionNode f).getExceptionalReturn()
1314+
or
1315+
// Skip the synthetic 'this' node, as a ThisExpr will be the next node anyway
1316+
nd = DataFlow::thisNode(_)
13141317
}
13151318
}
13161319

javascript/ql/src/semmle/javascript/dataflow/Nodes.qll

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1199,6 +1199,13 @@ class PartialInvokeNode extends DataFlow::Node {
11991199

12001200
PartialInvokeNode() { this = range }
12011201

1202+
/** Gets a node holding a callback invoked by this partial invocation node. */
1203+
DataFlow::Node getACallbackNode() {
1204+
isPartialArgument(result, _, _)
1205+
or
1206+
exists(getBoundReceiver(result))
1207+
}
1208+
12021209
/**
12031210
* Holds if `argument` is passed as argument `index` to the function in `callback`.
12041211
*/
@@ -1216,7 +1223,12 @@ class PartialInvokeNode extends DataFlow::Node {
12161223
/**
12171224
* Gets the node holding the receiver to be passed to the bound function, if specified.
12181225
*/
1219-
DataFlow::Node getBoundReceiver() { result = range.getBoundReceiver() }
1226+
DataFlow::Node getBoundReceiver() { result = range.getBoundReceiver(_) }
1227+
1228+
/**
1229+
* Gets the node holding the receiver to be passed to the bound function, if specified.
1230+
*/
1231+
DataFlow::Node getBoundReceiver(DataFlow::Node callback) { result = range.getBoundReceiver(callback) }
12201232
}
12211233

12221234
module PartialInvokeNode {
@@ -1235,9 +1247,17 @@ module PartialInvokeNode {
12351247
DataFlow::SourceNode getBoundFunction(DataFlow::Node callback, int boundArgs) { none() }
12361248

12371249
/**
1250+
* DEPRECATED. Use the two-argument version of `getBoundReceiver` instead.
1251+
*
12381252
* Gets the node holding the receiver to be passed to the bound function, if specified.
12391253
*/
1254+
deprecated
12401255
DataFlow::Node getBoundReceiver() { none() }
1256+
1257+
/**
1258+
* Gets the node holding the receiver to be passed to `callback`.
1259+
*/
1260+
DataFlow::Node getBoundReceiver(DataFlow::Node callback) { none() }
12411261
}
12421262

12431263
/**
@@ -1264,7 +1284,8 @@ module PartialInvokeNode {
12641284
result = this
12651285
}
12661286

1267-
override DataFlow::Node getBoundReceiver() {
1287+
override DataFlow::Node getBoundReceiver(DataFlow::Node callback) {
1288+
callback = getReceiver() and
12681289
result = getArgument(0)
12691290
}
12701291
}
@@ -1309,6 +1330,22 @@ module PartialInvokeNode {
13091330
result = this
13101331
}
13111332
}
1333+
1334+
/**
1335+
* A call to `for-in` or `for-own`, passing the context parameter to the target function.
1336+
*/
1337+
class ForOwnInPartialCall extends PartialInvokeNode::Range, DataFlow::CallNode {
1338+
ForOwnInPartialCall() {
1339+
exists(string name | name = "for-in" or name = "for-own" |
1340+
this = moduleImport(name).getACall()
1341+
)
1342+
}
1343+
1344+
override DataFlow::Node getBoundReceiver(DataFlow::Node callback) {
1345+
callback = getArgument(1) and
1346+
result = getArgument(2)
1347+
}
1348+
}
13121349
}
13131350

13141351
/**

javascript/ql/src/semmle/javascript/dataflow/internal/FlowSteps.qll

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ private module CachedSteps {
9999
private predicate partiallyCalls(
100100
DataFlow::PartialInvokeNode invk, DataFlow::AnalyzedNode callback, Function f
101101
) {
102-
invk.isPartialArgument(callback, _, _) and
102+
callback = invk.getACallbackNode() and
103103
exists(AbstractFunction callee | callee = callback.getAValue() |
104104
if callback.getAValue().isIndefinite("global")
105105
then f = callee.getFunction() and f.getFile() = invk.getFile()
@@ -135,6 +135,12 @@ private module CachedSteps {
135135
not p.isRestParameter() and
136136
parm = DataFlow::parameterNode(p)
137137
)
138+
or
139+
exists(DataFlow::Node callback |
140+
arg = invk.(DataFlow::PartialInvokeNode).getBoundReceiver(callback) and
141+
partiallyCalls(invk, callback, f) and
142+
parm = DataFlow::thisNode(f)
143+
)
138144
}
139145

140146
/**

javascript/ql/src/semmle/javascript/frameworks/AngularJS/AngularJSCore.qll

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1097,5 +1097,8 @@ private class BindCall extends DataFlow::PartialInvokeNode::Range, DataFlow::Cal
10971097
result = this
10981098
}
10991099

1100-
override DataFlow::Node getBoundReceiver() { result = getArgument(0) }
1100+
override DataFlow::Node getBoundReceiver(DataFlow::Node callback) {
1101+
callback = getArgument(1) and
1102+
result = getArgument(0)
1103+
}
11011104
}

0 commit comments

Comments
 (0)