Skip to content

Commit

Permalink
[New] jsx-no-script-url`: added options to specify properties to be c…
Browse files Browse the repository at this point in the history
…hecked
  • Loading branch information
sergei-startsev committed Oct 1, 2019
1 parent 92f3cef commit 01d9fec
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 11 deletions.
35 changes: 35 additions & 0 deletions docs/rules/jsx-no-script-url.md
Expand Up @@ -20,3 +20,38 @@ The following patterns are **not** considered warnings:
<Foo href="javascript:"></Foo>
<a href={"javascript:"}></a>
```

## Rule Options
```json
{
"react/jsx-no-script-url": [
"error",
[
{
"name": "Link",
"props": ["to"]
},
{
"name": "Foo",
"props": ["href", "to"]
}
]
]
}
```

Allows you to indicate a specific list of properties used by a custom component to be checked.

### name
Component name.

### props
List of properties that should be validated.

The following patterns are considered warnings with the options listed above:

```jsx
<Link to="javascript:void(0)"></Link>
<Foo href="javascript:void(0)"></Foo>
<Foo to="javascript:void(0)"></Foo>
```
54 changes: 47 additions & 7 deletions lib/rules/jsx-no-script-url.js
Expand Up @@ -11,11 +11,6 @@ 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;
Expand All @@ -33,13 +28,58 @@ module.exports = {
recommended: false,
url: docsUrl('jsx-no-script-url')
},
schema: []
schema: [{
type: 'array',
items: {
type: 'object',
properties: {
name: {
type: 'string'
},
props: {
type: 'array',
items: {
type: 'string',
uniqueItems: true
}
}
},
additionalProperties: false
}
}]
},

create(context) {
const configuration = context.options[0] || [];
const elements = configuration.map(i => i.name);

function shouldVerifyElement(node) {
const name = node.name && node.name.name;
return name === 'a' || elements.indexOf(name) !== -1;
}

function shouldVerifyProp(node) {
const name = node.name && node.name.name;
const parentName = node.parent.name && node.parent.name.name;

if (parentName === 'a' && name === 'href') {
return true;
}

if (elements.indexOf(parentName) === -1) {
return false;
}

const el = configuration.find(i => i.name === parentName);
const props = el && el.props || [];

return node.name && props.indexOf(name) !== -1;
}

return {
JSXAttribute(node) {
if (node.parent.name.name === 'a' && isHref(node) && hasJavaScriptProtocol(node)) {
const parent = node.parent;
if (shouldVerifyElement(parent) && shouldVerifyProp(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.');
}
Expand Down
24 changes: 20 additions & 4 deletions tests/lib/rules/jsx-no-script-url.js
Expand Up @@ -25,10 +25,9 @@ const parserOptions = {
// ------------------------------------------------------------------------------

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.'
}];
const 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.';
const defaultErrors = [{message}];

ruleTester.run('jsx-no-script-url', rule, {
valid: [
Expand All @@ -49,5 +48,22 @@ ruleTester.run('jsx-no-script-url', rule, {
}, {
code: '<a href="j\n\n\na\rv\tascript:"></a>',
errors: defaultErrors
}, {
code: '<Foo to="javascript:"></Foo>',
errors: defaultErrors,
options: [[{name: 'Foo', props: ['to', 'href']}]]
}, {
code: '<Foo href="javascript:"></Foo>',
errors: defaultErrors,
options: [[{name: 'Foo', props: ['to', 'href']}]]
}, {
code: `
<div>
<Foo href="javascript:"></Foo>
<Bar link="javascript:"></Bar>
</div>
`,
errors: [{message}, {message}],
options: [[{name: 'Foo', props: ['to', 'href']}, {name: 'Bar', props: ['link']}]]
}]
});

0 comments on commit 01d9fec

Please sign in to comment.