Skip to content

Commit 3a0681d

Browse files
committed
JS: API graph support for accessors (and classes)
1 parent bf958ff commit 3a0681d

File tree

4 files changed

+91
-1
lines changed

4 files changed

+91
-1
lines changed

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

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -604,12 +604,36 @@ module API {
604604
or
605605
lbl = Label::promisedError() and
606606
PromiseFlow::storeStep(rhs, pred, Promises::errorProp())
607+
or
608+
// The return-value of a getter G counts as a definition of property G
609+
// (Ordinary methods and properties are handled as PropWrite nodes)
610+
exists(string name | lbl = Label::member(name) |
611+
rhs = pred.(DataFlow::ObjectLiteralNode).getPropertyGetter(name).getAReturn()
612+
or
613+
rhs =
614+
pred.(DataFlow::ClassNode)
615+
.getStaticMember(name, DataFlow::MemberKind::getter())
616+
.getAReturn()
617+
)
618+
or
619+
// If `new C()` escapes, generate edges to its instance members
620+
exists(DataFlow::ClassNode cls, string name |
621+
pred = cls.getAClassReference().getAnInstantiation() and
622+
lbl = Label::member(name)
623+
|
624+
rhs = cls.getInstanceMethod(name)
625+
or
626+
rhs = cls.getInstanceMember(name, DataFlow::MemberKind::getter()).getAReturn()
627+
)
607628
)
608629
or
609630
exists(DataFlow::ClassNode cls, string name |
610631
base = MkClassInstance(cls) and
611-
lbl = Label::member(name) and
632+
lbl = Label::member(name)
633+
|
612634
rhs = cls.getInstanceMethod(name)
635+
or
636+
rhs = cls.getInstanceMember(name, DataFlow::MemberKind::getter()).getAReturn()
613637
)
614638
or
615639
exists(DataFlow::FunctionNode f |

javascript/ql/test/ApiGraphs/accessors/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: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
const foo = require('foo');
2+
3+
foo({
4+
myMethod(x) { /* use (parameter 0 (member myMethod (parameter 0 (member exports (module foo))))) */
5+
console.log(x);
6+
}
7+
});
8+
9+
foo({
10+
get myMethod() {
11+
return function(x) { /* use (parameter 0 (member myMethod (parameter 0 (member exports (module foo))))) */
12+
console.log(x)
13+
}
14+
}
15+
});
16+
17+
class C {
18+
static myMethod(x) { /* use (parameter 0 (member myMethod (parameter 0 (member exports (module foo))))) */
19+
console.log(x);
20+
}
21+
}
22+
foo(C);
23+
24+
class D {
25+
myMethod(x) { /* use (parameter 0 (member myMethod (parameter 0 (member exports (module foo))))) */
26+
console.log(x);
27+
}
28+
}
29+
foo(new D());
30+
31+
class E {
32+
get myMethod() {
33+
return function(x) { /* use (parameter 0 (member myMethod (parameter 0 (member exports (module foo))))) */
34+
console.log(x);
35+
}
36+
}
37+
}
38+
foo(new E());
39+
40+
class F {
41+
static get myMethod() {
42+
return function(x) { /* use (parameter 0 (member myMethod (parameter 0 (member exports (module foo))))) */
43+
console.log(x);
44+
}
45+
}
46+
}
47+
foo(F);
48+
49+
// Cases where the class is instantiated in `foo`:
50+
51+
class G {
52+
myMethod2(x) { /* use (parameter 0 (member myMethod2 (instance (parameter 0 (member exports (module foo)))))) */
53+
console.log(x);
54+
}
55+
}
56+
foo(G);
57+
58+
class H {
59+
get myMethod2() {
60+
return function (x) { /* use (parameter 0 (member myMethod2 (instance (parameter 0 (member exports (module foo)))))) */
61+
console.log(x);
62+
}
63+
}
64+
}
65+
foo(H);

0 commit comments

Comments
 (0)