diff --git a/javascript/ql/lib/semmle/javascript/ApiGraphs.qll b/javascript/ql/lib/semmle/javascript/ApiGraphs.qll index 0bd79dd2029..0345df3f00e 100644 --- a/javascript/ql/lib/semmle/javascript/ApiGraphs.qll +++ b/javascript/ql/lib/semmle/javascript/ApiGraphs.qll @@ -891,6 +891,17 @@ module API { (propDesc = Promises::errorProp() or propDesc = "") } + pragma[nomagic] + private DataFlow::ClassNode getALocalSubclass(DataFlow::SourceNode node) { + result.getASuperClassNode().getALocalSource() = node + } + + bindingset[node] + pragma[inline_late] + private DataFlow::ClassNode getALocalSubclassFwd(DataFlow::SourceNode node) { + result = getALocalSubclass(node) + } + /** * Holds if `ref` is a use of a node that should have an incoming edge from `base` labeled * `lbl` in the API graph. @@ -927,6 +938,15 @@ module API { or lbl = Label::forwardingFunction() and DataFlow::functionForwardingStep(pred.getALocalUse(), ref) + or + exists(DataFlow::ClassNode cls | + lbl = Label::instance() and + cls = getALocalSubclassFwd(pred).getADirectSubClass*() + | + ref = cls.getAReceiverNode() + or + ref = cls.getAClassReference().getAnInstantiation() + ) ) or exists(DataFlow::Node def, DataFlow::FunctionNode fn | diff --git a/javascript/ql/test/library-tests/frameworks/data/test.expected b/javascript/ql/test/library-tests/frameworks/data/test.expected index 28d7229789d..843b1f32d5b 100644 --- a/javascript/ql/test/library-tests/frameworks/data/test.expected +++ b/javascript/ql/test/library-tests/frameworks/data/test.expected @@ -74,6 +74,10 @@ taintFlow | test.js:249:28:249:35 | source() | test.js:249:28:249:35 | source() | | test.js:252:15:252:22 | source() | test.js:252:15:252:22 | source() | | test.js:254:32:254:39 | source() | test.js:254:32:254:39 | source() | +| test.js:262:10:262:31 | this.ba ... ource() | test.js:262:10:262:31 | this.ba ... ource() | +| test.js:265:6:265:39 | new MyS ... ource() | test.js:265:6:265:39 | new MyS ... ource() | +| test.js:269:10:269:31 | this.ba ... ource() | test.js:269:10:269:31 | this.ba ... ource() | +| test.js:272:6:272:40 | new MyS ... ource() | test.js:272:6:272:40 | new MyS ... ource() | isSink | test.js:54:18:54:25 | source() | test-sink | | test.js:55:22:55:29 | source() | test-sink | diff --git a/javascript/ql/test/library-tests/frameworks/data/test.js b/javascript/ql/test/library-tests/frameworks/data/test.js index ac702b82a8c..bbcb10418a1 100644 --- a/javascript/ql/test/library-tests/frameworks/data/test.js +++ b/javascript/ql/test/library-tests/frameworks/data/test.js @@ -256,3 +256,17 @@ function fuzzy() { fuzzyCall(source()); // OK - does not come from 'testlib' require('blah').fuzzyCall(source()); // OK - does not come from 'testlib' } + +class MySubclass extends testlib.BaseClass { + foo() { + sink(this.baseclassSource()); // NOT OK + } +} +sink(new MySubclass().baseclassSource()); // NOT OK + +class MySubclass2 extends MySubclass { + foo2() { + sink(this.baseclassSource()); // NOT OK + } +} +sink(new MySubclass2().baseclassSource()); // NOT OK diff --git a/javascript/ql/test/library-tests/frameworks/data/test.ql b/javascript/ql/test/library-tests/frameworks/data/test.ql index 039a0aa3920..6af176f4bf3 100644 --- a/javascript/ql/test/library-tests/frameworks/data/test.ql +++ b/javascript/ql/test/library-tests/frameworks/data/test.ql @@ -80,6 +80,7 @@ class Sources extends ModelInput::SourceModelCsv { "testlib;Member[ParamDecoratorSource].DecoratedParameter;test-source", "testlib;Member[MethodDecorator].DecoratedMember.Parameter[0];test-source", "testlib;Member[MethodDecoratorWithArgs].ReturnValue.DecoratedMember.Parameter[0];test-source", + "testlib;Member[BaseClass].Instance.Member[baseclassSource].ReturnValue;test-source", ] } }