-
Notifications
You must be signed in to change notification settings - Fork 1.9k
JS: More data flow through classes #760
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
|
Evaluation shows no result changes but a little bit of performance to investigate. |
ghost
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.
I like the ideas in this PR, but I would prefer a separate merge of the DataFlow::ClassNode feature.
I think the React libraries and queries could be a good test of the new API, js/unbound-event-handler-receiver is for example hard-wired to only reason about ES6 classes at the moment.
| predicate constructorReceiverToMethodStep(DataFlow::Node pred, DataFlow::Node succ) { | ||
| exists (ClassDefinition class_ | | ||
| pred = DataFlow::thisNode(class_.getConstructor().getBody()) and | ||
| succ = DataFlow::thisNode(class_.getAMethod().getBody()) |
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.
A call to MemberDeclaration::isStatic is required here to avoid flow between static and non-static members.
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.
Should be handled by ClassNode.
| succ = DataFlow::thisNode(class_.getAMethod().getBody()) | ||
| ) | ||
| or | ||
| exists (DataFlow::FunctionNode ctor, DataFlow::SourceNode prototype, DataFlow::PropWrite write | |
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.
This disjunct seems useful in a separate predicate or module for prototype reasoning (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.
Again, I believe ClassNode resolved this comment
| prototype = call.getASourceOperand() | ||
| ) | ||
| ) and | ||
| write = prototype.getAPropertyWrite() and |
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.
Maybe document that this flowstep does not traverse the prototype chain.
| * ``` | ||
| */ | ||
| class ClassNode extends DataFlow::SourceNode, DataFlow::ValueNode { | ||
| ClassNode() { |
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.
Could we make this class extensible? I think it would be useful to be able to support ES5 libraries for creating classes, like https://www.npmjs.com/package/create-react-class.
| */ | ||
| FunctionNode getAnInstanceMethod() { | ||
| exists (MethodDeclaration method | | ||
| method = astNode.(ClassDefinition).getAMethod() and |
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.
This duplicates most of ClassDefinition::getInstanceMethod.
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.
Some duplication here can't be avoided due to methods with computed property names.
| exists (MethodDeclaration method | | ||
| method = astNode.(ClassDefinition).getAMethod() and | ||
| not method.isStatic() and | ||
| not method.isAmbient() and |
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.
Do we really want the isAmbient check in the library? Most other uses of isAmbient are in the queries.
Dittos below.
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.
Since this is the dataflow library, I figured we should ignore things that don't have any runtime behaviour.
| result = method.getBody().flow() | ||
| ) | ||
| or | ||
| result = getAPrototypeReference().getAPropertyWrite(name).getRhs().getALocalSource() |
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 think the possibility of multiple prototype definitions and multiple method definitions motivates a name change to getAnInstanceMethod for this method.
|
I agree with Esben about landing the third commit separately. While you're at it, you could also make the first commit its own PR. I think those two PRs would be largely uncontroversial (but I like Esben's idea of making The second commit is more interesting; I'm a little bit concerned about the long-term implications of adding this flow step, but I haven't looked at the implementation in detail yet. |
|
Closing to open separate PRs |
Some improvements inspired by ODASA-7605, improving our data-flow library's handling of classes.
The first commit adds a return step from
thisin a constructor back to the caller, assuming the caller is anewcall.The two other commits are a bit more controversial:
One adds a call step from
thisin a constructor tothisin each of the methods in that class. The idea is that you can't call the method without first calling the constructor on that class, so anything you pass to the constructor can most likely flow into the methods. For example:There is potential for FPs here as the specific call site passing in a tainted value might not be using the
unsafemethod. However, it could be useful for analyzing frameworks where the methods are being invoked from client code.The last change introduces
DataFlow::ClassNodefor dealing with various forms of class definitions. I opted to stick with a chainable API for handling common tasks well, rather than mirroring the entire class hierarchy fromClasses.qll.I can separate out the commits into individual PRs if anyone prefers.