Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 3853da0

Browse files
author
Max Schaefer
committed
JavaScript: Teach API-graphs about bound arguments.
1 parent ecab17a commit 3853da0

5 files changed

Lines changed: 76 additions & 19 deletions

File tree

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

Lines changed: 45 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ module API {
207207
this = Impl::MkUse(result) or
208208
this = Impl::MkDef(result) or
209209
this = Impl::MkAsyncFuncResult(result) or
210-
this = Impl::MkSyntheticCallbackArg(_, result)
210+
this = Impl::MkSyntheticCallbackArg(_, _, result)
211211
}
212212

213213
/**
@@ -391,8 +391,8 @@ module API {
391391
not n.isRoot() and
392392
isUsed(n)
393393
} or
394-
MkSyntheticCallbackArg(DataFlow::Node src, DataFlow::InvokeNode nd) {
395-
trackUseNode(src, true).flowsTo(nd.getCalleeNode())
394+
MkSyntheticCallbackArg(DataFlow::Node src, int bound, DataFlow::InvokeNode nd) {
395+
trackUseNode(src, true, bound).flowsTo(nd.getCalleeNode())
396396
}
397397

398398
class TDef = MkModuleDef or TNonModuleDef;
@@ -528,18 +528,20 @@ module API {
528528
* The receiver is considered to be argument -1.
529529
*/
530530
private predicate argumentPassing(TApiNode base, int i, DataFlow::Node arg) {
531-
exists(DataFlow::Node use, DataFlow::SourceNode pred |
532-
use(base, use) and pred = trackUseNode(use, _)
531+
exists(DataFlow::Node use, DataFlow::SourceNode pred, int bound |
532+
use(base, use) and pred = trackUseNode(use, _, bound)
533533
|
534-
arg = pred.getAnInvocation().getArgument(i)
534+
arg = pred.getAnInvocation().getArgument(i - bound)
535535
or
536536
arg = pred.getACall().getReceiver() and
537+
bound = 0 and
537538
i = -1
538539
or
539540
exists(DataFlow::PartialInvokeNode pin, DataFlow::Node callback | pred.flowsTo(callback) |
540-
pin.isPartialArgument(callback, arg, i)
541+
pin.isPartialArgument(callback, arg, i - bound)
541542
or
542543
arg = pin.getBoundReceiver(callback) and
544+
bound = 0 and
543545
i = -1
544546
)
545547
)
@@ -616,7 +618,7 @@ module API {
616618
)
617619
or
618620
exists(DataFlow::InvokeNode call |
619-
base = MkSyntheticCallbackArg(_, call) and
621+
base = MkSyntheticCallbackArg(_, _, call) and
620622
lbl = Label::parameter(1) and
621623
ref = awaited(call)
622624
)
@@ -683,34 +685,55 @@ module API {
683685
)
684686
}
685687

688+
/**
689+
* Gets a data-flow node to which `nd`, which is a use of an API-graph node, flows.
690+
*
691+
* The flow from `nd` to that node may be inter-procedural. If `promisified` is `true`, the
692+
* flow goes through a promisification, and `boundArgs` indicates how many arguments have been
693+
* bound throughout the flow. (To ensure termination, we somewhat arbitrarily constrain the
694+
* number of bound arguments to be at most ten.)
695+
*/
686696
private DataFlow::SourceNode trackUseNode(
687-
DataFlow::SourceNode nd, boolean promisified, DataFlow::TypeTracker t
697+
DataFlow::SourceNode nd, boolean promisified, int boundArgs, DataFlow::TypeTracker t
688698
) {
689699
t.start() and
690700
use(_, nd) and
691701
result = nd and
692-
promisified = false
702+
promisified = false and
703+
boundArgs = 0
693704
or
694705
exists(DataFlow::CallNode promisify |
695706
promisify = API::moduleImport(["util", "bluebird"]).getMember("promisify").getACall()
696707
|
697-
trackUseNode(nd, false, t.continue()).flowsTo(promisify.getArgument(0)) and
708+
trackUseNode(nd, false, boundArgs, t.continue()).flowsTo(promisify.getArgument(0)) and
698709
promisified = true and
699710
result = promisify
700711
)
701712
or
702-
exists(DataFlow::TypeTracker t2 | result = trackUseNode(nd, promisified, t2).track(t2, t))
713+
exists(DataFlow::PartialInvokeNode pin, DataFlow::Node pred, int predBoundArgs |
714+
trackUseNode(nd, promisified, predBoundArgs, t.continue()).flowsTo(pred) and
715+
result = pin.getBoundFunction(pred, boundArgs - predBoundArgs) and
716+
boundArgs <= 10
717+
)
718+
or
719+
exists(DataFlow::TypeTracker t2 |
720+
result = trackUseNode(nd, promisified, boundArgs, t2).track(t2, t)
721+
)
703722
}
704723

705-
private DataFlow::SourceNode trackUseNode(DataFlow::SourceNode nd, boolean promisified) {
706-
result = trackUseNode(nd, promisified, DataFlow::TypeTracker::end())
724+
private DataFlow::SourceNode trackUseNode(
725+
DataFlow::SourceNode nd, boolean promisified, int boundArgs
726+
) {
727+
result = trackUseNode(nd, promisified, boundArgs, DataFlow::TypeTracker::end())
707728
}
708729

709730
/**
710731
* Gets a node that is inter-procedurally reachable from `nd`, which is a use of some node.
711732
*/
712733
cached
713-
DataFlow::SourceNode trackUseNode(DataFlow::SourceNode nd) { result = trackUseNode(nd, false) }
734+
DataFlow::SourceNode trackUseNode(DataFlow::SourceNode nd) {
735+
result = trackUseNode(nd, false, 0)
736+
}
714737

715738
private DataFlow::SourceNode trackDefNode(DataFlow::Node nd, DataFlow::TypeBackTracker t) {
716739
t.start() and
@@ -740,11 +763,14 @@ module API {
740763

741764
private DataFlow::SourceNode awaited(DataFlow::InvokeNode call, DataFlow::TypeTracker t) {
742765
t.startInPromise() and
743-
exists(MkSyntheticCallbackArg(_, call))
766+
exists(MkSyntheticCallbackArg(_, _, call))
744767
or
745768
exists(DataFlow::TypeTracker t2 | result = awaited(call, t2).track(t2, t))
746769
}
747770

771+
/**
772+
* Gets a node holding the resolved value of promise `call`.
773+
*/
748774
private DataFlow::Node awaited(DataFlow::InvokeNode call) {
749775
result = awaited(call, DataFlow::TypeTracker::end())
750776
}
@@ -810,10 +836,10 @@ module API {
810836
succ = MkAsyncFuncResult(f)
811837
)
812838
or
813-
exists(DataFlow::SourceNode src, DataFlow::InvokeNode call |
839+
exists(DataFlow::SourceNode src, int bound, DataFlow::InvokeNode call |
814840
use(pred, src) and
815-
lbl = Label::parameter(call.getNumArgument()) and
816-
succ = MkSyntheticCallbackArg(src, call)
841+
lbl = Label::parameter(bound + call.getNumArgument()) and
842+
succ = MkSyntheticCallbackArg(src, bound, call)
817843
)
818844
}
819845

javascript/ql/test/ApiGraphs/bound-args/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: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import bar from 'foo';
2+
3+
let boundbar = bar.bind(
4+
"receiver", // def (parameter -1 (member default (member exports (module foo))))
5+
"firstarg" // def (parameter 0 (member default (member exports (module foo))))
6+
);
7+
boundbar(
8+
"secondarg" // def (parameter 1 (member default (member exports (module foo))))
9+
)
10+
11+
let boundbar2 = boundbar.bind(
12+
"ignored", // !def (parameter -1 (member default (member exports (module foo))))
13+
"othersecondarg" // def (parameter 1 (member default (member exports (module foo))))
14+
)
15+
boundbar2(
16+
"thirdarg" // def (parameter 2 (member default (member exports (module foo))))
17+
)
18+
19+
let bar2 = bar;
20+
for (var i = 0; i < 2; ++i)
21+
bar2 = bar2.bind(
22+
null,
23+
i /* def (parameter 1 (member default (member exports (module foo)))) */ /* def (parameter 9 (member default (member exports (module foo)))) */
24+
);
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"name": "bound-args",
3+
"dependencies": {
4+
"foo": "*"
5+
}
6+
}

0 commit comments

Comments
 (0)