Skip to content

Commit

Permalink
[New] Add jsx-no-script-url to prevent usage of javascript: URLs
Browse files Browse the repository at this point in the history
  • Loading branch information
sergei-startsev committed Sep 25, 2019
1 parent 4a05fa2 commit 9ec97c4
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Expand Up @@ -168,6 +168,7 @@ Enable the rules that you would like to use.
* [react/jsx-no-comment-textnodes](docs/rules/jsx-no-comment-textnodes.md): Prevent comments from being inserted as text nodes
* [react/jsx-no-duplicate-props](docs/rules/jsx-no-duplicate-props.md): Prevent duplicate props in JSX
* [react/jsx-no-literals](docs/rules/jsx-no-literals.md): Prevent usage of unwrapped JSX strings
* [react/jsx-no-script-url](docs/rules/jsx-no-script-url.md): Prevent usage of `javascript:` URLs
* [react/jsx-no-target-blank](docs/rules/jsx-no-target-blank.md): Prevent usage of unsafe `target='_blank'`
* [react/jsx-no-undef](docs/rules/jsx-no-undef.md): Disallow undeclared variables in JSX
* [react/jsx-no-useless-fragment](docs/rules/jsx-no-useless-fragment.md): Disallow unnescessary fragments (fixable)
Expand Down
22 changes: 22 additions & 0 deletions docs/rules/jsx-no-script-url.md
@@ -0,0 +1,22 @@
# Prevent usage of `javascript:` URLs (react/jsx-no-script-url)

**In React 16.9** any URLs starting with `javascript:` [scheme](https://wiki.whatwg.org/wiki/URL_schemes#javascript:_URLs) log a warning.
React considers the pattern as a dangerous attack surface, see [details](https://reactjs.org/blog/2019/08/08/react-v16.9.0.html#deprecating-javascript-urls).
**In a future major release**, React will throw an error if it encounters a `javascript:` URL.

## Rule Details

The following patterns are considered warnings:

```jsx
<a href="javascript:"></a>
<a href="javascript:void(0)"></a>
<a href="j\n\n\na\rv\tascript:"></a>
```

The following patterns are **not** considered warnings:

```jsx
<Foo href="javascript:"></Foo>
<a href={"javascript:"}></a>
```
1 change: 1 addition & 0 deletions index.js
Expand Up @@ -34,6 +34,7 @@ const allRules = {
'jsx-no-comment-textnodes': require('./lib/rules/jsx-no-comment-textnodes'),
'jsx-no-duplicate-props': require('./lib/rules/jsx-no-duplicate-props'),
'jsx-no-literals': require('./lib/rules/jsx-no-literals'),
'jsx-no-script-url': require('./lib/rules/jsx-no-script-url'),
'jsx-no-target-blank': require('./lib/rules/jsx-no-target-blank'),
'jsx-no-useless-fragment': require('./lib/rules/jsx-no-useless-fragment'),
'jsx-one-expression-per-line': require('./lib/rules/jsx-one-expression-per-line'),
Expand Down
48 changes: 48 additions & 0 deletions lib/rules/jsx-no-script-url.js
@@ -0,0 +1,48 @@
/**
* @fileoverview Prevent usage of `javascript:` URLs
* @author Sergei Startsev
*/
'use strict';

const docsUrl = require('../util/docsUrl');

// ------------------------------------------------------------------------------
// Rule Definition
// ------------------------------------------------------------------------------

function isHref(attr) {
return attr.name &&
attr.name.name === 'href';
}

// https://github.com/facebook/react/blob/d0ebde77f6d1232cefc0da184d731943d78e86f2/packages/react-dom/src/shared/sanitizeURL.js#L30
/* eslint-disable-next-line max-len, no-control-regex */
const isJavaScriptProtocol = /^[\u0000-\u001F ]*j[\r\n\t]*a[\r\n\t]*v[\r\n\t]*a[\r\n\t]*s[\r\n\t]*c[\r\n\t]*r[\r\n\t]*i[\r\n\t]*p[\r\n\t]*t[\r\n\t]*\:/i;

function hasJavaScriptProtocol(attr) {
return attr.value.type === 'Literal' &&
isJavaScriptProtocol.test(attr.value.value);
}

module.exports = {
meta: {
docs: {
description: 'Forbid `javascript:` URLs',
category: 'Best Practices',
recommended: false,
url: docsUrl('jsx-no-script-url')
},
schema: []
},

create: function (context) {
return {
JSXAttribute: function (node) {
if (node.parent.name.name === 'a' && isHref(node) && hasJavaScriptProtocol(node)) {
context.report(node, 'A future version of React will block javascript: URLs as a security precaution. ' +
'Use event handlers instead if you can. If you need to generate unsafe HTML try using dangerouslySetInnerHTML instead.');
}
}
};
}
};
52 changes: 52 additions & 0 deletions tests/lib/rules/jsx-no-script-url.js
@@ -0,0 +1,52 @@
/**
* @fileoverview Prevent usage of `javascript:` URLs
* @author Sergei Startsev
*/
'use strict';

// ------------------------------------------------------------------------------
// Requirements
// ------------------------------------------------------------------------------

const rule = require('../../../lib/rules/jsx-no-script-url');
const RuleTester = require('eslint').RuleTester;

const parserOptions = {
ecmaVersion: 2018,
sourceType: 'module',
ecmaFeatures: {
jsx: true
}
};

// ------------------------------------------------------------------------------
// Tests
// ------------------------------------------------------------------------------

const ruleTester = new RuleTester({parserOptions});
const defaultErrors = [{
message: 'A future version of React will block javascript: URLs as a security precaution. ' +
'Use event handlers instead if you can. If you need to generate unsafe HTML try using dangerouslySetInnerHTML instead.'
}];

ruleTester.run('jsx-no-script-url', rule, {
valid: [
{code: '<a href="https://reactjs.org"></a>'},
{code: '<a href="mailto:foo@bar.com"></a>'},
{code: '<a href="#"></a>'},
{code: '<a href=""></a>'},
{code: '<a name="foo"></a>'},
{code: '<a href={"javascript:"}></a>'},
{code: '<Foo href="javascript:"></Foo>'}
],
invalid: [{
code: '<a href="javascript:"></a>',
errors: defaultErrors
}, {
code: '<a href="javascript:void(0)"></a>',
errors: defaultErrors
}, {
code: '<a href="j\n\n\na\rv\tascript:"></a>',
errors: defaultErrors
}]
});

0 comments on commit 9ec97c4

Please sign in to comment.