Skip to content

Commit f08e8b1

Browse files
authored
Merge pull request github#16136 from asgerf/js/instance-to-subclasses
JS: Make getInstance() propagate to subclasses
2 parents ad1139d + ad9838d commit f08e8b1

File tree

6 files changed

+60
-8
lines changed

6 files changed

+60
-8
lines changed

docs/codeql/codeql-language-guides/customizing-library-models-for-javascript.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -478,7 +478,7 @@ The following components are supported:
478478
- **Element** selects an element of an array, iterator, or set object.
479479
- **MapValue** selects a value of a map object.
480480
- **Awaited** selects the value of a promise.
481-
- **Instance** selects instances of a class.
481+
- **Instance** selects instances of a class, including instances of its subclasses.
482482
- **Fuzzy** selects all values that are derived from the current value through a combination of the other operations described in this list.
483483
For example, this can be used to find all values that appear to originate from a particular package. This can be useful for finding method calls
484484
from a known package, but where the receiver type is not known or is difficult to model.

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

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -241,15 +241,23 @@ module API {
241241
}
242242

243243
/**
244-
* Gets a node representing an instance of this API component, that is, an object whose
245-
* constructor is the function represented by this node.
244+
* Gets a node representing an instance of the class represented by this node.
245+
* This includes instances of subclasses.
246246
*
247-
* For example, if this node represents a use of some class `A`, then there might be a node
248-
* representing instances of `A`, typically corresponding to expressions `new A()` at the
249-
* source level.
247+
* For example:
248+
* ```js
249+
* import { C } from "foo";
250+
*
251+
* new C(); // API::moduleImport("foo").getMember("C").getInstance()
252+
*
253+
* class D extends C {
254+
* m() {
255+
* this; // API::moduleImport("foo").getMember("C").getInstance()
256+
* }
257+
* }
250258
*
251-
* This predicate may have multiple results when there are multiple constructor calls invoking this API component.
252-
* Consider using `getAnInstantiation()` if there is a need to distinguish between individual constructor calls.
259+
* new D(); // API::moduleImport("foo").getMember("C").getInstance()
260+
* ```
253261
*/
254262
cached
255263
Node getInstance() {
@@ -891,6 +899,17 @@ module API {
891899
(propDesc = Promises::errorProp() or propDesc = "")
892900
}
893901

902+
pragma[nomagic]
903+
private DataFlow::ClassNode getALocalSubclass(DataFlow::SourceNode node) {
904+
result.getASuperClassNode().getALocalSource() = node
905+
}
906+
907+
bindingset[node]
908+
pragma[inline_late]
909+
private DataFlow::ClassNode getALocalSubclassFwd(DataFlow::SourceNode node) {
910+
result = getALocalSubclass(node)
911+
}
912+
894913
/**
895914
* Holds if `ref` is a use of a node that should have an incoming edge from `base` labeled
896915
* `lbl` in the API graph.
@@ -927,6 +946,15 @@ module API {
927946
or
928947
lbl = Label::forwardingFunction() and
929948
DataFlow::functionForwardingStep(pred.getALocalUse(), ref)
949+
or
950+
exists(DataFlow::ClassNode cls |
951+
lbl = Label::instance() and
952+
cls = getALocalSubclassFwd(pred).getADirectSubClass*()
953+
|
954+
ref = cls.getAReceiverNode()
955+
or
956+
ref = cls.getAClassReference().getAnInstantiation()
957+
)
930958
)
931959
or
932960
exists(DataFlow::Node def, DataFlow::FunctionNode fn |
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* `API::Node#getInstance()` now includes instances of subclasses, include transitive subclasses.
5+
The same changes applies to uses of the `Instance` token in data extensions.

javascript/ql/test/library-tests/frameworks/data/test.expected

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@ taintFlow
7474
| test.js:249:28:249:35 | source() | test.js:249:28:249:35 | source() |
7575
| test.js:252:15:252:22 | source() | test.js:252:15:252:22 | source() |
7676
| test.js:254:32:254:39 | source() | test.js:254:32:254:39 | source() |
77+
| test.js:262:10:262:31 | this.ba ... ource() | test.js:262:10:262:31 | this.ba ... ource() |
78+
| test.js:265:6:265:39 | new MyS ... ource() | test.js:265:6:265:39 | new MyS ... ource() |
79+
| test.js:269:10:269:31 | this.ba ... ource() | test.js:269:10:269:31 | this.ba ... ource() |
80+
| test.js:272:6:272:40 | new MyS ... ource() | test.js:272:6:272:40 | new MyS ... ource() |
7781
isSink
7882
| test.js:54:18:54:25 | source() | test-sink |
7983
| test.js:55:22:55:29 | source() | test-sink |

javascript/ql/test/library-tests/frameworks/data/test.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,3 +256,17 @@ function fuzzy() {
256256
fuzzyCall(source()); // OK - does not come from 'testlib'
257257
require('blah').fuzzyCall(source()); // OK - does not come from 'testlib'
258258
}
259+
260+
class MySubclass extends testlib.BaseClass {
261+
foo() {
262+
sink(this.baseclassSource()); // NOT OK
263+
}
264+
}
265+
sink(new MySubclass().baseclassSource()); // NOT OK
266+
267+
class MySubclass2 extends MySubclass {
268+
foo2() {
269+
sink(this.baseclassSource()); // NOT OK
270+
}
271+
}
272+
sink(new MySubclass2().baseclassSource()); // NOT OK

javascript/ql/test/library-tests/frameworks/data/test.ql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ class Sources extends ModelInput::SourceModelCsv {
8080
"testlib;Member[ParamDecoratorSource].DecoratedParameter;test-source",
8181
"testlib;Member[MethodDecorator].DecoratedMember.Parameter[0];test-source",
8282
"testlib;Member[MethodDecoratorWithArgs].ReturnValue.DecoratedMember.Parameter[0];test-source",
83+
"testlib;Member[BaseClass].Instance.Member[baseclassSource].ReturnValue;test-source",
8384
]
8485
}
8586
}

0 commit comments

Comments
 (0)