diff --git a/javascript/ql/lib/semmle/javascript/ApiGraphs.qll b/javascript/ql/lib/semmle/javascript/ApiGraphs.qll index 850e92244512..1a96e25b3b9f 100644 --- a/javascript/ql/lib/semmle/javascript/ApiGraphs.qll +++ b/javascript/ql/lib/semmle/javascript/ApiGraphs.qll @@ -1324,7 +1324,9 @@ module API { exists(DataFlow::TypeTracker t, StepSummary summary, DataFlow::SourceNode prev | prev = trackUseNode(nd, promisified, boundArgs, prop, t) and StepSummary::step(prev, res, summary) and - result = t.append(summary) + result = t.append(summary) and + // Block argument-passing into 'this' when it determines the call target + not summary = CallReceiverStep() ) } @@ -1381,7 +1383,9 @@ module API { exists(DataFlow::TypeBackTracker t, StepSummary summary, DataFlow::Node next | next = trackDefNode(nd, t) and StepSummary::step(prev, next, summary) and - result = t.prepend(summary) + result = t.prepend(summary) and + // Block argument-passing steps from 'this' back to a receiver when it determines the call target + not summary = CallReceiverStep() ) } diff --git a/javascript/ql/lib/semmle/javascript/dataflow/TypeTracking.qll b/javascript/ql/lib/semmle/javascript/dataflow/TypeTracking.qll index 9e912a336f6f..e4c8f162972a 100644 --- a/javascript/ql/lib/semmle/javascript/dataflow/TypeTracking.qll +++ b/javascript/ql/lib/semmle/javascript/dataflow/TypeTracking.qll @@ -65,6 +65,8 @@ class TypeTracker extends TTypeTracker { or step = CallStep() and result = MkTypeTracker(true, prop) or + step = CallReceiverStep() and result = MkTypeTracker(true, prop) + or step = ReturnStep() and hasCall = false and result = this or step = LoadStep(prop) and result = MkTypeTracker(hasCall, "") @@ -238,6 +240,8 @@ class TypeBackTracker extends TTypeBackTracker { or step = CallStep() and hasReturn = false and result = this or + step = CallReceiverStep() and hasReturn = false and result = this + or step = ReturnStep() and result = MkTypeBackTracker(true, prop) or exists(string p | step = LoadStep(p) and prop = "" and result = MkTypeBackTracker(hasReturn, p)) diff --git a/javascript/ql/lib/semmle/javascript/dataflow/internal/StepSummary.qll b/javascript/ql/lib/semmle/javascript/dataflow/internal/StepSummary.qll index 2bcd89130a9c..fed492074b6a 100644 --- a/javascript/ql/lib/semmle/javascript/dataflow/internal/StepSummary.qll +++ b/javascript/ql/lib/semmle/javascript/dataflow/internal/StepSummary.qll @@ -43,6 +43,7 @@ private module Cached { newtype TStepSummary = LevelStep() or CallStep() or + CallReceiverStep() or ReturnStep() or StoreStep(PropertyName prop) or LoadStep(PropertyName prop) or @@ -101,6 +102,15 @@ private module Cached { ) } + pragma[nomagic] + private predicate isReceiverForMethodDispatch(DataFlow::Node node) { + exists(DataFlow::SourceNode base, DataFlow::CallNode invoke | + node = invoke.getReceiver() and + base = node.getALocalSource() and + invoke.getCalleeNode() = base.getAPropertyRead() + ) + } + /** * INTERNAL: Use `TypeBackTracker.smallstep()` instead. */ @@ -116,7 +126,11 @@ private module Cached { or // Flow into function callStep(pred, succ) and - summary = CallStep() + ( + if isReceiverForMethodDispatch(pred) + then summary = CallReceiverStep() + else summary = CallStep() + ) or // Flow out of function returnStep(pred, succ) and @@ -251,6 +265,8 @@ class StepSummary extends TStepSummary { or this instanceof CallStep and result = "call" or + this instanceof CallReceiverStep and result = "call-receiver" + or this instanceof ReturnStep and result = "return" or exists(string prop | this = StoreStep(prop) | result = "store " + prop) diff --git a/javascript/ql/test/ApiGraphs/explicit-this/VerifyAssertions.expected b/javascript/ql/test/ApiGraphs/explicit-this/VerifyAssertions.expected new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/javascript/ql/test/ApiGraphs/explicit-this/VerifyAssertions.ql b/javascript/ql/test/ApiGraphs/explicit-this/VerifyAssertions.ql new file mode 100644 index 000000000000..b9c54e26072d --- /dev/null +++ b/javascript/ql/test/ApiGraphs/explicit-this/VerifyAssertions.ql @@ -0,0 +1 @@ +import ApiGraphs.VerifyAssertions diff --git a/javascript/ql/test/ApiGraphs/explicit-this/package.json b/javascript/ql/test/ApiGraphs/explicit-this/package.json new file mode 100644 index 000000000000..f48acd623603 --- /dev/null +++ b/javascript/ql/test/ApiGraphs/explicit-this/package.json @@ -0,0 +1,6 @@ +{ + "name": "explicit-this", + "dependencies": { + "something": "*" + } +} diff --git a/javascript/ql/test/ApiGraphs/explicit-this/tst.js b/javascript/ql/test/ApiGraphs/explicit-this/tst.js new file mode 100644 index 000000000000..a3f5ecff21e4 --- /dev/null +++ b/javascript/ql/test/ApiGraphs/explicit-this/tst.js @@ -0,0 +1,7 @@ +const lib = require('something'); + +function f() { + this.two(); /** use=moduleImport("something").getMember("exports").getMember("one").getMember("two").getReturn() */ +} + +f.call(lib.one);