From c98c01e80ab2738e8115d8fed07d3c14b1f16c83 Mon Sep 17 00:00:00 2001 From: Lucas Werkmeister Date: Tue, 29 Jul 2025 17:11:40 +0200 Subject: [PATCH 01/11] feat: Add ignorePattern to no-v-html MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows configuring the rule such that certain variables are allowed in v-html=. For instance, we would like to allowlist variables named “*Html” (as in the test), where the name already marks them as containing safe HTML that’s expected to be used with v-html=. --- .changeset/purple-lights-invite.md | 5 +++++ lib/rules/no-v-html.js | 19 ++++++++++++++++++- tests/lib/rules/no-v-html.js | 11 +++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 .changeset/purple-lights-invite.md diff --git a/.changeset/purple-lights-invite.md b/.changeset/purple-lights-invite.md new file mode 100644 index 000000000..80aa90b8d --- /dev/null +++ b/.changeset/purple-lights-invite.md @@ -0,0 +1,5 @@ +--- +'eslint-plugin-vue': minor +--- + +Added `ignorePattern` option to [`vue/no-v-html`](https://eslint.vuejs.org/rules/no-v-html.html) diff --git a/lib/rules/no-v-html.js b/lib/rules/no-v-html.js index 355e86b21..9be0d8eaa 100644 --- a/lib/rules/no-v-html.js +++ b/lib/rules/no-v-html.js @@ -4,6 +4,7 @@ */ 'use strict' const utils = require('../utils') +const { toRegExp } = require('../utils/regexp') module.exports = { meta: { @@ -14,16 +15,32 @@ module.exports = { url: 'https://eslint.vuejs.org/rules/no-v-html.html' }, fixable: null, - schema: [], + schema: [{ + type: 'object', + properties: { + ignorePattern: { + type: 'string' + } + } + }], messages: { unexpected: "'v-html' directive can lead to XSS attack." } }, /** @param {RuleContext} context */ create(context) { + const options = context.options[0] + let ignoredVarMatcher = null + if (options?.ignorePattern) { + ignoredVarMatcher = toRegExp(options.ignorePattern, { remove: 'g' }) + } + return utils.defineTemplateBodyVisitor(context, { /** @param {VDirective} node */ "VAttribute[directive=true][key.name.name='html']"(node) { + if (ignoredVarMatcher !== null && node.value.expression.type === 'Identifier' && ignoredVarMatcher.test(node.value.expression.name)) { + return + } context.report({ node, loc: node.loc, diff --git a/tests/lib/rules/no-v-html.js b/tests/lib/rules/no-v-html.js index d9ae67ca1..efe7a8f71 100644 --- a/tests/lib/rules/no-v-html.js +++ b/tests/lib/rules/no-v-html.js @@ -28,6 +28,11 @@ ruleTester.run('no-v-html', rule, { { filename: 'test.vue', code: '' + }, + { + filename: 'test.vue', + code: '', + options: [{ignorePattern: '/^(?:html|.+Html)$/'}] } ], invalid: [ @@ -45,6 +50,12 @@ ruleTester.run('no-v-html', rule, { filename: 'test.vue', code: '', errors: ["'v-html' directive can lead to XSS attack."] + }, + { + filename: 'test.vue', + code: '', + options: [{ignorePattern: '^(?:html|.+Html)$'}], + errors: ["'v-html' directive can lead to XSS attack."] } ] }) From 9dcfc74d628624b77e1dde4d7cf02f81688cd696 Mon Sep 17 00:00:00 2001 From: Lucas Werkmeister Date: Tue, 29 Jul 2025 19:29:43 +0200 Subject: [PATCH 02/11] Apply suggestions from code review Co-authored-by: Flo Edelmann --- lib/rules/no-v-html.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/rules/no-v-html.js b/lib/rules/no-v-html.js index 9be0d8eaa..91aaa55a9 100644 --- a/lib/rules/no-v-html.js +++ b/lib/rules/no-v-html.js @@ -21,7 +21,8 @@ module.exports = { ignorePattern: { type: 'string' } - } + }, + additionalProperties: false }], messages: { unexpected: "'v-html' directive can lead to XSS attack." @@ -30,15 +31,14 @@ module.exports = { /** @param {RuleContext} context */ create(context) { const options = context.options[0] - let ignoredVarMatcher = null - if (options?.ignorePattern) { - ignoredVarMatcher = toRegExp(options.ignorePattern, { remove: 'g' }) - } + const ignoredVarMatcher = options?.ignorePattern + ? toRegExp(options.ignorePattern, { remove: 'g' }) + : undefined return utils.defineTemplateBodyVisitor(context, { /** @param {VDirective} node */ "VAttribute[directive=true][key.name.name='html']"(node) { - if (ignoredVarMatcher !== null && node.value.expression.type === 'Identifier' && ignoredVarMatcher.test(node.value.expression.name)) { + if (ignoredVarMatcher && node.value.expression.type === 'Identifier' && ignoredVarMatcher.test(node.value.expression.name)) { return } context.report({ From 6c775ac36d3db959340c2c365affc5e42c3e4860 Mon Sep 17 00:00:00 2001 From: Lucas Werkmeister Date: Tue, 29 Jul 2025 19:31:11 +0200 Subject: [PATCH 03/11] npm run lint:fix --- lib/rules/no-v-html.js | 26 ++++++++++++++++---------- tests/lib/rules/no-v-html.js | 4 ++-- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/lib/rules/no-v-html.js b/lib/rules/no-v-html.js index 91aaa55a9..98ebaa1af 100644 --- a/lib/rules/no-v-html.js +++ b/lib/rules/no-v-html.js @@ -15,15 +15,17 @@ module.exports = { url: 'https://eslint.vuejs.org/rules/no-v-html.html' }, fixable: null, - schema: [{ - type: 'object', - properties: { - ignorePattern: { - type: 'string' - } - }, - additionalProperties: false - }], + schema: [ + { + type: 'object', + properties: { + ignorePattern: { + type: 'string' + } + }, + additionalProperties: false + } + ], messages: { unexpected: "'v-html' directive can lead to XSS attack." } @@ -38,7 +40,11 @@ module.exports = { return utils.defineTemplateBodyVisitor(context, { /** @param {VDirective} node */ "VAttribute[directive=true][key.name.name='html']"(node) { - if (ignoredVarMatcher && node.value.expression.type === 'Identifier' && ignoredVarMatcher.test(node.value.expression.name)) { + if ( + ignoredVarMatcher && + node.value.expression.type === 'Identifier' && + ignoredVarMatcher.test(node.value.expression.name) + ) { return } context.report({ diff --git a/tests/lib/rules/no-v-html.js b/tests/lib/rules/no-v-html.js index efe7a8f71..0ea4eeb51 100644 --- a/tests/lib/rules/no-v-html.js +++ b/tests/lib/rules/no-v-html.js @@ -32,7 +32,7 @@ ruleTester.run('no-v-html', rule, { { filename: 'test.vue', code: '', - options: [{ignorePattern: '/^(?:html|.+Html)$/'}] + options: [{ ignorePattern: '/^(?:html|.+Html)$/' }] } ], invalid: [ @@ -54,7 +54,7 @@ ruleTester.run('no-v-html', rule, { { filename: 'test.vue', code: '', - options: [{ignorePattern: '^(?:html|.+Html)$'}], + options: [{ ignorePattern: '^(?:html|.+Html)$' }], errors: ["'v-html' directive can lead to XSS attack."] } ] From cd23ada1725e1f235e3eebcd5abbd0dcc99ff3bb Mon Sep 17 00:00:00 2001 From: Lucas Werkmeister Date: Tue, 29 Jul 2025 19:36:46 +0200 Subject: [PATCH 04/11] Add to docs --- docs/rules/no-v-html.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/rules/no-v-html.md b/docs/rules/no-v-html.md index 3098683e0..cbb730a1c 100644 --- a/docs/rules/no-v-html.md +++ b/docs/rules/no-v-html.md @@ -32,7 +32,15 @@ This rule reports all uses of `v-html` directive in order to reduce the risk of ## :wrench: Options -Nothing. +```json +{ + "vue/no-v-html": ["error", { + "ignorePattern": "/Html$" + }] +} +``` + +- `ignorePattern` ... disables reporting when the v-html directive references a variable matching this pattern. By default, all v-html uses are forbidden. ## :mute: When Not To Use It From 3e034183bbfc6a5a13262bc66611069a0c03cc66 Mon Sep 17 00:00:00 2001 From: Lucas Werkmeister Date: Wed, 30 Jul 2025 11:59:05 +0200 Subject: [PATCH 05/11] Apply suggestions from code review Co-authored-by: Flo Edelmann --- docs/rules/no-v-html.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/rules/no-v-html.md b/docs/rules/no-v-html.md index cbb730a1c..f6cbd65b5 100644 --- a/docs/rules/no-v-html.md +++ b/docs/rules/no-v-html.md @@ -40,7 +40,7 @@ This rule reports all uses of `v-html` directive in order to reduce the risk of } ``` -- `ignorePattern` ... disables reporting when the v-html directive references a variable matching this pattern. By default, all v-html uses are forbidden. +- `ignorePattern` ... disables reporting when the `v-html` directive references a variable matching this pattern. By default, all `v-html` uses are forbidden. ## :mute: When Not To Use It From 8185b212f2d65114f7cc50cfeccb8b028efa9451 Mon Sep 17 00:00:00 2001 From: Lucas Werkmeister Date: Wed, 30 Jul 2025 12:02:25 +0200 Subject: [PATCH 06/11] Change ignorePattern to plain RegExp --- docs/rules/no-v-html.md | 2 +- lib/rules/no-v-html.js | 9 ++++----- tests/lib/rules/no-v-html.js | 4 ++-- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/docs/rules/no-v-html.md b/docs/rules/no-v-html.md index f6cbd65b5..cda34b100 100644 --- a/docs/rules/no-v-html.md +++ b/docs/rules/no-v-html.md @@ -35,7 +35,7 @@ This rule reports all uses of `v-html` directive in order to reduce the risk of ```json { "vue/no-v-html": ["error", { - "ignorePattern": "/Html$" + "ignorePattern": ".Html$" }] } ``` diff --git a/lib/rules/no-v-html.js b/lib/rules/no-v-html.js index 98ebaa1af..6c3f222e3 100644 --- a/lib/rules/no-v-html.js +++ b/lib/rules/no-v-html.js @@ -4,7 +4,6 @@ */ 'use strict' const utils = require('../utils') -const { toRegExp } = require('../utils/regexp') module.exports = { meta: { @@ -33,17 +32,17 @@ module.exports = { /** @param {RuleContext} context */ create(context) { const options = context.options[0] - const ignoredVarMatcher = options?.ignorePattern - ? toRegExp(options.ignorePattern, { remove: 'g' }) + const ignoreRegEx = options?.ignorePattern + ? new RegExp(options.ignorePattern, 'u') : undefined return utils.defineTemplateBodyVisitor(context, { /** @param {VDirective} node */ "VAttribute[directive=true][key.name.name='html']"(node) { if ( - ignoredVarMatcher && + ignoreRegEx && node.value.expression.type === 'Identifier' && - ignoredVarMatcher.test(node.value.expression.name) + ignoreRegEx.test(node.value.expression.name) ) { return } diff --git a/tests/lib/rules/no-v-html.js b/tests/lib/rules/no-v-html.js index 0ea4eeb51..c4baa4a76 100644 --- a/tests/lib/rules/no-v-html.js +++ b/tests/lib/rules/no-v-html.js @@ -32,7 +32,7 @@ ruleTester.run('no-v-html', rule, { { filename: 'test.vue', code: '', - options: [{ ignorePattern: '/^(?:html|.+Html)$/' }] + options: [{ ignorePattern: '.Html$' }] } ], invalid: [ @@ -54,7 +54,7 @@ ruleTester.run('no-v-html', rule, { { filename: 'test.vue', code: '', - options: [{ ignorePattern: '^(?:html|.+Html)$' }], + options: [{ ignorePattern: '.Html$' }], errors: ["'v-html' directive can lead to XSS attack."] } ] From 0b6235ff834f948867f5980e9fa62495d7b23557 Mon Sep 17 00:00:00 2001 From: Lucas Werkmeister Date: Wed, 30 Jul 2025 12:06:18 +0200 Subject: [PATCH 07/11] Add related rules --- docs/rules/no-v-html.md | 5 +++++ docs/rules/no-v-text-v-html-on-component.md | 5 +++++ docs/rules/no-v-text.md | 5 +++++ 3 files changed, 15 insertions(+) diff --git a/docs/rules/no-v-html.md b/docs/rules/no-v-html.md index cda34b100..d86a3b925 100644 --- a/docs/rules/no-v-html.md +++ b/docs/rules/no-v-html.md @@ -46,6 +46,11 @@ This rule reports all uses of `v-html` directive in order to reduce the risk of If you are certain the content passed to `v-html` is sanitized HTML you can disable this rule. +## :couple: Related Rules + +- [vue/no-v-text](./no-v-text.md) +- [vue/no-v-text-v-html-on-component](./no-v-text-v-html-on-component.md) + ## :rocket: Version This rule was introduced in eslint-plugin-vue v4.7.0 diff --git a/docs/rules/no-v-text-v-html-on-component.md b/docs/rules/no-v-text-v-html-on-component.md index 9bbbcf7ab..aa1aceff6 100644 --- a/docs/rules/no-v-text-v-html-on-component.md +++ b/docs/rules/no-v-text-v-html-on-component.md @@ -84,6 +84,11 @@ If you use v-text / v-html on a component, it will overwrite the component's con +## :couple: Related Rules + +- [vue/no-v-text](./no-v-text.md) +- [vue/no-v-html](./no-v-html.md) + ## :rocket: Version This rule was introduced in eslint-plugin-vue v8.4.0 diff --git a/docs/rules/no-v-text.md b/docs/rules/no-v-text.md index 89bedbfbb..3fcd6811b 100644 --- a/docs/rules/no-v-text.md +++ b/docs/rules/no-v-text.md @@ -32,6 +32,11 @@ This rule reports all uses of `v-text` directive. Nothing. +## :couple: Related Rules + +- [vue/no-v-html](./no-v-html.md) +- [vue/no-v-text-v-html-on-component](./no-v-text-v-html-on-component.md) + ## :rocket: Version This rule was introduced in eslint-plugin-vue v7.17.0 From 79b608cf53d62ab3772a1136f60c1a487e45110a Mon Sep 17 00:00:00 2001 From: Lucas Werkmeister Date: Wed, 30 Jul 2025 12:13:03 +0200 Subject: [PATCH 08/11] Add ignorePattern example to no-v-html docs --- docs/rules/no-v-html.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/docs/rules/no-v-html.md b/docs/rules/no-v-html.md index d86a3b925..cbcc14665 100644 --- a/docs/rules/no-v-html.md +++ b/docs/rules/no-v-html.md @@ -42,6 +42,23 @@ This rule reports all uses of `v-html` directive in order to reduce the risk of - `ignorePattern` ... disables reporting when the `v-html` directive references a variable matching this pattern. By default, all `v-html` uses are forbidden. +### `{ "ignorePattern": ".Html$" }` + + + +```vue + +``` + + + ## :mute: When Not To Use It If you are certain the content passed to `v-html` is sanitized HTML you can disable this rule. From 37c7542f6b76fe97c4d3b9570c8e01e88f4c2cb7 Mon Sep 17 00:00:00 2001 From: Lucas Werkmeister Date: Thu, 31 Jul 2025 15:59:37 +0200 Subject: [PATCH 09/11] Make tests more strict Based on #2878. --- tests/lib/rules/no-v-html.js | 40 ++++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/tests/lib/rules/no-v-html.js b/tests/lib/rules/no-v-html.js index c4baa4a76..f2ae16b7d 100644 --- a/tests/lib/rules/no-v-html.js +++ b/tests/lib/rules/no-v-html.js @@ -39,23 +39,55 @@ ruleTester.run('no-v-html', rule, { { filename: 'test.vue', code: '', - errors: ["'v-html' directive can lead to XSS attack."] + errors: [ + { + message: "'v-html' directive can lead to XSS attack.", + line: 1, + column: 16, + endLine: 1, + endColumn: 28 + } + ] }, { filename: 'test.vue', code: '', - errors: ["'v-html' directive can lead to XSS attack."] + errors: [ + { + message: "'v-html' directive can lead to XSS attack.", + line: 1, + column: 15, + endLine: 1, + endColumn: 36 + } + ] }, { filename: 'test.vue', code: '', - errors: ["'v-html' directive can lead to XSS attack."] + errors: [ + { + message: "'v-html' directive can lead to XSS attack.", + line: 1, + column: 20, + endLine: 1, + endColumn: 26 + } + ] }, { filename: 'test.vue', code: '', options: [{ ignorePattern: '.Html$' }], - errors: ["'v-html' directive can lead to XSS attack."] + errors: [ + { + message: "'v-html' directive can lead to XSS attack.", + line: 1, + column: 16, + endLine: 1, + endColumn: 37 + } + ] } ] }) From 90380c69aff5ac35880d2223f1f06ec876dec270 Mon Sep 17 00:00:00 2001 From: Lucas Werkmeister Date: Thu, 31 Jul 2025 16:34:25 +0200 Subject: [PATCH 10/11] Change tests+docs ignorePattern --- docs/rules/no-v-html.md | 8 ++++---- tests/lib/rules/no-v-html.js | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/rules/no-v-html.md b/docs/rules/no-v-html.md index cbcc14665..507eaf9b4 100644 --- a/docs/rules/no-v-html.md +++ b/docs/rules/no-v-html.md @@ -35,22 +35,22 @@ This rule reports all uses of `v-html` directive in order to reduce the risk of ```json { "vue/no-v-html": ["error", { - "ignorePattern": ".Html$" + "ignorePattern": "^html" }] } ``` - `ignorePattern` ... disables reporting when the `v-html` directive references a variable matching this pattern. By default, all `v-html` uses are forbidden. -### `{ "ignorePattern": ".Html$" }` +### `{ "ignorePattern": "^html" }` - + ```vue