Skip to content

Commit 59dcd65

Browse files
Gasparilayannickcr
authored andcommitted
Add jsx-no-target-blank rule
1 parent b76bd25 commit 59dcd65

File tree

5 files changed

+108
-1
lines changed

5 files changed

+108
-1
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ The plugin has a [recommended configuration](#user-content-recommended-configura
112112
* [jsx-no-bind](docs/rules/jsx-no-bind.md): Prevent usage of `.bind()` and arrow functions in JSX props
113113
* [jsx-no-duplicate-props](docs/rules/jsx-no-duplicate-props.md): Prevent duplicate props in JSX
114114
* [jsx-no-literals](docs/rules/jsx-no-literals.md): Prevent usage of unwrapped JSX strings
115+
* [jsx-no-target-blank](docs/rules/jsx-no-target-blank.md): Prevent usage of unsafe `target='\_blank'`
115116
* [jsx-no-undef](docs/rules/jsx-no-undef.md): Disallow undeclared variables in JSX
116117
* [jsx-pascal-case](docs/rules/jsx-pascal-case.md): Enforce PascalCase for user-defined JSX components
117118
* [jsx-sort-props](docs/rules/jsx-sort-props.md): Enforce props alphabetical sorting

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

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Prevent usage of unsafe target='_blank' (jsx-no-target-blank)
2+
3+
When creating a JSX element that has an a tag, it is often desired to have
4+
the link open in a new tab using the target='_blank' attribute. Using this
5+
attribute unaccompanied by rel='noreferrer noopener', however, is a severe
6+
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'.
8+
9+
## Rule Details
10+
11+
The following patterns are considered errors:
12+
13+
```javascript
14+
var Hello = <a target='_blank'></a>
15+
```
16+
17+
The following patterns are not considered erros:
18+
19+
```javascript
20+
var Hello = <p target='_blank'></p>
21+
var Hello = <a target='_blank' rel='noopener noreferrer'></a>
22+
var Hello = <a></a>
23+
```
24+
25+
## When Not To Use It
26+
27+
If you do not have any external links, you can disable this rule

index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ module.exports = {
4343
'no-string-refs': require('./lib/rules/no-string-refs'),
4444
'prefer-stateless-function': require('./lib/rules/prefer-stateless-function'),
4545
'require-render-return': require('./lib/rules/require-render-return'),
46-
'jsx-first-prop-new-line': require('./lib/rules/jsx-first-prop-new-line')
46+
'jsx-first-prop-new-line': require('./lib/rules/jsx-first-prop-new-line'),
47+
'jsx-no-target-blank': require('./lib/rules/jsx-no-target-blank')
4748
},
4849
configs: {
4950
recommended: {

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

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/**
2+
* @fileoverview Forbid target='_blank' attribute
3+
* @author Kevin Miller
4+
*/
5+
'use strict';
6+
7+
// ------------------------------------------------------------------------------
8+
// Rule Definition
9+
// ------------------------------------------------------------------------------
10+
11+
module.exports = function(context) {
12+
return {
13+
JSXAttribute: function(node) {
14+
if (node.name.name === 'target' && node.value.value === '_blank') {
15+
var relFound = false;
16+
var attrs = node.parent.attributes;
17+
for (var idx in attrs) {
18+
if (attrs[idx].name.name === 'rel') {
19+
var tags = attrs[idx].value.value.split(' ');
20+
if (tags.indexOf('noopener') >= 0 && tags.indexOf('noreferrer') >= 0) {
21+
relFound = true;
22+
break;
23+
}
24+
}
25+
}
26+
if (!relFound) {
27+
context.report(node, 'Using target="_blank" without rel="noopener noreferrer" ' +
28+
'is a security risk: see https://mathiasbynens.github.io/rel-noopener');
29+
}
30+
}
31+
}
32+
};
33+
};
34+
35+
module.exports.schema = [];
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/**
2+
* @fileoverview Forbid target='_blank' attribute
3+
* @author Kevin Miller
4+
*/
5+
'use strict';
6+
7+
// ------------------------------------------------------------------------------
8+
// Requirements
9+
// ------------------------------------------------------------------------------
10+
11+
var rule = require('../../../lib/rules/jsx-no-target-blank');
12+
var RuleTester = require('eslint').RuleTester;
13+
14+
var parserOptions = {
15+
ecmaVersion: 6,
16+
ecmaFeatures: {
17+
jsx: true
18+
}
19+
};
20+
21+
// ------------------------------------------------------------------------------
22+
// Tests
23+
// ------------------------------------------------------------------------------
24+
25+
var ruleTester = new RuleTester();
26+
ruleTester.run('jsx-no-target-blank', rule, {
27+
valid: [
28+
{code: '<a href="foobar"></a>', parserOptions: parserOptions},
29+
{code: '<a randomTag></a>', parserOptions: parserOptions},
30+
{code: '<a href="foobar" target="_blank" rel="noopener noreferrer"></a>', parserOptions: parserOptions}
31+
],
32+
invalid: [
33+
{code: '<a target="_blank"></a>', parserOptions: parserOptions,
34+
errors: [{message: 'Using target="_blank" without rel="noopener noreferrer" is a security risk:' +
35+
' see https://mathiasbynens.github.io/rel-noopener'}]},
36+
{code: '<a target="_blank" rel=""></a>', parserOptions: parserOptions,
37+
errors: [{message: 'Using target="_blank" without rel="noopener noreferrer" is a security risk:' +
38+
' see https://mathiasbynens.github.io/rel-noopener'}]},
39+
{code: '<a target="_blank" rel="noopenernoreferrer"></a>', parserOptions: parserOptions,
40+
errors: [{message: 'Using target="_blank" without rel="noopener noreferrer" is a security risk:' +
41+
' see https://mathiasbynens.github.io/rel-noopener'}]}
42+
]
43+
});

0 commit comments

Comments
 (0)