Skip to content

Commit 812cba0

Browse files
author
Max Schaefer
authored
Merge pull request #828 from esben-semmle/js/vue-support-1
JS: basic Vue support
2 parents 97c5b8e + 235625d commit 812cba0

29 files changed

+771
-1
lines changed

change-notes/1.20/analysis-javascript.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
## General improvements
44

55
* Support for popular libraries has been improved. Consequently, queries may produce better results on code bases that use the following features:
6-
- client-side code, for example [React](https://reactjs.org/)
6+
- client-side code, for example [React](https://reactjs.org/) and [Vue](https://vuejs.org/)
77
- cookies and webstorage, for example [js-cookie](https://github.com/js-cookie/js-cookie)
88
- server-side code, for example [hapi](https://hapijs.com/)
99
- asynchronous code, for example [a-sync-waterfall](https://www.npmjs.com/package/a-sync-waterfall)
@@ -17,6 +17,7 @@
1717

1818
| **Query** | **Tags** | **Purpose** |
1919
|-----------------------------------------------|------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
20+
| Arrow method on Vue instance (`js/vue/arrow-method-on-vue-instance`) | reliability, frameworks/vue | Highlights arrow functions that are used as methods on Vue instances. Results are shown on LGTM by default.|
2021
| Cross-window communication with unrestricted target origin (`js/cross-window-information-leak`) | security, external/cwe/201, external/cwe/359 | Highlights code that sends potentially sensitive information to another window without restricting the receiver window's origin, indicating a possible violation of [CWE-201](https://cwe.mitre.org/data/definitions/201.html). Results are shown on LGTM by default. |
2122
| Double escaping or unescaping (`js/double-escaping`) | correctness, security, external/cwe/cwe-116 | Highlights potential double escaping or unescaping of special characters, indicating a possible violation of [CWE-116](https://cwe.mitre.org/data/definitions/116.html). Results are shown on LGTM by default. |
2223
| Incomplete regular expression for hostnames (`js/incomplete-hostname-regexp`) | correctness, security, external/cwe/cwe-020 | Highlights hostname sanitizers that are likely to be incomplete, indicating a violation of [CWE-020](https://cwe.mitre.org/data/definitions/20.html). Results are shown on LGTM by default.|

javascript/config/suites/javascript/frameworks-more

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,4 @@
2323
+ semmlecode-javascript-queries/React/UnusedOrUndefinedStateProperty.ql: /Frameworks/React
2424
+ semmlecode-javascript-queries/Electron/DisablingWebSecurity.ql: /Frameworks/Electron
2525
+ semmlecode-javascript-queries/Electron/AllowRunningInsecureContent.ql: /Frameworks/Electron
26+
+ semmlecode-javascript-queries/Vue/ArrowMethodOnVueInstance.ql: /Frameworks/Vue
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
6+
<overview>
7+
<p>
8+
9+
The Vue framework invokes the methods of a Vue instance with
10+
the instance as the receiver. It is however impossible to perform
11+
this binding of instance and receiver for arrow functions, so the
12+
<code>this</code> variable in an arrow function on a Vue instance may
13+
not have the value that the programmer expects.
14+
15+
</p>
16+
</overview>
17+
18+
<recommendation>
19+
<p>
20+
Ensure that the methods on a Vue instance can have their receiver bound to the instance.
21+
</p>
22+
</recommendation>
23+
24+
<example>
25+
26+
<p>
27+
28+
The following example shows two similar Vue instances, the only
29+
difference is how the <code>created</code> life cycle hook
30+
callback is defined.
31+
32+
The first Vue instance uses an arrow function as the callback.
33+
This means that the <code>this</code> variable will have the global
34+
object as its value, causing <code>this.myProperty</code> to evaluate
35+
to <code>undefined</code>, which may not be intended.
36+
37+
Instead, the second Vue instance uses an ordinary function as the callback,
38+
causing <code>this.myProperty</code> to evaluate to <code>42</code>.
39+
40+
</p>
41+
42+
<sample src="examples/ArrowMethodOnVueInstance.js"/>
43+
44+
</example>
45+
46+
<references>
47+
<li>Vue documentation: <a href="https://vuejs.org/v2/guide/instance.html">The Vue Instance</a></li>
48+
</references>
49+
</qhelp>
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/**
2+
* @name Arrow method on Vue instance
3+
* @description An arrow method on a Vue instance doesn't have its `this` variable bound to the Vue instance.
4+
* @kind problem
5+
* @problem.severity warning
6+
* @id js/vue/arrow-method-on-vue-instance
7+
* @tags reliability
8+
* frameworks/vue
9+
* @precision high
10+
*/
11+
12+
import javascript
13+
14+
from Vue::Instance instance, DataFlow::Node def, DataFlow::FunctionNode arrow, ThisExpr dis
15+
where instance.getABoundFunction() = def and
16+
arrow.flowsTo(def) and
17+
arrow.asExpr() instanceof ArrowFunctionExpr and
18+
arrow.asExpr() = dis.getEnclosingFunction()
19+
select def, "The $@ of this $@ it will not be bound to the Vue instance.", dis, "`this` variable", arrow, "arrow function"
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
new Vue({
2+
data: {
3+
myProperty: 42
4+
},
5+
created: () => {
6+
// BAD: prints: "myProperty is: undefined"
7+
console.log('myProperty is: ' + this.myProperty);
8+
}
9+
});
10+
11+
new Vue({
12+
data: {
13+
myProperty: 42
14+
},
15+
created: function () {
16+
// GOOD: prints: "myProperty is: 1"
17+
console.log('myProperty is: ' + this.myProperty);
18+
}
19+
});

javascript/ql/src/javascript.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ import semmle.javascript.frameworks.Request
7878
import semmle.javascript.frameworks.SQL
7979
import semmle.javascript.frameworks.StringFormatters
8080
import semmle.javascript.frameworks.UriLibraries
81+
import semmle.javascript.frameworks.Vue
8182
import semmle.javascript.frameworks.XmlParsers
8283
import semmle.javascript.frameworks.xUnit
8384
import semmle.javascript.linters.ESLint

javascript/ql/src/semmle/javascript/HTML.qll

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,40 @@ module HTML {
159159
* if it can be determined.
160160
*/
161161
Script resolveSource() { result.getFile().getAbsolutePath() = resolveSourcePath() }
162+
163+
/**
164+
* Gets the inline script of this script element, if any.
165+
*/
166+
private InlineScript getInlineScript() {
167+
exists(string f, Location l1, int sl1, int sc1, int el1, int ec1, Location l2, int sl2, int sc2, int el2, int ec2 |
168+
l1 = getLocation() and
169+
l2 = result.getLocation() and
170+
l1.hasLocationInfo(f, sl1, sc1, el1, ec1) and
171+
l2.hasLocationInfo(f, sl2, sc2, el2, ec2)
172+
|
173+
(
174+
sl1 = sl2 and sc1 < sc2
175+
or
176+
sl1 < sl2
177+
) and
178+
(
179+
el1 = el2 and ec1 > ec2
180+
or
181+
el1 > el2
182+
)
183+
) and
184+
// the src attribute has precedence
185+
not exists(getSourcePath())
186+
}
187+
188+
/**
189+
* Gets the script of this element, if it can be determined.
190+
*/
191+
Script getScript() {
192+
result = getInlineScript() or
193+
result = resolveSource()
194+
}
195+
162196
}
163197

164198
/**

0 commit comments

Comments
 (0)