diff --git a/docs/rules/jsx-no-script-url.md b/docs/rules/jsx-no-script-url.md index 53f9202ef4..f3e1247bcf 100644 --- a/docs/rules/jsx-no-script-url.md +++ b/docs/rules/jsx-no-script-url.md @@ -20,3 +20,38 @@ The following patterns are **not** considered warnings: ``` + +## 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 + + + +``` diff --git a/lib/rules/jsx-no-script-url.js b/lib/rules/jsx-no-script-url.js index 660672ca90..0bbce3d8cc 100644 --- a/lib/rules/jsx-no-script-url.js +++ b/lib/rules/jsx-no-script-url.js @@ -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; @@ -33,13 +28,59 @@ 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 + } + } + }, + required: ['name', 'props'], + 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.'); } diff --git a/tests/lib/rules/jsx-no-script-url.js b/tests/lib/rules/jsx-no-script-url.js index a6aeb6c650..edb800aa42 100644 --- a/tests/lib/rules/jsx-no-script-url.js +++ b/tests/lib/rules/jsx-no-script-url.js @@ -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: [ @@ -49,5 +48,22 @@ ruleTester.run('jsx-no-script-url', rule, { }, { code: '', errors: defaultErrors + }, { + code: '', + errors: defaultErrors, + options: [[{name: 'Foo', props: ['to', 'href']}]] + }, { + code: '', + errors: defaultErrors, + options: [[{name: 'Foo', props: ['to', 'href']}]] + }, { + code: ` +
+ + +
+ `, + errors: [{message}, {message}], + options: [[{name: 'Foo', props: ['to', 'href']}, {name: 'Bar', props: ['link']}]] }] });