Skip to content

Commit 10c9b74

Browse files
authored
Merge pull request #20586 from asgerf/js/api-graphs-block-this
JS: Restrict receiver-flow in API graphs
2 parents 2918d30 + 587ad5c commit 10c9b74

File tree

7 files changed

+41
-3
lines changed

7 files changed

+41
-3
lines changed

javascript/ql/lib/semmle/javascript/ApiGraphs.qll

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1324,7 +1324,9 @@ module API {
13241324
exists(DataFlow::TypeTracker t, StepSummary summary, DataFlow::SourceNode prev |
13251325
prev = trackUseNode(nd, promisified, boundArgs, prop, t) and
13261326
StepSummary::step(prev, res, summary) and
1327-
result = t.append(summary)
1327+
result = t.append(summary) and
1328+
// Block argument-passing into 'this' when it determines the call target
1329+
not summary = CallReceiverStep()
13281330
)
13291331
}
13301332

@@ -1381,7 +1383,9 @@ module API {
13811383
exists(DataFlow::TypeBackTracker t, StepSummary summary, DataFlow::Node next |
13821384
next = trackDefNode(nd, t) and
13831385
StepSummary::step(prev, next, summary) and
1384-
result = t.prepend(summary)
1386+
result = t.prepend(summary) and
1387+
// Block argument-passing steps from 'this' back to a receiver when it determines the call target
1388+
not summary = CallReceiverStep()
13851389
)
13861390
}
13871391

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ class TypeTracker extends TTypeTracker {
6565
or
6666
step = CallStep() and result = MkTypeTracker(true, prop)
6767
or
68+
step = CallReceiverStep() and result = MkTypeTracker(true, prop)
69+
or
6870
step = ReturnStep() and hasCall = false and result = this
6971
or
7072
step = LoadStep(prop) and result = MkTypeTracker(hasCall, "")
@@ -238,6 +240,8 @@ class TypeBackTracker extends TTypeBackTracker {
238240
or
239241
step = CallStep() and hasReturn = false and result = this
240242
or
243+
step = CallReceiverStep() and hasReturn = false and result = this
244+
or
241245
step = ReturnStep() and result = MkTypeBackTracker(true, prop)
242246
or
243247
exists(string p | step = LoadStep(p) and prop = "" and result = MkTypeBackTracker(hasReturn, p))

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

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ private module Cached {
4343
newtype TStepSummary =
4444
LevelStep() or
4545
CallStep() or
46+
CallReceiverStep() or
4647
ReturnStep() or
4748
StoreStep(PropertyName prop) or
4849
LoadStep(PropertyName prop) or
@@ -101,6 +102,15 @@ private module Cached {
101102
)
102103
}
103104

105+
pragma[nomagic]
106+
private predicate isReceiverForMethodDispatch(DataFlow::Node node) {
107+
exists(DataFlow::SourceNode base, DataFlow::CallNode invoke |
108+
node = invoke.getReceiver() and
109+
base = node.getALocalSource() and
110+
invoke.getCalleeNode() = base.getAPropertyRead()
111+
)
112+
}
113+
104114
/**
105115
* INTERNAL: Use `TypeBackTracker.smallstep()` instead.
106116
*/
@@ -116,7 +126,11 @@ private module Cached {
116126
or
117127
// Flow into function
118128
callStep(pred, succ) and
119-
summary = CallStep()
129+
(
130+
if isReceiverForMethodDispatch(pred)
131+
then summary = CallReceiverStep()
132+
else summary = CallStep()
133+
)
120134
or
121135
// Flow out of function
122136
returnStep(pred, succ) and
@@ -251,6 +265,8 @@ class StepSummary extends TStepSummary {
251265
or
252266
this instanceof CallStep and result = "call"
253267
or
268+
this instanceof CallReceiverStep and result = "call-receiver"
269+
or
254270
this instanceof ReturnStep and result = "return"
255271
or
256272
exists(string prop | this = StoreStep(prop) | result = "store " + prop)

javascript/ql/test/ApiGraphs/explicit-this/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: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"name": "explicit-this",
3+
"dependencies": {
4+
"something": "*"
5+
}
6+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
const lib = require('something');
2+
3+
function f() {
4+
this.two(); /** use=moduleImport("something").getMember("exports").getMember("one").getMember("two").getReturn() */
5+
}
6+
7+
f.call(lib.one);

0 commit comments

Comments
 (0)