Skip to content

Commit da8c6b2

Browse files
gbakernetljharb
authored andcommitted
[New] jsx-no-target-blank: add support for linkComponents setting
1 parent da71ff4 commit da8c6b2

File tree

3 files changed

+63
-15
lines changed

3 files changed

+63
-15
lines changed

docs/rules/jsx-no-target-blank.md

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,37 +20,61 @@ This rule aims to prevent user generated links from creating security vulnerabil
2020

2121
* enabled: for enabling the rule. 0=off, 1=warn, 2=error. Defaults to 0.
2222
* enforce: optional string, 'always' or 'never'
23+
* Link components can be something other than an `<a>`, see [shared settings](https://github.com/yannickcr/eslint-plugin-react/blob/master/README.md#configuration) for `linkComponents` configuration)
24+
25+
### `enforceDynamicLinks`
26+
27+
#### always
2328

24-
### always (default)
2529
`{"enforceDynamicLinks": "always"}` enforces the rule if the href is a dynamic link (default)
2630

2731
When {"enforceDynamicLinks": "always"} is set, the following patterns are considered errors:
2832

2933
```jsx
3034
var Hello = <a target='_blank' href="http://example.com/"></a>
31-
var Hello = <a target='_blank' href={ dynamicLink }></a>
35+
var Hello = <a target='_blank' href={dynamicLink}></a>
3236
```
3337

3438
The following patterns are **not** considered errors:
3539

3640
```jsx
37-
var Hello = <p target='_blank'></p>
38-
var Hello = <a target='_blank' rel='noopener noreferrer' href="http://example.com"></a>
39-
var Hello = <a target='_blank' href="relative/path/in/the/host"></a>
40-
var Hello = <a target='_blank' href="/absolute/path/in/the/host"></a>
41+
var Hello = <p target="_blank"></p>
42+
var Hello = <a target="_blank" rel="noopener noreferrer" href="http://example.com"></a>
43+
var Hello = <a target="_blank" href="relative/path/in/the/host"></a>
44+
var Hello = <a target="_blank" href="/absolute/path/in/the/host"></a>
4145
var Hello = <a></a>
4246
```
4347

44-
### never
48+
#### never
4549

4650
`{"enforceDynamicLinks": "never"}` does not enforce the rule if the href is a dynamic link
4751

4852
When {"enforceDynamicLinks": "never"} is set, the following patterns are **not** considered errors:
4953

5054
```jsx
51-
var Hello = <a target='_blank' href={ dynamicLink }></a>
55+
var Hello = <a target='_blank' href={dynamicLink}></a>
56+
```
57+
58+
### Link components
59+
60+
Link components can be something other than an `<a>`, see [shared settings](https://github.com/yannickcr/eslint-plugin-react/blob/master/README.md#configuration) for `linkComponents` configuration)
61+
62+
The following patterns are considered errors:
63+
64+
```jsx
65+
var Hello = <Link target="_blank" to="http://example.com/"></Link>
66+
var Hello = <Link target="_blank" to={dynamicLink}></Link>
67+
```
68+
69+
The following patterns are **not** considered errors:
70+
71+
```jsx
72+
var Hello = <Link target="_blank" rel="noopener noreferrer" to="http://example.com"></Link>
73+
var Hello = <Link target="_blank" to="relative/path/in/the/host"></Link>
74+
var Hello = <Link target="_blank" to="/absolute/path/in/the/host"></Link>
75+
var Hello = <Link />
5276
```
5377

5478
## When Not To Use It
5579

56-
If you do not have any external links, you can disable this rule
80+
If you do not have any external links, you can disable this rule

lib/rules/jsx-no-target-blank.js

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
'use strict';
66

77
const docsUrl = require('../util/docsUrl');
8+
const linkComponentsUtil = require('../util/linkComponents');
89

910
// ------------------------------------------------------------------------------
1011
// Rule Definition
@@ -18,16 +19,16 @@ function isTargetBlank(attr) {
1819
attr.value.value.toLowerCase() === '_blank';
1920
}
2021

21-
function hasExternalLink(element) {
22+
function hasExternalLink(element, linkAttribute) {
2223
return element.attributes.some(attr => attr.name &&
23-
attr.name.name === 'href' &&
24+
attr.name.name === linkAttribute &&
2425
attr.value.type === 'Literal' &&
2526
/^(?:\w+:|\/\/)/.test(attr.value.value));
2627
}
2728

28-
function hasDynamicLink(element) {
29+
function hasDynamicLink(element, linkAttribute) {
2930
return element.attributes.some(attr => attr.name &&
30-
attr.name.name === 'href' &&
31+
attr.name.name === linkAttribute &&
3132
attr.value.type === 'JSXExpressionContainer');
3233
}
3334

@@ -63,14 +64,17 @@ module.exports = {
6364
create: function(context) {
6465
const configuration = context.options[0] || {};
6566
const enforceDynamicLinks = configuration.enforceDynamicLinks || 'always';
67+
const components = linkComponentsUtil.getLinkComponents(context);
6668

6769
return {
6870
JSXAttribute: function(node) {
69-
if (node.parent.name.name !== 'a' || !isTargetBlank(node) || hasSecureRel(node.parent)) {
71+
if (!components.has(node.parent.name.name) || !isTargetBlank(node) || hasSecureRel(node.parent)) {
7072
return;
7173
}
7274

73-
if (hasExternalLink(node.parent) || (enforceDynamicLinks === 'always' && hasDynamicLink(node.parent))) {
75+
const linkAttribute = components.get(node.parent.name.name);
76+
77+
if (hasExternalLink(node.parent, linkAttribute) || (enforceDynamicLinks === 'always' && hasDynamicLink(node.parent, linkAttribute))) {
7478
context.report(node, 'Using target="_blank" without rel="noopener noreferrer" ' +
7579
'is a security risk: see https://mathiasbynens.github.io/rel-noopener');
7680
}

tests/lib/rules/jsx-no-target-blank.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,16 @@ ruleTester.run('jsx-no-target-blank', rule, {
4747
{
4848
code: '<a target="_blank" href={ dynamicLink }></a>',
4949
options: [{enforceDynamicLinks: 'never'}]
50+
},
51+
{
52+
code: '<Link target="_blank" href={ dynamicLink }></Link>',
53+
options: [{enforceDynamicLinks: 'never'}],
54+
settings: {linkComponents: ['Link']}
55+
},
56+
{
57+
code: '<Link target="_blank" to={ dynamicLink }></Link>',
58+
options: [{enforceDynamicLinks: 'never'}],
59+
settings: {linkComponents: {name: 'Link', linkAttribute: 'to'}}
5060
}
5161
],
5262
invalid: [{
@@ -83,5 +93,15 @@ ruleTester.run('jsx-no-target-blank', rule, {
8393
code: '<a target="_blank" href={ dynamicLink }></a>',
8494
options: [{enforceDynamicLinks: 'always'}],
8595
errors: defaultErrors
96+
}, {
97+
code: '<Link target="_blank" href={ dynamicLink }></Link>',
98+
options: [{enforceDynamicLinks: 'always'}],
99+
settings: {linkComponents: ['Link']},
100+
errors: defaultErrors
101+
}, {
102+
code: '<Link target="_blank" to={ dynamicLink }></Link>',
103+
options: [{enforceDynamicLinks: 'always'}],
104+
settings: {linkComponents: {name: 'Link', linkAttribute: 'to'}},
105+
errors: defaultErrors
86106
}]
87107
});

0 commit comments

Comments
 (0)