Skip to content

Commit dfc4436

Browse files
author
Max Schaefer
committed
JavaScript: Teach API graphs to recognise arguments supplied in partial function applications.
1 parent 18bdc05 commit dfc4436

File tree

5 files changed

+45
-12
lines changed

5 files changed

+45
-12
lines changed

javascript/ql/src/semmle/javascript/ApiGraphs.qll

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -412,16 +412,9 @@ module API {
412412
rhs = f.getAReturn()
413413
)
414414
or
415-
exists(DataFlow::SourceNode src, DataFlow::InvokeNode invk |
416-
use(base, src) and invk = trackUseNode(src).getAnInvocation()
417-
|
418-
exists(int i |
419-
lbl = Label::parameter(i) and
420-
rhs = invk.getArgument(i)
421-
)
422-
or
423-
lbl = Label::receiver() and
424-
rhs = invk.(DataFlow::CallNode).getReceiver()
415+
exists(int i |
416+
lbl = Label::parameter(i) and
417+
argumentPassing(base, i, rhs)
425418
)
426419
or
427420
exists(DataFlow::SourceNode src, DataFlow::PropWrite pw |
@@ -432,6 +425,30 @@ module API {
432425
)
433426
}
434427

428+
/**
429+
* Holds if `arg` is passed as the `i`th argument to a use of `base`, either by means of a
430+
* full invocation, or in a partial function application.
431+
*
432+
* The receiver is considered to be argument -1.
433+
*/
434+
private predicate argumentPassing(TApiNode base, int i, DataFlow::Node arg) {
435+
exists(DataFlow::SourceNode use, DataFlow::SourceNode pred |
436+
use(base, use) and pred = trackUseNode(use)
437+
|
438+
arg = pred.getAnInvocation().getArgument(i)
439+
or
440+
arg = pred.getACall().getReceiver() and
441+
i = -1
442+
or
443+
exists(DataFlow::PartialInvokeNode pin, DataFlow::Node callback | pred.flowsTo(callback) |
444+
pin.isPartialArgument(callback, arg, i)
445+
or
446+
arg = pin.getBoundReceiver(callback) and
447+
i = -1
448+
)
449+
)
450+
}
451+
435452
/**
436453
* Holds if `rhs` is the right-hand side of a definition of node `nd`.
437454
*/
@@ -719,10 +736,14 @@ private module Label {
719736
bindingset[s]
720737
string parameterByStringIndex(string s) {
721738
result = "parameter " + s and
722-
s.toInt() >= 0
739+
s.toInt() >= -1
723740
}
724741

725-
/** Gets the `parameter` edge label for the `i`th parameter. */
742+
/**
743+
* Gets the `parameter` edge label for the `i`th parameter.
744+
*
745+
* The receiver is considered to be parameter -1.
746+
*/
726747
bindingset[i]
727748
string parameter(int i) { result = parameterByStringIndex(i.toString()) }
728749

javascript/ql/test/ApiGraphs/partial-invoke/VerifyAssertions.expected

Whitespace-only changes.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
import ApiGraphs.VerifyAssertions
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
const cp = require('child_process');
2+
3+
module.exports = function () {
4+
return cp.spawn.bind(
5+
cp, // def (parameter -1 (member spawn (member exports (module child_process))))
6+
"cat" // def (parameter 0 (member spawn (member exports (module child_process))))
7+
);
8+
};
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"name": "partial-invoke"
3+
}

0 commit comments

Comments
 (0)