Skip to content

Conversation

@asger-semmle
Copy link
Contributor

@asger-semmle asger-semmle commented Jan 11, 2019

Some improvements inspired by ODASA-7605, improving our data-flow library's handling of classes.

The first commit adds a return step from this in a constructor back to the caller, assuming the caller is a new call.

The two other commits are a bit more controversial:

One adds a call step from this in a constructor to this in 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:

class C {
  constructor(x) { this.x = x }
  unsafe() { sink(x) }
}
new C(source());

There is potential for FPs here as the specific call site passing in a tainted value might not be using the unsafe method. However, it could be useful for analyzing frameworks where the methods are being invoked from client code.

The last change introduces DataFlow::ClassNode for 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 from Classes.qll.

I can separate out the commits into individual PRs if anyone prefers.

@asger-semmle asger-semmle requested a review from a team as a code owner January 11, 2019 18:55
@ghost ghost self-assigned this Jan 14, 2019
@asger-semmle
Copy link
Contributor Author

Evaluation shows no result changes but a little bit of performance to investigate.

Copy link

@ghost ghost left a 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())
Copy link

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.

Copy link
Contributor Author

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 |
Copy link

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).

Copy link
Contributor Author

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
Copy link

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() {
Copy link

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
Copy link

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.

Copy link
Contributor Author

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
Copy link

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.

Copy link
Contributor Author

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()
Copy link

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.

@xiemaisi
Copy link

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 ClassNode separate, perhaps using the ::Range pattern).

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.

@asger-semmle
Copy link
Contributor Author

Closing to open separate PRs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants