Skip to content

Commit f584154

Browse files
committed
Extract rule: template-link-rel-noopener
1 parent 5d2ad94 commit f584154

4 files changed

Lines changed: 93 additions & 0 deletions

File tree

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,12 @@ rules in templates can be disabled with eslint directives with mustache or html
304304
| [route-path-style](docs/rules/route-path-style.md) | enforce usage of kebab-case (instead of snake_case or camelCase) in route paths | | | 💡 |
305305
| [routes-segments-snake-case](docs/rules/routes-segments-snake-case.md) | enforce usage of snake_cased dynamic segments in routes || | |
306306

307+
### Security
308+
309+
| Name                       | Description | 💼 | 🔧 | 💡 |
310+
| :--------------------------------------------------------------------- | :-------------------------------------------------------------- | :- | :- | :- |
311+
| [template-link-rel-noopener](docs/rules/template-link-rel-noopener.md) | require rel="noopener noreferrer" on links with target="_blank" | | 🔧 | |
312+
307313
### Services
308314

309315
| Name                                      | Description | 💼 | 🔧 | 💡 |
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# ember/template-link-rel-noopener
2+
3+
🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix).
4+
5+
<!-- end auto-generated rule header -->
6+
7+
## Examples
8+
9+
See ember-template-lint documentation.
10+
11+
## References
12+
13+
- [ember-template-lint link-rel-noopener](https://github.com/ember-template-lint/ember-template-lint/blob/master/docs/rule/link-rel-noopener.md)
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/** @type {import('eslint').Rule.RuleModule} */
2+
module.exports = {
3+
meta: {
4+
type: 'problem',
5+
docs: {
6+
description: 'require rel="noopener noreferrer" on links with target="_blank"',
7+
category: 'Security',
8+
strictGjs: true,
9+
strictGts: true,
10+
url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/template-link-rel-noopener.md',
11+
},
12+
fixable: 'code',
13+
schema: [],
14+
messages: {
15+
missingRel: 'links with target="_blank" must have rel="noopener noreferrer"',
16+
},
17+
},
18+
create(context) {
19+
return {
20+
GlimmerElementNode(node) {
21+
if (node.tag !== 'a') {
22+
return;
23+
}
24+
25+
const targetAttr = node.attributes?.find((a) => a.name === 'target');
26+
if (!targetAttr?.value || targetAttr.value.type !== 'GlimmerTextNode') {
27+
return;
28+
}
29+
if (targetAttr.value.chars !== '_blank') {
30+
return;
31+
}
32+
33+
const relAttr = node.attributes?.find((a) => a.name === 'rel');
34+
const hasProperRel =
35+
relAttr?.value?.type === 'GlimmerTextNode' &&
36+
/noopener/.test(relAttr.value.chars) &&
37+
/noreferrer/.test(relAttr.value.chars);
38+
39+
if (!hasProperRel) {
40+
context.report({
41+
node: targetAttr,
42+
messageId: 'missingRel',
43+
fix(fixer) {
44+
const sourceCode = context.sourceCode;
45+
const openTag = sourceCode.getText(node).match(/^<a[^>]*/)[0];
46+
const insertPos = node.range[0] + openTag.length;
47+
return fixer.insertTextBeforeRange(
48+
[insertPos, insertPos],
49+
' rel="noopener noreferrer"'
50+
);
51+
},
52+
});
53+
}
54+
},
55+
};
56+
},
57+
};
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
const rule = require('../../../lib/rules/template-link-rel-noopener');
2+
const RuleTester = require('eslint').RuleTester;
3+
4+
const ruleTester = new RuleTester({
5+
parser: require.resolve('ember-eslint-parser'),
6+
parserOptions: { ecmaVersion: 2022, sourceType: 'module' },
7+
});
8+
ruleTester.run('template-link-rel-noopener', rule, {
9+
valid: ['<template><a href="/" target="_blank" rel="noopener noreferrer">Link</a></template>'],
10+
invalid: [
11+
{
12+
code: '<template><a href="/" target="_blank">Link</a></template>',
13+
output: '<template><a href="/" target="_blank" rel="noopener noreferrer">Link</a></template>',
14+
errors: [{ messageId: 'missingRel' }],
15+
},
16+
],
17+
});

0 commit comments

Comments
 (0)