-
Notifications
You must be signed in to change notification settings - Fork 1.9k
JS: Refactorization of NoSQL.qll to use API-graphs #4363
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
Conversation
1b525de to
0060473
Compare
| or | ||
| exists(DataFlow::TypeBackTracker t2 | result = getAMongoDbCallback(t2).backtrack(t2, t)) | ||
| // slightly imprecise, is not supposed to have a result if the parameter name is "db" (that would be a mongodb v2 `Db`). | ||
| result = getAMongoDbCallback().getParameter(1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't you use the newly introduced getAnImmediateUse to rule this out? The immediate use would be the DataFlow::Parameter itself, so you can check its name.
|
An evaluation came back bad because of a bad join-order (see below tuple counts). I changed the predicate a bit such that the performance should be good now. |
|
A new evaluation came back, and it's looking good. I think it might actually be a performance improvement. |
max-schaefer
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fantastic work, thanks! I have a few suggestions for improving terminological consistency, and one TypeScript-related question. I'm fine with addressing the latter in a follow-up PR to avoid upsetting the performance apple cart.
| or | ||
| exists(DataFlow::TypeBackTracker t2 | result = getAMongoDbCallback(t2).backtrack(t2, t)) | ||
| result = getAMongoDbCallback().getParameter(1) and | ||
| not result.getAnImmediateUse().(DataFlow::ParameterNode).getName() = "db" // mongodb v2 provides a `Db` here |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we end up doing this a lot, it might be nice to have a predicate asParameter() on API::Node, similar to how we have asExpr() and similar on data-flow nodes (but no need to squeeze it into this PR).
| private class TypedMongoCollection extends API::EntryPoint { | ||
| TypedMongoCollection() { this = "TypedMongoCollection" } | ||
|
|
||
| override DataFlow::SourceNode getAUse() { result.hasUnderlyingType("mongodb", "Collection") } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
API graphs are meant to support canonical type names. Have you tried using them instead? (They aren't particularly well exposed in the API at the moment, but they probably should be.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To expand on the above: you are meant to be able to do root().getMember("mongodb").getMember("Collection").getInstance() (cf this test). If that doesn't work, I'll fix it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To expand on the above: you are meant to be able to do
root().getMember("mongodb").getMember("Collection").getInstance()(cf this test). If that doesn't work, I'll fix it.
That worked, except it ends up looking like:
result = API::root().getASuccessor(API::EdgeLabel::mod("mongodb")).getMember("Collection").getInstance()The mongodb type has edge "mod mongodb", but it is not a module-import so using API::moduleImport("mongodb") does not work.
The API could use a cleanup, but lets do that later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW I ran into the same issue with the Angular2 model and held off on using API graphs for this reason.
The snippet you posted leaves more questions than it answers. Is root().getMember(x) the same as API::moduleImport(x)? If Collection is the name of an interface, what exactly does getInstance mean?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That worked, except it ends up looking like: (...)
🙈
The mongodb type has edge "mod mongodb", but it is not a module-import so using API::moduleImport("mongodb") does not work.
That's an oversight. I'll fix it, then we can see about using it in the NoSQL model.
If Collection is the name of an interface, what exactly does getInstance mean?
Ah, I had hoped that the concept of being an instance of a type would be natural, but if it's not we can introduce a new kind of edge for it. Any ideas for what to call it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I had hoped that the concept of being an instance of a type would be natural, but if it's not we can introduce a new kind of edge for it. Any ideas for what to call it?
I think getInstance() is natural. We just need some utility-predicate to work with CanonicalNames.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd be in favor of a classless predicate that's a direct replacement for hasUnderlyingType:
API::nodeOfType("mongodb", "Collection")I think representing values, type names, and type-namespaces as the same kind of node will lead to confusion, as most of the API::Node predicates are meaningless for type names and type-namespaces. Going straight to the "value" space means you can never mix up the two three kinds of nodes.
I don't think we need to change the underlying encoding.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something like this?
Co-authored-by: Max Schaefer <54907921+max-schaefer@users.noreply.github.com>
|
I think I've found a potential performance improvement. But that can wait for later, this PR is good to merge as is (when it comes to performance). @max-schaefer I did a quick-and-dirty experiment trying to make a |
Yes. It's something I've gone back and forth about a couple of times. The problem with the Definitely worth revisiting that decision if it benefits performance. |
This is the implementation I used in my small experiment: erik-krogh@0ec5e0e (I forgot to add the link in the previous comment). |
|
Thanks for sharing. I think that implementation nicely highlights the duplication you get with that approach. |
|
I'd prefer to remove the last commit. Then we have something that's identical (modulo comments) to the version that was evaluated. I'll fix the TypeScript API in a separate PR. |
I've reverted the last commit. |
A bit of precision has been lost compared to the previous implementation, due to limitations of the API-graphs API.But interestingly, no test case failed as a result.
The imprecisions are in the top of NoSQL.qll (
getAMongoClient()/getAMongoDb()).I added a new convenience method to
API::EntryPoint, which allows me to easily get anAPI::Nodefrom anEntryPoint.I had to change the implementation in some places to avoid API-graph related gotchas.
For example: instead of
Mongoose::InvokeNode(modelling a call to a Mongoose function) I now have aMongoose::MongooseFunction(modelling the Mongoose function itself).