File tree 6 files changed +159
-2
lines changed
6 files changed +159
-2
lines changed Original file line number Diff line number Diff line change 8
8
| Rule ID | Description | |
9
9
| :--------| :------------| :---|
10
10
| [ vue-i18n/<wbr >no-missing-keys] ( ./no-missing-keys.html ) | disallow missing locale message key at localization methods | :star : |
11
+ | [ vue-i18n/<wbr >no-v-html] ( ./no-v-html.html ) | disallow use of localization methods on v-html to prevent XSS attack | :star : |
11
12
12
13
## Best Practices
13
14
Original file line number Diff line number Diff line change
1
+ # vue-i18n/no-v-html
2
+
3
+ > disallow use of localization methods on v-html to prevent XSS attack
4
+
5
+ - :star : The ` "extends": "plugin:vue-i18n/recommended" ` property in a configuration file enables this rule.
6
+
7
+ This rule reports all uses of localization methods on ` v-html ` directive in order to reduce the risk of injecting potentially unsafe / unescaped html into the browser leading to Cross-Site Scripting (XSS) attacks.
8
+
9
+ ## :book : Rule Details
10
+
11
+ You can be detected with this rule the following:
12
+
13
+ - ` $t `
14
+ - ` t `
15
+ - ` $tc `
16
+ - ` tc `
17
+
18
+ :-1 : Examples of ** incorrect** code for this rule:
19
+
20
+ locale messages:
21
+ ``` json
22
+ {
23
+ "term" : " <p>I accept xxx <a href=\"\/ term\" >Terms of Service Agreement</a></p>"
24
+ }
25
+ ```
26
+
27
+ localization codes:
28
+
29
+ ``` vue
30
+ <template>
31
+ <div class="app">
32
+ <!-- ✗ BAD -->
33
+ <p v-html="$t('term')"></p>
34
+ </div>
35
+ </template>
36
+ ```
37
+
38
+ :+1 : Examples of ** correct** code for this rule:
39
+
40
+ locale messages:
41
+ ``` json
42
+ {
43
+ "tos" : " Term of Service" ,
44
+ "term" : " I accept xxx {0}."
45
+ }
46
+ ```
47
+
48
+ localization codes:
49
+
50
+ ``` vue
51
+ <template>
52
+ <div class="app">
53
+ <!-- ✗ GOOD -->
54
+ <i18n path="term" tag="label" for="tos">
55
+ <a :href="url" target="_blank">{{ $t('tos') }}</a>
56
+ </i18n>
57
+ </div>
58
+ </template>
59
+ ```
60
+
61
+ ## :mute : When Not To Use It
62
+
63
+ If you are certain the content passed to ` v-html ` is sanitized HTML you can disable this rule.
64
+
65
+ ## :books : Further reading
66
+
67
+ - [ XSS in Vue.js] ( https://blog.sqreen.io/xss-in-vue-js/ )
Original file line number Diff line number Diff line change 4
4
module . exports = {
5
5
plugins : [ 'vue-i18n' ] ,
6
6
rules : {
7
- 'vue-i18n/no-missing-keys' : 'error'
7
+ 'vue-i18n/no-missing-keys' : 'error' ,
8
+ 'vue-i18n/no-v-html' : 'error'
8
9
}
9
10
}
Original file line number Diff line number Diff line change 4
4
module . exports = {
5
5
'no-dynamic-keys' : require ( './rules/no-dynamic-keys' ) ,
6
6
'no-missing-keys' : require ( './rules/no-missing-keys' ) ,
7
- 'no-unused-keys' : require ( './rules/no-unused-keys' )
7
+ 'no-unused-keys' : require ( './rules/no-unused-keys' ) ,
8
+ 'no-v-html' : require ( './rules/no-v-html' )
8
9
}
Original file line number Diff line number Diff line change
1
+ /**
2
+ * @author kazuya kawaguchi (a.k.a. kazupon)
3
+ */
4
+ 'use strict'
5
+
6
+ const { defineTemplateBodyVisitor } = require ( '../utils/index' )
7
+
8
+ function checkDirective ( context , node ) {
9
+ if ( ( node . value && node . value . type === 'VExpressionContainer' ) &&
10
+ ( node . value . expression && node . value . expression . type === 'CallExpression' ) ) {
11
+ const expressionNode = node . value . expression
12
+ const funcName = ( expressionNode . callee . type === 'MemberExpression' && expressionNode . callee . property . name ) || expressionNode . callee . name
13
+ if ( ! / ^ ( \$ t | t | \$ t c | t c ) $ / . test ( funcName ) || ! expressionNode . arguments || ! expressionNode . arguments . length ) {
14
+ return
15
+ }
16
+ context . report ( {
17
+ node,
18
+ message : `Using ${ funcName } on 'v-html' directive can lead to XSS attack.`
19
+ } )
20
+ }
21
+ }
22
+
23
+ function create ( context ) {
24
+ return defineTemplateBodyVisitor ( context , {
25
+ "VAttribute[directive=true][key.name='html']" ( node ) {
26
+ checkDirective ( context , node )
27
+ } ,
28
+
29
+ "VAttribute[directive=true][key.name.name='html']" ( node ) {
30
+ checkDirective ( context , node )
31
+ }
32
+ } )
33
+ }
34
+
35
+ module . exports = {
36
+ meta : {
37
+ type : 'suggestion' ,
38
+ docs : {
39
+ description : 'disallow use of localization methods on v-html to prevent XSS attack' ,
40
+ category : 'Possible Errors' ,
41
+ recommended : true
42
+ } ,
43
+ fixable : null ,
44
+ schema : [ ]
45
+ } ,
46
+ create
47
+ }
Original file line number Diff line number Diff line change
1
+ /**
2
+ * @author kazuya kawaguchi (a.k.a. kazupon)
3
+ */
4
+ 'use strict'
5
+
6
+ const RuleTester = require ( 'eslint' ) . RuleTester
7
+ const rule = require ( '../../../lib/rules/no-v-html' )
8
+
9
+ const tester = new RuleTester ( {
10
+ parser : 'vue-eslint-parser' ,
11
+ parserOptions : { ecmaVersion : 2015 }
12
+ } )
13
+
14
+ tester . run ( 'no-v-html' , rule , {
15
+ valid : [ {
16
+ code : `<template>
17
+ <div class="app">
18
+ <i18n path="term" tag="label" for="tos">
19
+ <a :href="url" target="_blank">{{ $t('tos') }}</a>
20
+ </i18n>
21
+ </div>
22
+ </template>`
23
+ } ] ,
24
+
25
+ invalid : [ {
26
+ code : `<template>
27
+ <p v-html="$t('hello')"></p>
28
+ </template>` ,
29
+ errors : [
30
+ `Using $t on 'v-html' directive can lead to XSS attack.`
31
+ ]
32
+ } , {
33
+ code : `<template>
34
+ <p v-html="this.t('hello')"></p>
35
+ </template>` ,
36
+ errors : [
37
+ `Using t on 'v-html' directive can lead to XSS attack.`
38
+ ]
39
+ } ]
40
+ } )
You can’t perform that action at this time.
0 commit comments