JS: whitelist auto-bind methods in js/unbound-event-handler-receiver

This commit is contained in:
Esben Sparre Andreasen
2018-09-11 22:21:02 +02:00
parent eb10f603ab
commit fcc33ce93d
3 changed files with 45 additions and 21 deletions

View File

@@ -13,28 +13,33 @@ import javascript
* Holds if the receiver of `method` is bound.
*/
private predicate isBoundInMethod(MethodDeclaration method) {
exists (DataFlow::ThisNode thiz, MethodDeclaration bindingMethod, string name |
name = method.getName() and
exists (DataFlow::ThisNode thiz, MethodDeclaration bindingMethod |
bindingMethod.getDeclaringClass() = method.getDeclaringClass() and
not bindingMethod.isStatic() and
thiz.getBinder().getAstNode() = bindingMethod.getBody() |
exists (DataFlow::Node rhs, DataFlow::MethodCallNode bind |
// this.<methodName> = <expr>.bind(...)
thiz.hasPropertyWrite(name, rhs) and
bind.flowsTo(rhs) and
bind.getMethodName() = "bind"
)
// require("auto-bind")(this)
thiz.flowsTo(DataFlow::moduleImport("auto-bind").getACall().getArgument(0))
or
exists (DataFlow::MethodCallNode bindAll |
bindAll.getMethodName() = "bindAll" and
thiz.flowsTo(bindAll.getArgument(0)) |
// _.bindAll(this, <name1>)
bindAll.getArgument(1).mayHaveStringValue(name)
exists (string name |
name = method.getName() |
exists (DataFlow::Node rhs, DataFlow::MethodCallNode bind |
// this.<methodName> = <expr>.bind(...)
thiz.hasPropertyWrite(name, rhs) and
bind.flowsTo(rhs) and
bind.getMethodName() = "bind"
)
or
// _.bindAll(this, [<name1>, <name2>])
exists (DataFlow::ArrayLiteralNode names |
names.flowsTo(bindAll.getArgument(1)) and
names.getAnElement().mayHaveStringValue(name)
exists (DataFlow::MethodCallNode bindAll |
bindAll.getMethodName() = "bindAll" and
thiz.flowsTo(bindAll.getArgument(0)) |
// _.bindAll(this, <name1>)
bindAll.getArgument(1).mayHaveStringValue(name)
or
// _.bindAll(this, [<name1>, <name2>])
exists (DataFlow::ArrayLiteralNode names |
names.flowsTo(bindAll.getArgument(1)) and
names.getAnElement().mayHaveStringValue(name)
)
)
)
)

View File

@@ -1,3 +1,3 @@
| tst.js:8:18:8:40 | onClick ... bound1} | The receiver of this event handler call is unbound, `$@` will be `undefined` in the call to $@ | tst.js:37:9:37:12 | this | this | tst.js:36:5:38:5 | unbound ... ;\\n } | unbound1 |
| tst.js:9:18:9:40 | onClick ... bound2} | The receiver of this event handler call is unbound, `$@` will be `undefined` in the call to $@ | tst.js:41:15:41:18 | this | this | tst.js:40:5:42:5 | unbound ... ;\\n } | unbound2 |
| tst.js:10:18:10:35 | onClick={unbound3} | The receiver of this event handler call is unbound, `$@` will be `undefined` in the call to $@ | tst.js:45:15:45:18 | this | this | tst.js:44:5:46:5 | unbound ... ;\\n } | unbound3 |
| tst.js:27:18:27:40 | onClick ... bound1} | The receiver of this event handler call is unbound, `$@` will be `undefined` in the call to $@ | tst.js:56:9:56:12 | this | this | tst.js:55:5:57:5 | unbound ... ;\\n } | unbound1 |
| tst.js:28:18:28:40 | onClick ... bound2} | The receiver of this event handler call is unbound, `$@` will be `undefined` in the call to $@ | tst.js:60:15:60:18 | this | this | tst.js:59:5:61:5 | unbound ... ;\\n } | unbound2 |
| tst.js:29:18:29:35 | onClick={unbound3} | The receiver of this event handler call is unbound, `$@` will be `undefined` in the call to $@ | tst.js:64:15:64:18 | this | this | tst.js:63:5:65:5 | unbound ... ;\\n } | unbound3 |

View File

@@ -1,6 +1,25 @@
import React from 'react';
import autoBind from 'auto-bind';
class Component extends React.Component {
class Component0 extends React.Component {
render() {
return <div>
<div onClick={this.bound_throughAutoBind}/> // OK
</div>
}
constructor(props) {
super(props);
autoBind(this);
}
bound_throughAutoBind() {
this.setState({ });
}
}
class Component1 extends React.Component {
render() {
var unbound3 = this.unbound3;