From 710a14e8f763b84bcc2c375212d097a7095ea4b4 Mon Sep 17 00:00:00 2001 From: David Petersen Date: Tue, 26 Jul 2016 13:14:16 -0500 Subject: [PATCH] Add new dom-elements-no-danger-with-children rule (Fixes #710) Prevents dangerouslySetInnerHTML and children from being used at the same time --- README.md | 2 + .../dom-elements-no-danger-with-children.md | 39 +++++++++++ index.js | 4 +- .../dom-elements-no-danger-with-children.js | 53 +++++++++++++++ .../dom-elements-no-danger-with-children.js | 67 +++++++++++++++++++ 5 files changed, 164 insertions(+), 1 deletion(-) create mode 100644 docs/rules/dom-elements-no-danger-with-children.md create mode 100644 lib/rules/dom-elements-no-danger-with-children.js create mode 100644 tests/lib/rules/dom-elements-no-danger-with-children.js diff --git a/README.md b/README.md index fb08b1cb6a..469a5523b6 100644 --- a/README.md +++ b/README.md @@ -80,6 +80,7 @@ Finally, enable all of the rules that you would like to use. Use [our preset](# # List of supported rules * [react/display-name](docs/rules/display-name.md): Prevent missing `displayName` in a React component definition +* [react/dom-elements-no-danger-with-children](docs/rules/dom-elements-no-danger-with-children.md): Prevent problem with children and props.dangerouslySetInnerHTML * [react/forbid-prop-types](docs/rules/forbid-prop-types.md): Forbid certain propTypes * [react/no-danger](docs/rules/no-danger.md): Prevent usage of dangerous JSX properties * [react/no-deprecated](docs/rules/no-deprecated.md): Prevent usage of deprecated methods @@ -156,6 +157,7 @@ See [ESLint documentation](http://eslint.org/docs/user-guide/configuring#extendi The rules enabled in this configuration are: * [react/display-name](docs/rules/display-name.md) +* [react/dom-elements-no-danger-with-children](docs/rules/dom-elements-no-danger-with-children.md) * [react/jsx-no-duplicate-props](docs/rules/jsx-no-duplicate-props.md) * [react/jsx-no-undef](docs/rules/jsx-no-undef.md) * [react/jsx-uses-react](docs/rules/jsx-uses-react.md) diff --git a/docs/rules/dom-elements-no-danger-with-children.md b/docs/rules/dom-elements-no-danger-with-children.md new file mode 100644 index 0000000000..07c0600073 --- /dev/null +++ b/docs/rules/dom-elements-no-danger-with-children.md @@ -0,0 +1,39 @@ +# Prevent problem with children and props.dangerouslySetInnerHTML (dom-elements-no-danger-with-children) + +This rule helps prevent problems caused by using children and the dangerouslySetInnerHTML prop at the same time + +## Rule Details + +The following patterns are considered warnings: + +```js +
+ Children +
+``` + +```js +React.createElement("div", { dangerouslySetInnerHTML: { __html: "HTML" } }, "Children"); +``` + + +The following patterns are not considered warnings: + +```js +
+``` + +```js +
+ Children +
+``` + +```js +React.createElement("div", { dangerouslySetInnerHTML: { __html: "HTML" } }); +``` + +```js +React.createElement("div", {}, "Children"); +``` + diff --git a/index.js b/index.js index a28494f79e..e1d6159765 100644 --- a/index.js +++ b/index.js @@ -50,7 +50,8 @@ var rules = { 'jsx-no-target-blank': require('./lib/rules/jsx-no-target-blank'), 'jsx-filename-extension': require('./lib/rules/jsx-filename-extension'), 'require-optimization': require('./lib/rules/require-optimization'), - 'no-find-dom-node': require('./lib/rules/no-find-dom-node') + 'no-find-dom-node': require('./lib/rules/no-find-dom-node'), + 'dom-elements-no-danger-with-children': require('./lib/rules/dom-elements-no-danger-with-children') }; var ruleNames = Object.keys(rules); @@ -70,6 +71,7 @@ module.exports = { }, rules: { 'react/display-name': 2, + 'react/dom-elements-no-danger-with-children': 2, 'react/jsx-no-duplicate-props': 2, 'react/jsx-no-undef': 2, 'react/jsx-uses-react': 2, diff --git a/lib/rules/dom-elements-no-danger-with-children.js b/lib/rules/dom-elements-no-danger-with-children.js new file mode 100644 index 0000000000..573e44941d --- /dev/null +++ b/lib/rules/dom-elements-no-danger-with-children.js @@ -0,0 +1,53 @@ +/** + * @fileoverview Report when a dom element is using both children and dangerouslySetInnerHTML + * @author David Petersen + */ +'use strict'; + +// ------------------------------------------------------------------------------ +// Rule Definition +// ------------------------------------------------------------------------------ +module.exports = { + meta: { + docs: { + description: 'Report when a dom element is using both children and dangerouslySetInnerHTML', + category: '', + recommended: true + }, + schema: [] // no options + }, + create: function(context) { + return { + JSXElement: function (node) { + if (!node.children.length) { + return; + } + var attributes = node.openingElement.attributes; + if (attributes) { + var jsxElement = attributes.find(function (attribute) { + return attribute.name.name === 'dangerouslySetInnerHTML'; + }); + if (jsxElement) { + context.report(node, 'Only set one of `children` or `props.dangerouslySetInnerHTML`'); + } + } + }, + CallExpression: function (node) { + if ( + node.callee + && node.callee.type === 'MemberExpression' + && node.callee.property.name === 'createElement' + && node.arguments.length > 2 + ) { + var props = node.arguments[1].properties; + var dangerously = props.find(function(prop) { + return prop.key.name === 'dangerouslySetInnerHTML'; + }); + if (dangerously) { + context.report(node, 'Only set one of `children` or `props.dangerouslySetInnerHTML`'); + } + } + } + }; + } +}; diff --git a/tests/lib/rules/dom-elements-no-danger-with-children.js b/tests/lib/rules/dom-elements-no-danger-with-children.js new file mode 100644 index 0000000000..564564a271 --- /dev/null +++ b/tests/lib/rules/dom-elements-no-danger-with-children.js @@ -0,0 +1,67 @@ +/** + * @fileoverview Report when a dom element is using both children and dangerouslySetInnerHTML + * @author David Petersen + */ +'use strict'; + +// ------------------------------------------------------------------------------ +// Requirements +// ------------------------------------------------------------------------------ + +var rule = require('../../../lib/rules/dom-elements-no-danger-with-children'); +var RuleTester = require('eslint').RuleTester; + +var parserOptions = { + ecmaVersion: 6, + ecmaFeatures: { + jsx: true + } +}; + +// ------------------------------------------------------------------------------ +// Tests +// ------------------------------------------------------------------------------ + +var ruleTester = new RuleTester(); +ruleTester.run('dom-elements-no-danger-with-children', rule, { + valid: [ + { + code: '
Children
', + parserOptions: parserOptions + }, + { + code: '
', + parserOptions: parserOptions + }, + { + code: 'React.createElement("div", { dangerouslySetInnerHTML: { __html: "HTML" } });', + parserOptions: parserOptions + }, + { + code: 'React.createElement("div", {}, "Children");', + parserOptions: parserOptions + } + ], + invalid: [ + { + code: [ + '
', + ' Children', + '
' + ].join('\n'), + errors: [{message: 'Only set one of `children` or `props.dangerouslySetInnerHTML`'}], + parserOptions: parserOptions + }, + { + code: [ + 'React.createElement(', + ' "div",', + ' { dangerouslySetInnerHTML: { __html: "HTML" } },', + ' "Children"', + ');' + ].join('\n'), + errors: [{message: 'Only set one of `children` or `props.dangerouslySetInnerHTML`'}], + parserOptions: parserOptions + } + ] +});