From 41ca26d4b8d9f9b3f13c422537466f6f08ec449c Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Mon, 14 Nov 2016 14:03:12 -0800 Subject: [PATCH] [Fix] Never call Object prototype builtins directly. Fixes #955. --- .eslintrc | 3 ++- index.js | 6 ++++-- lib/rules/display-name.js | 3 ++- lib/rules/jsx-closing-bracket-location.js | 8 +++++--- lib/rules/jsx-max-props-per-line.js | 4 +++- lib/rules/jsx-no-duplicate-props.js | 4 +++- lib/rules/jsx-tag-spacing.js | 3 ++- lib/rules/jsx-wrap-multilines.js | 4 +++- lib/rules/no-direct-mutation-state.js | 3 ++- lib/rules/no-multi-comp.js | 3 ++- lib/rules/no-set-state.js | 3 ++- lib/rules/no-unused-prop-types.js | 3 ++- lib/rules/prefer-stateless-function.js | 3 ++- lib/rules/prop-types.js | 3 ++- lib/rules/require-optimization.js | 3 ++- lib/rules/require-render-return.js | 3 ++- lib/rules/sort-comp.js | 9 +++++---- lib/util/Components.js | 7 ++++--- package.json | 1 + tests/lib/rules/jsx-tag-spacing.js | 2 ++ tests/lib/rules/no-unused-prop-types.js | 2 +- tests/lib/rules/prop-types.js | 2 +- 22 files changed, 54 insertions(+), 28 deletions(-) diff --git a/.eslintrc b/.eslintrc index 3e19d3868f..0667d9ddb5 100644 --- a/.eslintrc +++ b/.eslintrc @@ -152,6 +152,7 @@ "max-len": [2, 120], "max-params": 0, "max-statements": 0, - "no-plusplus": 0 + "no-plusplus": 0, + "no-prototype-builtins": 2 } } diff --git a/index.js b/index.js index 59a74d5d56..156f73a042 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,7 @@ 'use strict'; +var has = require('has'); + var allRules = { 'jsx-uses-react': require('./lib/rules/jsx-uses-react'), 'no-multi-comp': require('./lib/rules/no-multi-comp'), @@ -62,7 +64,7 @@ var allRules = { function filterRules(rules, predicate) { var result = {}; for (var key in rules) { - if (rules.hasOwnProperty(key) && predicate(rules[key])) { + if (has(rules, key) && predicate(rules[key])) { result[key] = rules[key]; } } @@ -72,7 +74,7 @@ function filterRules(rules, predicate) { function configureAsError(rules) { var result = {}; for (var key in rules) { - if (!rules.hasOwnProperty(key)) { + if (!has(rules, key)) { continue; } result['react/' + key] = 2; diff --git a/lib/rules/display-name.js b/lib/rules/display-name.js index db8e670e6a..8b36bfa0dd 100644 --- a/lib/rules/display-name.js +++ b/lib/rules/display-name.js @@ -4,6 +4,7 @@ */ 'use strict'; +var has = require('has'); var Components = require('../util/Components'); // ------------------------------------------------------------------------------ @@ -223,7 +224,7 @@ module.exports = { var list = components.list(); // Report missing display name for all components for (var component in list) { - if (!list.hasOwnProperty(component) || list[component].hasDisplayName) { + if (!has(list, component) || list[component].hasDisplayName) { continue; } reportMissingDisplayName(list[component]); diff --git a/lib/rules/jsx-closing-bracket-location.js b/lib/rules/jsx-closing-bracket-location.js index 9da181467e..67520506cb 100644 --- a/lib/rules/jsx-closing-bracket-location.js +++ b/lib/rules/jsx-closing-bracket-location.js @@ -4,6 +4,8 @@ */ 'use strict'; +var has = require('has'); + // ------------------------------------------------------------------------------ // Rule Definition // ------------------------------------------------------------------------------ @@ -70,16 +72,16 @@ module.exports = { options.selfClosing = config; } else if (typeof config === 'object') { // [1, {location: 'something'}] (back-compat) - if (config.hasOwnProperty('location')) { + if (has(config, 'location')) { options.nonEmpty = config.location; options.selfClosing = config.location; } // [1, {nonEmpty: 'something'}] - if (config.hasOwnProperty('nonEmpty')) { + if (has(config, 'nonEmpty')) { options.nonEmpty = config.nonEmpty; } // [1, {selfClosing: 'something'}] - if (config.hasOwnProperty('selfClosing')) { + if (has(config, 'selfClosing')) { options.selfClosing = config.selfClosing; } } diff --git a/lib/rules/jsx-max-props-per-line.js b/lib/rules/jsx-max-props-per-line.js index df7ddaf0ae..0838edc68c 100644 --- a/lib/rules/jsx-max-props-per-line.js +++ b/lib/rules/jsx-max-props-per-line.js @@ -5,6 +5,8 @@ 'use strict'; +var has = require('has'); + // ------------------------------------------------------------------------------ // Rule Definition // ------------------------------------------------------------------------------ @@ -55,7 +57,7 @@ module.exports = { }); for (var line in props) { - if (!props.hasOwnProperty(line)) { + if (!has(props, line)) { continue; } if (props[line].length > maximum) { diff --git a/lib/rules/jsx-no-duplicate-props.js b/lib/rules/jsx-no-duplicate-props.js index af91509925..092f0597b1 100644 --- a/lib/rules/jsx-no-duplicate-props.js +++ b/lib/rules/jsx-no-duplicate-props.js @@ -5,6 +5,8 @@ 'use strict'; +var has = require('has'); + // ------------------------------------------------------------------------------ // Rule Definition // ------------------------------------------------------------------------------ @@ -48,7 +50,7 @@ module.exports = { name = name.toLowerCase(); } - if (props.hasOwnProperty(name)) { + if (has(props, name)) { context.report({ node: decl, message: 'No duplicate props allowed' diff --git a/lib/rules/jsx-tag-spacing.js b/lib/rules/jsx-tag-spacing.js index 94814ba9bc..9bb3b5fc65 100644 --- a/lib/rules/jsx-tag-spacing.js +++ b/lib/rules/jsx-tag-spacing.js @@ -4,6 +4,7 @@ */ 'use strict'; +var has = require('has'); var getTokenBeforeClosingBracket = require('../util/getTokenBeforeClosingBracket'); // ------------------------------------------------------------------------------ @@ -205,7 +206,7 @@ module.exports = { afterOpening: 'never' }; for (var key in options) { - if (options.hasOwnProperty(key) && context.options[0].hasOwnProperty(key)) { + if (has(options, key) && has(context.options[0] || {}, key)) { options[key] = context.options[0][key]; } } diff --git a/lib/rules/jsx-wrap-multilines.js b/lib/rules/jsx-wrap-multilines.js index e2fef2c608..7ee23e706c 100644 --- a/lib/rules/jsx-wrap-multilines.js +++ b/lib/rules/jsx-wrap-multilines.js @@ -4,6 +4,8 @@ */ 'use strict'; +var has = require('has'); + // ------------------------------------------------------------------------------ // Constants // ------------------------------------------------------------------------------ @@ -79,7 +81,7 @@ module.exports = { function isEnabled(type) { var userOptions = context.options[0] || {}; - if (({}).hasOwnProperty.call(userOptions, type)) { + if (has(userOptions, type)) { return userOptions[type]; } return DEFAULTS[type]; diff --git a/lib/rules/no-direct-mutation-state.js b/lib/rules/no-direct-mutation-state.js index 310910a035..e24ce1b42a 100644 --- a/lib/rules/no-direct-mutation-state.js +++ b/lib/rules/no-direct-mutation-state.js @@ -4,6 +4,7 @@ */ 'use strict'; +var has = require('has'); var Components = require('../util/Components'); // ------------------------------------------------------------------------------ @@ -76,7 +77,7 @@ module.exports = { 'Program:exit': function() { var list = components.list(); for (var component in list) { - if (!list.hasOwnProperty(component) || isValid(list[component])) { + if (!has(list, component) || isValid(list[component])) { continue; } reportMutations(list[component]); diff --git a/lib/rules/no-multi-comp.js b/lib/rules/no-multi-comp.js index 2f165fe32c..f8fbd0b921 100644 --- a/lib/rules/no-multi-comp.js +++ b/lib/rules/no-multi-comp.js @@ -4,6 +4,7 @@ */ 'use strict'; +var has = require('has'); var Components = require('../util/Components'); // ------------------------------------------------------------------------------ @@ -60,7 +61,7 @@ module.exports = { var i = 0; for (var component in list) { - if (!list.hasOwnProperty(component) || isIgnored(list[component]) || ++i === 1) { + if (!has(list, component) || isIgnored(list[component]) || ++i === 1) { continue; } context.report({ diff --git a/lib/rules/no-set-state.js b/lib/rules/no-set-state.js index 8c2dffc975..9d848d4860 100644 --- a/lib/rules/no-set-state.js +++ b/lib/rules/no-set-state.js @@ -4,6 +4,7 @@ */ 'use strict'; +var has = require('has'); var Components = require('../util/Components'); // ------------------------------------------------------------------------------ @@ -73,7 +74,7 @@ module.exports = { 'Program:exit': function() { var list = components.list(); for (var component in list) { - if (!list.hasOwnProperty(component) || isValid(list[component])) { + if (!has(list, component) || isValid(list[component])) { continue; } reportSetStateUsages(list[component]); diff --git a/lib/rules/no-unused-prop-types.js b/lib/rules/no-unused-prop-types.js index e0357486af..67548599c3 100644 --- a/lib/rules/no-unused-prop-types.js +++ b/lib/rules/no-unused-prop-types.js @@ -7,6 +7,7 @@ // As for exceptions for props.children or props.className (and alike) look at // https://github.com/yannickcr/eslint-plugin-react/issues/7 +var has = require('has'); var Components = require('../util/Components'); var variable = require('../util/variable'); @@ -933,7 +934,7 @@ module.exports = { var list = components.list(); // Report undeclared proptypes for all classes for (var component in list) { - if (!list.hasOwnProperty(component) || !mustBeValidated(list[component])) { + if (!has(list, component) || !mustBeValidated(list[component])) { continue; } reportUnusedPropTypes(list[component]); diff --git a/lib/rules/prefer-stateless-function.js b/lib/rules/prefer-stateless-function.js index 540c717055..9cf896a616 100644 --- a/lib/rules/prefer-stateless-function.js +++ b/lib/rules/prefer-stateless-function.js @@ -6,6 +6,7 @@ */ 'use strict'; +var has = require('has'); var Components = require('../util/Components'); var versionUtil = require('../util/version'); @@ -371,7 +372,7 @@ module.exports = { var list = components.list(); for (var component in list) { if ( - !list.hasOwnProperty(component) || + !has(list, component) || hasOtherProperties(list[component].node) || list[component].useThis || list[component].useRef || diff --git a/lib/rules/prop-types.js b/lib/rules/prop-types.js index d452df66cc..ea3f5db403 100644 --- a/lib/rules/prop-types.js +++ b/lib/rules/prop-types.js @@ -7,6 +7,7 @@ // As for exceptions for props.children or props.className (and alike) look at // https://github.com/yannickcr/eslint-plugin-react/issues/7 +var has = require('has'); var Components = require('../util/Components'); var variable = require('../util/variable'); @@ -915,7 +916,7 @@ module.exports = { var list = components.list(); // Report undeclared proptypes for all classes for (var component in list) { - if (!list.hasOwnProperty(component) || !mustBeValidated(list[component])) { + if (!has(list, component) || !mustBeValidated(list[component])) { continue; } reportUndeclaredPropTypes(list[component]); diff --git a/lib/rules/require-optimization.js b/lib/rules/require-optimization.js index e843e278ff..7a49a259bc 100644 --- a/lib/rules/require-optimization.js +++ b/lib/rules/require-optimization.js @@ -4,6 +4,7 @@ */ 'use strict'; +var has = require('has'); var Components = require('../util/Components'); module.exports = { @@ -219,7 +220,7 @@ module.exports = { // Report missing shouldComponentUpdate for all components for (var component in list) { - if (!list.hasOwnProperty(component) || list[component].hasSCU) { + if (!has(list, component) || list[component].hasSCU) { continue; } reportMissingOptimization(list[component]); diff --git a/lib/rules/require-render-return.js b/lib/rules/require-render-return.js index 6c212053ea..030444f432 100644 --- a/lib/rules/require-render-return.js +++ b/lib/rules/require-render-return.js @@ -4,6 +4,7 @@ */ 'use strict'; +var has = require('has'); var Components = require('../util/Components'); // ------------------------------------------------------------------------------ @@ -111,7 +112,7 @@ module.exports = { var list = components.list(); for (var component in list) { if ( - !list.hasOwnProperty(component) || + !has(list, component) || !hasRenderMethod(list[component].node) || list[component].hasReturnStatement || (!utils.isES5Component(list[component].node) && !utils.isES6Component(list[component].node)) diff --git a/lib/rules/sort-comp.js b/lib/rules/sort-comp.js index 422fb298b9..4c324ec75b 100644 --- a/lib/rules/sort-comp.js +++ b/lib/rules/sort-comp.js @@ -4,6 +4,7 @@ */ 'use strict'; +var has = require('has'); var util = require('util'); var Components = require('../util/Components'); @@ -24,7 +25,7 @@ function getMethodsOrder(defaultConfig, userConfig) { var entry; for (var i = 0, j = order.length; i < j; i++) { entry = order[i]; - if (groups.hasOwnProperty(entry)) { + if (has(groups, entry)) { config = config.concat(groups[entry]); } else { config.push(entry); @@ -236,7 +237,7 @@ module.exports = { */ function dedupeErrors() { for (var i in errors) { - if (!errors.hasOwnProperty(i)) { + if (!has(errors, i)) { continue; } var index = errors[i].closest.ref.index; @@ -262,7 +263,7 @@ module.exports = { var indexA; var indexB; for (var i in errors) { - if (!errors.hasOwnProperty(i)) { + if (!has(errors, i)) { continue; } @@ -412,7 +413,7 @@ module.exports = { 'Program:exit': function() { var list = components.list(); for (var component in list) { - if (!list.hasOwnProperty(component)) { + if (!has(list, component)) { continue; } var properties = getComponentProperties(list[component].node); diff --git a/lib/util/Components.js b/lib/util/Components.js index 181dfa2493..c3b299957d 100644 --- a/lib/util/Components.js +++ b/lib/util/Components.js @@ -4,6 +4,7 @@ */ 'use strict'; +var has = require('has'); var util = require('util'); var doctrine = require('doctrine'); var variableUtil = require('./variable'); @@ -83,7 +84,7 @@ Components.prototype.list = function() { var usedPropTypes = {}; // Find props used in components for which we are not confident for (var i in this._list) { - if (!this._list.hasOwnProperty(i) || this._list[i].confidence >= 2) { + if (!has(this._list, i) || this._list[i].confidence >= 2) { continue; } var component = null; @@ -105,7 +106,7 @@ Components.prototype.list = function() { } // Assign used props in not confident components to the parent component for (var j in this._list) { - if (!this._list.hasOwnProperty(j) || this._list[j].confidence < 2) { + if (!has(this._list, j) || this._list[j].confidence < 2) { continue; } var id = this._getId(this._list[j].node); @@ -126,7 +127,7 @@ Components.prototype.list = function() { Components.prototype.length = function() { var length = 0; for (var i in this._list) { - if (!this._list.hasOwnProperty(i) || this._list[i].confidence < 2) { + if (!has(this._list, i) || this._list[i].confidence < 2) { continue; } length++; diff --git a/package.json b/package.json index f81aa5e79b..48496f6cc3 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "bugs": "https://github.com/yannickcr/eslint-plugin-react/issues", "dependencies": { "doctrine": "^1.2.2", + "has": "^1.0.1", "jsx-ast-utils": "^1.3.3" }, "devDependencies": { diff --git a/tests/lib/rules/jsx-tag-spacing.js b/tests/lib/rules/jsx-tag-spacing.js index 5432cf0f19..5a6f954ff6 100644 --- a/tests/lib/rules/jsx-tag-spacing.js +++ b/tests/lib/rules/jsx-tag-spacing.js @@ -58,6 +58,8 @@ var ruleTester = new RuleTester({ }); ruleTester.run('jsx-tag-spacing', rule, { valid: [{ + code: '' + }, { code: '', options: beforeSelfClosingOptions('always') }, { diff --git a/tests/lib/rules/no-unused-prop-types.js b/tests/lib/rules/no-unused-prop-types.js index 13936d5fc4..8d255add46 100644 --- a/tests/lib/rules/no-unused-prop-types.js +++ b/tests/lib/rules/no-unused-prop-types.js @@ -233,7 +233,7 @@ ruleTester.run('no-unused-prop-types', rule, { code: [ 'class Hello extends React.Component {', ' render() {', - ' if (this.props.hasOwnProperty(\'firstname\')) {', + ' if (Object.prototype.hasOwnProperty.call(this.props, \'firstname\')) {', ' return
Hello {this.props.firstname}
;', ' }', ' return
Hello
;', diff --git a/tests/lib/rules/prop-types.js b/tests/lib/rules/prop-types.js index 3a60db4a6b..22ddf05b6c 100644 --- a/tests/lib/rules/prop-types.js +++ b/tests/lib/rules/prop-types.js @@ -241,7 +241,7 @@ ruleTester.run('prop-types', rule, { code: [ 'class Hello extends React.Component {', ' render() {', - ' if (this.props.hasOwnProperty(\'firstname\')) {', + ' if (Object.prototype.hasOwnProperty.call(this.props, \'firstname\')) {', ' return
Hello {this.props.firstname}
;', ' }', ' return
Hello
;',