Skip to content

Commit d073dc7

Browse files
author
Ken Earley
committed
Add option to enforce no-target-blank on dynamic links
1 parent 3736e83 commit d073dc7

File tree

3 files changed

+47
-11
lines changed

3 files changed

+47
-11
lines changed

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

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,28 @@ When creating a JSX element that has an `a` tag, it is often desired to have
44
the link open in a new tab using the `target='_blank'` attribute. Using this
55
attribute unaccompanied by `rel='noreferrer noopener'`, however, is a severe
66
security vulnerability ([see here for more details](https://mathiasbynens.github.io/rel-noopener))
7-
This rules requires that you accompany all `target='_blank'` attributes with `rel='noreferrer noopener'`.
7+
This rules requires that you accompany `target='_blank'` attributes with `rel='noreferrer noopener'`.
88

99
## Rule Details
1010

11-
The following patterns are considered errors:
11+
This rule aims to prevent user generated links from creating security vulerabilities by requiring
12+
`rel='noreferrer noopener'` for external links, and optionally any dynamically generated links.
13+
14+
## Rule Options
15+
16+
There are two main options for the rule:
17+
18+
* `{"enforceDynamicLinks": "always"}` enforces the rule if the href is a dyanamic link (default)
19+
* `{"enforceDynamicLinks": "never"}` does not enforce the rule if the href is a dyamic link
20+
21+
22+
### always (default)
23+
24+
When {"enforceDynamicLinks": "always"} is set, the following patterns are considered errors:
1225

1326
```jsx
1427
var Hello = <a target='_blank' href="http://example.com/"></a>
28+
var Hello = <a target='_blank' href={ dynamicLink }></a>
1529
```
1630

1731
The following patterns are **not** considered errors:
@@ -24,6 +38,14 @@ var Hello = <a target='_blank' href="/absolute/path/in/the/host"></a>
2438
var Hello = <a></a>
2539
```
2640

41+
### never
42+
43+
When {"enforceDynamicLinks": "never"} is set, the following patterns are **not** considered errors:
44+
45+
```jsx
46+
var Hello = <a target='_blank' href={ dynamicLink }></a>
47+
```
48+
2749
## When Not To Use It
2850

2951
If you do not have any external links, you can disable this rule

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

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ function hasDynamicLink(element) {
2929
attr.value.type === 'JSXExpressionContainer');
3030
}
3131

32-
3332
function hasSecureRel(element) {
3433
return element.attributes.find(attr => {
3534
if (attr.type === 'JSXAttribute' && attr.name.name === 'rel') {
@@ -48,21 +47,28 @@ module.exports = {
4847
recommended: true,
4948
url: docsUrl('jsx-no-target-blank')
5049
},
51-
schema: []
50+
schema: [{
51+
type: 'object',
52+
properties: {
53+
enforceDynamicLinks: {
54+
enum: ['always', 'never']
55+
}
56+
},
57+
additionalProperties: false
58+
}]
5259
},
5360

5461
create: function(context) {
62+
const configuration = context.options[0] || {};
63+
const enforceDynamicLinks = configuration.enforceDynamicLinks || 'always';
64+
5565
return {
5666
JSXAttribute: function(node) {
57-
if (node.parent.name.name !== 'a') {
67+
if (node.parent.name.name !== 'a' || !isTargetBlank(node) || hasSecureRel(node.parent)) {
5868
return;
5969
}
6070

61-
if (
62-
isTargetBlank(node) &&
63-
(hasExternalLink(node.parent) || hasDynamicLink(node.parent)) &&
64-
!hasSecureRel(node.parent)
65-
) {
71+
if (hasExternalLink(node.parent) || (enforceDynamicLinks === 'always' && hasDynamicLink(node.parent))) {
6672
context.report(node, 'Using target="_blank" without rel="noopener noreferrer" ' +
6773
'is a security risk: see https://mathiasbynens.github.io/rel-noopener');
6874
}

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,11 @@ ruleTester.run('jsx-no-target-blank', rule, {
4343
{code: '<a target="_blank" rel={relValue}></a>'},
4444
{code: '<a target={targetValue} rel="noopener noreferrer"></a>'},
4545
{code: '<a target={targetValue} href="relative/path"></a>'},
46-
{code: '<a target={targetValue} href="/absolute/path"></a>'}
46+
{code: '<a target={targetValue} href="/absolute/path"></a>'},
47+
{
48+
code: '<a target="_blank" href={ dynamicLink }></a>',
49+
options: [{enforceDynamicLinks: 'never'}]
50+
}
4751
],
4852
invalid: [{
4953
code: '<a target="_blank" href="http://example.com"></a>',
@@ -75,5 +79,9 @@ ruleTester.run('jsx-no-target-blank', rule, {
7579
}, {
7680
code: '<a target="_blank" href={ dynamicLink }></a>',
7781
errors: defaultErrors
82+
}, {
83+
code: '<a target="_blank" href={ dynamicLink }></a>',
84+
options: [{enforceDynamicLinks: 'always'}],
85+
errors: defaultErrors
7886
}]
7987
});

0 commit comments

Comments
 (0)