From 72ac7ae8b30f0293823dc72227f875d91c4e37d1 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Wed, 25 Nov 2020 11:12:01 -0500 Subject: [PATCH 1/4] add fixer for the jsx-no-target-blank rule --- lib/rules/jsx-no-target-blank.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/rules/jsx-no-target-blank.js b/lib/rules/jsx-no-target-blank.js index 5c0f5353d7..1525935054 100644 --- a/lib/rules/jsx-no-target-blank.js +++ b/lib/rules/jsx-no-target-blank.js @@ -61,6 +61,7 @@ function hasSecureRel(element, allowReferrer) { module.exports = { meta: { + fixable: 'code', docs: { description: 'Forbid `target="_blank"` attribute without `rel="noreferrer"`', category: 'Best Practices', @@ -103,7 +104,10 @@ module.exports = { context.report({ node, message: 'Using target="_blank" without rel="noreferrer" ' - + 'is a security risk: see https://html.spec.whatwg.org/multipage/links.html#link-type-noopener' + + 'is a security risk: see https://html.spec.whatwg.org/multipage/links.html#link-type-noopener', + fix(fixer) { + return fixer.insertTextAfter(node, ' rel="noreferrer"'), + } }); } } From c03de3d401bbda5d5f05cb221e1d3ea3400486f9 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Fri, 27 Nov 2020 12:52:36 -0500 Subject: [PATCH 2/4] make fixer more robust, add output to fixed tests --- lib/rules/jsx-no-target-blank.js | 39 +++++++++++++++++++++++++- tests/lib/rules/jsx-no-target-blank.js | 25 +++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/lib/rules/jsx-no-target-blank.js b/lib/rules/jsx-no-target-blank.js index 1525935054..cc3445590b 100644 --- a/lib/rules/jsx-no-target-blank.js +++ b/lib/rules/jsx-no-target-blank.js @@ -106,7 +106,44 @@ module.exports = { message: 'Using target="_blank" without rel="noreferrer" ' + 'is a security risk: see https://html.spec.whatwg.org/multipage/links.html#link-type-noopener', fix(fixer) { - return fixer.insertTextAfter(node, ' rel="noreferrer"'), + const relAttribute = node.parent.attributes.find((attr) => attr.name.name === 'rel'); + if (!relAttribute) { + return fixer.insertTextAfter(node, ' rel="noreferrer"'); + } + + if (!relAttribute.value) { + return fixer.insertTextAfter(relAttribute, '="noreferrer"'); + } + + if (relAttribute.value.type === 'Literal') { + if (!relAttribute.value.value.trim()) { + return fixer.replaceText(relAttribute.value, '"noreferrer"'); + } + + const parts = relAttribute.value.value + .split('noreferrer') + .filter((part) => part); + return fixer.replaceText(relAttribute.value, `"${parts.join(' ')} noreferrer"`); + } + + if (relAttribute.value.expression.type === 'Literal') { + const valueType = typeof relAttribute.value.expression.value; + if ( + ['boolean', 'number', 'symbol', 'bigint', 'undefined'].includes(valueType) + || relAttribute.value.expression.value === null + ) { + return fixer.replaceText(relAttribute.value.expression, '"noreferrer"'); + } + + if (valueType === 'string') { + const parts = relAttribute.value.expression.value + .split('noreferrer') + .filter((part) => part); + return fixer.replaceText(relAttribute.value.expression, `"${parts.join(' ')} noreferrer"`); + } + } + + return null; } }); } diff --git a/tests/lib/rules/jsx-no-target-blank.js b/tests/lib/rules/jsx-no-target-blank.js index e4b813ade9..2e0b0dabee 100644 --- a/tests/lib/rules/jsx-no-target-blank.js +++ b/tests/lib/rules/jsx-no-target-blank.js @@ -96,60 +96,85 @@ ruleTester.run('jsx-no-target-blank', rule, { ], invalid: [{ code: '', + output: '', errors: defaultErrors }, { code: '', + output: '', errors: defaultErrors }, { code: '', + output: '', errors: defaultErrors }, { code: '', + output: '', errors: defaultErrors }, { code: '', + output: '', errors: defaultErrors }, { code: '', + output: '', errors: defaultErrors }, { code: '', + output: '', errors: defaultErrors }, { code: '', + output: '', errors: defaultErrors }, { code: '', + output: '', + errors: defaultErrors + }, { + code: '', errors: defaultErrors }, { code: '', + output: '', errors: defaultErrors }, { code: '', + output: '', + errors: defaultErrors + }, { + code: '', + output: '', errors: defaultErrors }, { code: '', + output: '', errors: defaultErrors }, { code: '', + output: '', errors: defaultErrors }, { code: '', + output: '', errors: defaultErrors }, { code: '', + output: '', errors: defaultErrors }, { code: '', + output: '', options: [{enforceDynamicLinks: 'always'}], errors: defaultErrors }, { code: '', + output: '', options: [{enforceDynamicLinks: 'always'}], settings: {linkComponents: ['Link']}, errors: defaultErrors }, { code: '', + output: '', options: [{enforceDynamicLinks: 'always'}], settings: {linkComponents: {name: 'Link', linkAttribute: 'to'}}, errors: defaultErrors From dae46b34b1b0c1e5267c4f678f542336af85e251 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Mon, 21 Dec 2020 11:58:59 -0500 Subject: [PATCH 3/4] add more fixing tests --- tests/lib/rules/jsx-no-target-blank.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/lib/rules/jsx-no-target-blank.js b/tests/lib/rules/jsx-no-target-blank.js index 2e0b0dabee..ce8764d018 100644 --- a/tests/lib/rules/jsx-no-target-blank.js +++ b/tests/lib/rules/jsx-no-target-blank.js @@ -102,6 +102,18 @@ ruleTester.run('jsx-no-target-blank', rule, { code: '', output: '', errors: defaultErrors + }, { + code: '', + output: '', + errors: defaultErrors + }, { + code: '', + output: '', + errors: defaultErrors + }, { + code: '', + output: '', + errors: defaultErrors }, { code: '', output: '', From de33e0b181852b45dca37ee0ee3e69261b1df737 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Wed, 23 Dec 2020 12:56:30 -0500 Subject: [PATCH 4/4] better fixer for literals and more performat --- lib/rules/jsx-no-target-blank.js | 13 ++++++++----- tests/lib/rules/jsx-no-target-blank.js | 12 ++++++------ 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/lib/rules/jsx-no-target-blank.js b/lib/rules/jsx-no-target-blank.js index cc3445590b..d49b39039c 100644 --- a/lib/rules/jsx-no-target-blank.js +++ b/lib/rules/jsx-no-target-blank.js @@ -127,18 +127,21 @@ module.exports = { } if (relAttribute.value.expression.type === 'Literal') { - const valueType = typeof relAttribute.value.expression.value; if ( - ['boolean', 'number', 'symbol', 'bigint', 'undefined'].includes(valueType) + typeof relAttribute.value.expression.value === 'undefined' + || typeof relAttribute.value.expression.value === 'boolean' + || typeof relAttribute.value.expression.value === 'number' + || typeof relAttribute.value.expression.value === 'symbol' + || typeof relAttribute.value.expression.value === 'bigint' || relAttribute.value.expression.value === null ) { - return fixer.replaceText(relAttribute.value.expression, '"noreferrer"'); + return fixer.replaceText(relAttribute.value.expression.parent, '"noreferrer"'); } - if (valueType === 'string') { + if (typeof relAttribute.value.expression.value === 'string') { const parts = relAttribute.value.expression.value .split('noreferrer') - .filter((part) => part); + .filter(Boolean); return fixer.replaceText(relAttribute.value.expression, `"${parts.join(' ')} noreferrer"`); } } diff --git a/tests/lib/rules/jsx-no-target-blank.js b/tests/lib/rules/jsx-no-target-blank.js index ce8764d018..6b51e355fe 100644 --- a/tests/lib/rules/jsx-no-target-blank.js +++ b/tests/lib/rules/jsx-no-target-blank.js @@ -104,15 +104,15 @@ ruleTester.run('jsx-no-target-blank', rule, { errors: defaultErrors }, { code: '', - output: '', + output: '', errors: defaultErrors }, { code: '', - output: '', + output: '', errors: defaultErrors }, { code: '', - output: '', + output: '', errors: defaultErrors }, { code: '', @@ -132,15 +132,15 @@ ruleTester.run('jsx-no-target-blank', rule, { errors: defaultErrors }, { code: '', - output: '', + output: '', errors: defaultErrors }, { code: '', - output: '', + output: '', errors: defaultErrors }, { code: '', - output: '', + output: '', errors: defaultErrors }, { code: '',