Skip to content

JS: API graph support for accessors (and classes members) #9234

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 23, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
JS: API graph support for accessors (and classes)
  • Loading branch information
asgerf committed May 23, 2022
commit 33dac5e95f039e62ea3af6af0265e33c9bbeb55c
26 changes: 25 additions & 1 deletion javascript/ql/lib/semmle/javascript/ApiGraphs.qll
Original file line number Diff line number Diff line change
Expand Up @@ -604,12 +604,36 @@ module API {
or
lbl = Label::promisedError() and
PromiseFlow::storeStep(rhs, pred, Promises::errorProp())
or
// The return-value of a getter G counts as a definition of property G
// (Ordinary methods and properties are handled as PropWrite nodes)
exists(string name | lbl = Label::member(name) |
rhs = pred.(DataFlow::ObjectLiteralNode).getPropertyGetter(name).getAReturn()
or
rhs =
pred.(DataFlow::ClassNode)
.getStaticMember(name, DataFlow::MemberKind::getter())
.getAReturn()
)
or
// If `new C()` escapes, generate edges to its instance members
exists(DataFlow::ClassNode cls, string name |
pred = cls.getAClassReference().getAnInstantiation() and
lbl = Label::member(name)
|
rhs = cls.getInstanceMethod(name)
or
rhs = cls.getInstanceMember(name, DataFlow::MemberKind::getter()).getAReturn()
)
)
or
exists(DataFlow::ClassNode cls, string name |
base = MkClassInstance(cls) and
lbl = Label::member(name) and
lbl = Label::member(name)
|
rhs = cls.getInstanceMethod(name)
or
rhs = cls.getInstanceMember(name, DataFlow::MemberKind::getter()).getAReturn()
)
or
exists(DataFlow::FunctionNode f |
Expand Down
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import ApiGraphs.VerifyAssertions
65 changes: 65 additions & 0 deletions javascript/ql/test/ApiGraphs/accessors/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
const foo = require('foo');

foo({
myMethod(x) { /* use (parameter 0 (member myMethod (parameter 0 (member exports (module foo))))) */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
myMethod(x) { /* use (parameter 0 (member myMethod (parameter 0 (member exports (module foo))))) */
myMethod(x) { /* use=moduleImport("foo").getMember("exports").getParameter(0).getMember("myMethod").getParameter(0) */

I recently changed the testing syntax.
All your tests need to be rewritten to the new syntax, because currently they don't test anything.

console.log(x);
}
});

foo({
get myMethod() {
return function(x) { /* use (parameter 0 (member myMethod (parameter 0 (member exports (module foo))))) */
console.log(x)
}
}
});

class C {
static myMethod(x) { /* use (parameter 0 (member myMethod (parameter 0 (member exports (module foo))))) */
console.log(x);
}
}
foo(C);

class D {
myMethod(x) { /* use (parameter 0 (member myMethod (parameter 0 (member exports (module foo))))) */
console.log(x);
}
}
foo(new D());

class E {
get myMethod() {
return function(x) { /* use (parameter 0 (member myMethod (parameter 0 (member exports (module foo))))) */
console.log(x);
}
}
}
foo(new E());

class F {
static get myMethod() {
return function(x) { /* use (parameter 0 (member myMethod (parameter 0 (member exports (module foo))))) */
console.log(x);
}
}
}
foo(F);

// Cases where the class is instantiated in `foo`:

class G {
myMethod2(x) { /* use (parameter 0 (member myMethod2 (instance (parameter 0 (member exports (module foo)))))) */
console.log(x);
}
}
foo(G);

class H {
get myMethod2() {
return function (x) { /* use (parameter 0 (member myMethod2 (instance (parameter 0 (member exports (module foo)))))) */
console.log(x);
}
}
}
foo(H);