Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 1220b50

Browse files
author
Esben Sparre Andreasen
committed
JS: whitelist _.bindAll-methods in js/unbound-event-handler-receiver
1 parent 9e0ba51 commit 1220b50

3 files changed

Lines changed: 49 additions & 22 deletions

File tree

javascript/ql/src/Expressions/UnboundEventHandlerReceiver.ql

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,30 @@ import javascript
1313
* Holds if the receiver of `method` is bound in a method of its class.
1414
*/
1515
private predicate isBoundInMethod(MethodDeclaration method) {
16-
exists (DataFlow::ThisNode thiz, MethodDeclaration bindingMethod |
16+
exists (DataFlow::ThisNode thiz, MethodDeclaration bindingMethod, string name |
17+
name = method.getName() and
1718
bindingMethod.getDeclaringClass() = method.getDeclaringClass() and
1819
not bindingMethod.isStatic() and
19-
thiz.getBinder().getAstNode() = bindingMethod.getBody() and
20+
thiz.getBinder().getAstNode() = bindingMethod.getBody() |
2021
exists (DataFlow::Node rhs, DataFlow::MethodCallNode bind |
2122
// this.<methodName> = <expr>.bind(...)
22-
thiz.hasPropertyWrite(method.getName(), rhs) and
23+
thiz.hasPropertyWrite(name, rhs) and
2324
bind.flowsTo(rhs) and
2425
bind.getMethodName() = "bind"
2526
)
27+
or
28+
exists (DataFlow::MethodCallNode bindAll |
29+
bindAll.getMethodName() = "bindAll" and
30+
thiz.flowsTo(bindAll.getArgument(0)) |
31+
// _.bindAll(this, <name1>)
32+
bindAll.getArgument(1).mayHaveStringValue(name)
33+
or
34+
// _.bindAll(this, [<name1>, <name2>])
35+
exists (DataFlow::ArrayLiteralNode names |
36+
names.flowsTo(bindAll.getArgument(1)) and
37+
names.getAnElement().mayHaveStringValue(name)
38+
)
39+
)
2640
)
2741
}
2842

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
| tst.js:56:18:56:40 | onClick ... bound1} | The receiver of this event handler call is unbound, `$@` will be `undefined` in the call to $@ | tst.js:14:9:14:12 | this | this | tst.js:13:5:15:5 | unbound ... ;\\n } | unbound1 |
2-
| tst.js:57:18:57:40 | onClick ... bound2} | The receiver of this event handler call is unbound, `$@` will be `undefined` in the call to $@ | tst.js:18:15:18:18 | this | this | tst.js:17:5:19:5 | unbound ... ;\\n } | unbound2 |
3-
| tst.js:58:18:58:35 | onClick={unbound3} | The receiver of this event handler call is unbound, `$@` will be `undefined` in the call to $@ | tst.js:22:15:22:18 | this | this | tst.js:21:5:23:5 | unbound ... ;\\n } | unbound3 |
1+
| 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:35:9:35:12 | this | this | tst.js:34:5:36:5 | unbound ... ;\\n } | unbound1 |
2+
| 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:39:15:39:18 | this | this | tst.js:38:5:40:5 | unbound ... ;\\n } | unbound2 |
3+
| 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:43:15:43:18 | this | this | tst.js:42:5:44:5 | unbound ... ;\\n } | unbound3 |

javascript/ql/test/query-tests/Expressions/UnboundEventHandlerReceiver/tst.js

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,34 @@
11
import React from 'react';
22

33
class Component extends React.Component {
4+
5+
render() {
6+
var unbound3 = this.unbound3;
7+
return <div>
8+
<div onClick={this.unbound1}/> // NOT OK
9+
<div onClick={this.unbound2}/> // NOT OK
10+
<div onClick={unbound3}/> // NOT OK
11+
<div onClick={this.bound_throughBindInConstructor}/> // OK
12+
<div onClick={this.bound_throughDeclaration}/> // OK
13+
<div onClick={this.unbound_butNoThis}/> // OK
14+
<div onClick={this.unbound_butNoThis2}/> // OK
15+
<div onClick={(e) => this.unbound_butInvokedSafely(e)}/> // OK
16+
<div onClick={this.bound_throughBindInMethod}/> // OK
17+
<div onClick={this.bound_throughNonSyntacticBindInConstructor}/> // OK
18+
<div onClick={this.bound_throughBindAllInConstructor1}/> // OK
19+
<div onClick={this.bound_throughBindAllInConstructor2}/> // OK
20+
</div>
21+
}
22+
423
constructor(props) {
524
super(props);
625
this.bound_throughBindInConstructor = this.bound_throughBindInConstructor.bind(this);
726
this.bound_throughBizarreBind = foo.bar.bind(baz);
827
var cmp = this;
928
var bound = (cmp.bound_throughNonSyntacticBindInConstructor.bind(this));
1029
(cmp).bound_throughNonSyntacticBindInConstructor = bound;
30+
_.bindAll(this, 'bound_throughBindAllInConstructor1');
31+
_.bindAll(this, ['bound_throughBindAllInConstructor2']);
1132
}
1233

1334
unbound1() {
@@ -50,22 +71,6 @@ class Component extends React.Component {
5071
this.setState({ });
5172
}
5273

53-
render() {
54-
var unbound3 = this.unbound3;
55-
return <div>
56-
<div onClick={this.unbound1}/> // NOT OK
57-
<div onClick={this.unbound2}/> // NOT OK
58-
<div onClick={unbound3}/> // NOT OK
59-
<div onClick={this.bound_throughBindInConstructor}/> // OK
60-
<div onClick={this.bound_throughDeclaration}/> // OK
61-
<div onClick={this.unbound_butNoThis}/> // OK
62-
<div onClick={this.unbound_butNoThis2}/> // OK
63-
<div onClick={(e) => this.unbound_butInvokedSafely(e)}/> // OK
64-
<div onClick={this.bound_throughBindInMethod}/> // OK
65-
<div onClick={this.bound_throughNonSyntacticBindInConstructor}/> // OK
66-
</div>
67-
}
68-
6974
componentWillMount() {
7075
this.bound_throughBindInMethod = this.bound_throughBindInMethod.bind(this);
7176
}
@@ -74,6 +79,14 @@ class Component extends React.Component {
7479
this.setState({ });
7580
}
7681

82+
bound_throughBindAllInConstructor1() {
83+
this.setState({ });
84+
}
85+
86+
bound_throughBindAllInConstructor2() {
87+
this.setState({ });
88+
}
89+
7790
}
7891

7992
// semmle-extractor-options: --experimental

0 commit comments

Comments
 (0)