| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -156,6 +156,7 @@ rules: | |||
| 156 | 156 | - name: SubtleCrypto | |
| 157 | 157 | message: Use `const { SubtleCrypto } = require('internal/crypto/webcrypto');` instead of the global. | |
| 158 | 158 | # Custom rules in tools/eslint-rules | |
| 159 | + node-core/avoid-prototype-pollution: error | ||
| 159 | 160 | node-core/lowercase-name-for-primitive: error | |
| 160 | 161 | node-core/non-ascii-character: error | |
| 161 | 162 | node-core/no-array-destructuring: error | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -78,7 +78,7 @@ function copyPropsRenamed(src, dest, prefix) { | |||
| 78 | 78 | copyAccessor(dest, prefix, newKey, desc); | |
| 79 | 79 | } else { | |
| 80 | 80 | const name = `${prefix}${newKey}`; | |
| 81 | - ReflectDefineProperty(dest, name, desc); | ||
| 81 | + ReflectDefineProperty(dest, name, { __proto__: null, ...desc }); | ||
| 82 | 82 | if (varargsMethods.includes(name)) { | |
| 83 | 83 | ReflectDefineProperty(dest, `${name}Apply`, { | |
| 84 | 84 | __proto__: null, | |
@@ -105,7 +105,7 @@ function copyPropsRenamedBound(src, dest, prefix) { | |||
| 105 | 105 | } | |
| 106 | 106 | ||
| 107 | 107 | const name = `${prefix}${newKey}`; | |
| 108 | - ReflectDefineProperty(dest, name, desc); | ||
| 108 | + ReflectDefineProperty(dest, name, { __proto__: null, ...desc }); | ||
| 109 | 109 | if (varargsMethods.includes(name)) { | |
| 110 | 110 | ReflectDefineProperty(dest, `${name}Apply`, { | |
| 111 | 111 | __proto__: null, | |
@@ -129,7 +129,7 @@ function copyPrototype(src, dest, prefix) { | |||
| 129 | 129 | } | |
| 130 | 130 | ||
| 131 | 131 | const name = `${prefix}${newKey}`; | |
| 132 | - ReflectDefineProperty(dest, name, desc); | ||
| 132 | + ReflectDefineProperty(dest, name, { __proto__: null, ...desc }); | ||
| 133 | 133 | if (varargsMethods.includes(name)) { | |
| 134 | 134 | ReflectDefineProperty(dest, `${name}Apply`, { | |
| 135 | 135 | __proto__: null, | |
@@ -312,7 +312,7 @@ const copyProps = (src, dest) => { | |||
| 312 | 312 | ReflectDefineProperty( | |
| 313 | 313 | dest, | |
| 314 | 314 | key, | |
| 315 | - ReflectGetOwnPropertyDescriptor(src, key)); | ||
| 315 | + { __proto__: null, ...ReflectGetOwnPropertyDescriptor(src, key) }); | ||
| 316 | 316 | } | |
| 317 | 317 | }); | |
| 318 | 318 | }; | |
@@ -340,7 +340,7 @@ const makeSafe = (unsafe, safe) => { | |||
| 340 | 340 | return new SafeIterator(this); | |
| 341 | 341 | }; | |
| 342 | 342 | } | |
| 343 | - ReflectDefineProperty(safe.prototype, key, desc); | ||
| 343 | + ReflectDefineProperty(safe.prototype, key, { __proto__: null, ...desc }); | ||
| 344 | 344 | } | |
| 345 | 345 | }); | |
| 346 | 346 | } else { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,147 @@ | |||
| 1 | + 'use strict'; | ||
| 2 | + | ||
| 3 | + const common = require('../common'); | ||
| 4 | + if ((!common.hasCrypto) || (!common.hasIntl)) { | ||
| 5 | + common.skip('ESLint tests require crypto and Intl'); | ||
| 6 | + } | ||
| 7 | + | ||
| 8 | + common.skipIfEslintMissing(); | ||
| 9 | + | ||
| 10 | + const RuleTester = require('../../tools/node_modules/eslint').RuleTester; | ||
| 11 | + const rule = require('../../tools/eslint-rules/avoid-prototype-pollution'); | ||
| 12 | + | ||
| 13 | + new RuleTester({ | ||
| 14 | + parserOptions: { ecmaVersion: 2022 }, | ||
| 15 | + }) | ||
| 16 | + .run('property-descriptor-no-prototype-pollution', rule, { | ||
| 17 | + valid: [ | ||
| 18 | + 'ObjectDefineProperties({}, {})', | ||
| 19 | + 'ObjectCreate(null, {})', | ||
| 20 | + 'ObjectDefineProperties({}, { key })', | ||
| 21 | + 'ObjectCreate(null, { key })', | ||
| 22 | + 'ObjectDefineProperties({}, { ...spread })', | ||
| 23 | + 'ObjectCreate(null, { ...spread })', | ||
| 24 | + 'ObjectDefineProperties({}, { key: valueDescriptor })', | ||
| 25 | + 'ObjectCreate(null, { key: valueDescriptor })', | ||
| 26 | + 'ObjectDefineProperties({}, { key: { ...{}, __proto__: null } })', | ||
| 27 | + 'ObjectCreate(null, { key: { ...{}, __proto__: null } })', | ||
| 28 | + 'ObjectDefineProperties({}, { key: { __proto__: null } })', | ||
| 29 | + 'ObjectCreate(null, { key: { __proto__: null } })', | ||
| 30 | + 'ObjectDefineProperties({}, { key: { __proto__: null, enumerable: true } })', | ||
| 31 | + 'ObjectCreate(null, { key: { __proto__: null, enumerable: true } })', | ||
| 32 | + 'ObjectDefineProperties({}, { key: { "__proto__": null } })', | ||
| 33 | + 'ObjectCreate(null, { key: { "__proto__": null } })', | ||
| 34 | + 'ObjectDefineProperties({}, { key: { \'__proto__\': null } })', | ||
| 35 | + 'ObjectCreate(null, { key: { \'__proto__\': null } })', | ||
| 36 | + 'ObjectDefineProperty({}, "key", ObjectCreate(null))', | ||
| 37 | + 'ReflectDefineProperty({}, "key", ObjectCreate(null))', | ||
| 38 | + 'ObjectDefineProperty({}, "key", valueDescriptor)', | ||
| 39 | + 'ReflectDefineProperty({}, "key", valueDescriptor)', | ||
| 40 | + 'ObjectDefineProperty({}, "key", { __proto__: null })', | ||
| 41 | + 'ReflectDefineProperty({}, "key", { __proto__: null })', | ||
| 42 | + 'ObjectDefineProperty({}, "key", { __proto__: null, enumerable: true })', | ||
| 43 | + 'ReflectDefineProperty({}, "key", { __proto__: null, enumerable: true })', | ||
| 44 | + 'ObjectDefineProperty({}, "key", { "__proto__": null })', | ||
| 45 | + 'ReflectDefineProperty({}, "key", { "__proto__": null })', | ||
| 46 | + 'ObjectDefineProperty({}, "key", { \'__proto__\': null })', | ||
| 47 | + 'ReflectDefineProperty({}, "key", { \'__proto__\': null })', | ||
| 48 | + ], | ||
| 49 | + invalid: [ | ||
| 50 | + { | ||
| 51 | + code: 'ObjectDefineProperties({}, ObjectGetOwnPropertyDescriptors({}))', | ||
| 52 | + errors: [{ message: /prototype pollution/ }], | ||
| 53 | + }, | ||
| 54 | + { | ||
| 55 | + code: 'ObjectCreate(null, ObjectGetOwnPropertyDescriptors({}))', | ||
| 56 | + errors: [{ message: /prototype pollution/ }], | ||
| 57 | + }, | ||
| 58 | + { | ||
| 59 | + code: 'ObjectDefineProperties({}, { key: {} })', | ||
| 60 | + errors: [{ message: /null-prototype/ }], | ||
| 61 | + }, | ||
| 62 | + { | ||
| 63 | + code: 'ObjectCreate(null, { key: {} })', | ||
| 64 | + errors: [{ message: /null-prototype/ }], | ||
| 65 | + }, | ||
| 66 | + { | ||
| 67 | + code: 'ObjectDefineProperties({}, { key: { [void 0]: { ...{ __proto__: null } } } })', | ||
| 68 | + errors: [{ message: /null-prototype/ }], | ||
| 69 | + }, | ||
| 70 | + { | ||
| 71 | + code: 'ObjectCreate(null, { key: { [void 0]: { ...{ __proto__: null } } } })', | ||
| 72 | + errors: [{ message: /null-prototype/ }], | ||
| 73 | + }, | ||
| 74 | + { | ||
| 75 | + code: 'ObjectDefineProperties({}, { key: { __proto__: Object.prototype } })', | ||
| 76 | + errors: [{ message: /null-prototype/ }], | ||
| 77 | + }, | ||
| 78 | + { | ||
| 79 | + code: 'ObjectCreate(null, { key: { __proto__: Object.prototype } })', | ||
| 80 | + errors: [{ message: /null-prototype/ }], | ||
| 81 | + }, | ||
| 82 | + { | ||
| 83 | + code: 'ObjectDefineProperties({}, { key: { [`__proto__`]: null } })', | ||
| 84 | + errors: [{ message: /null-prototype/ }], | ||
| 85 | + }, | ||
| 86 | + { | ||
| 87 | + code: 'ObjectCreate(null, { key: { [`__proto__`]: null } })', | ||
| 88 | + errors: [{ message: /null-prototype/ }], | ||
| 89 | + }, | ||
| 90 | + { | ||
| 91 | + code: 'ObjectDefineProperties({}, { key: { enumerable: true } })', | ||
| 92 | + errors: [{ message: /null-prototype/ }], | ||
| 93 | + }, | ||
| 94 | + { | ||
| 95 | + code: 'ObjectCreate(null, { key: { enumerable: true } })', | ||
| 96 | + errors: [{ message: /null-prototype/ }], | ||
| 97 | + }, | ||
| 98 | + { | ||
| 99 | + code: 'ObjectDefineProperty({}, "key", {})', | ||
| 100 | + errors: [{ message: /null-prototype/ }], | ||
| 101 | + }, | ||
| 102 | + { | ||
| 103 | + code: 'ReflectDefineProperty({}, "key", {})', | ||
| 104 | + errors: [{ message: /null-prototype/ }], | ||
| 105 | + }, | ||
| 106 | + { | ||
| 107 | + code: 'ObjectDefineProperty({}, "key", ObjectGetOwnPropertyDescriptor({}, "key"))', | ||
| 108 | + errors: [{ message: /prototype pollution/ }], | ||
| 109 | + }, | ||
| 110 | + { | ||
| 111 | + code: 'ReflectDefineProperty({}, "key", ObjectGetOwnPropertyDescriptor({}, "key"))', | ||
| 112 | + errors: [{ message: /prototype pollution/ }], | ||
| 113 | + }, | ||
| 114 | + { | ||
| 115 | + code: 'ObjectDefineProperty({}, "key", ReflectGetOwnPropertyDescriptor({}, "key"))', | ||
| 116 | + errors: [{ message: /prototype pollution/ }], | ||
| 117 | + }, | ||
| 118 | + { | ||
| 119 | + code: 'ReflectDefineProperty({}, "key", ReflectGetOwnPropertyDescriptor({}, "key"))', | ||
| 120 | + errors: [{ message: /prototype pollution/ }], | ||
| 121 | + }, | ||
| 122 | + { | ||
| 123 | + code: 'ObjectDefineProperty({}, "key", { __proto__: Object.prototype })', | ||
| 124 | + errors: [{ message: /null-prototype/ }], | ||
| 125 | + }, | ||
| 126 | + { | ||
| 127 | + code: 'ReflectDefineProperty({}, "key", { __proto__: Object.prototype })', | ||
| 128 | + errors: [{ message: /null-prototype/ }], | ||
| 129 | + }, | ||
| 130 | + { | ||
| 131 | + code: 'ObjectDefineProperty({}, "key", { [`__proto__`]: null })', | ||
| 132 | + errors: [{ message: /null-prototype/ }], | ||
| 133 | + }, | ||
| 134 | + { | ||
| 135 | + code: 'ReflectDefineProperty({}, "key", { [`__proto__`]: null })', | ||
| 136 | + errors: [{ message: /null-prototype/ }], | ||
| 137 | + }, | ||
| 138 | + { | ||
| 139 | + code: 'ObjectDefineProperty({}, "key", { enumerable: true })', | ||
| 140 | + errors: [{ message: /null-prototype/ }], | ||
| 141 | + }, | ||
| 142 | + { | ||
| 143 | + code: 'ReflectDefineProperty({}, "key", { enumerable: true })', | ||
| 144 | + errors: [{ message: /null-prototype/ }], | ||
| 145 | + }, | ||
| 146 | + ] | ||
| 147 | + }); | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,92 @@ | |||
| 1 | + 'use strict'; | ||
| 2 | + | ||
| 3 | + function checkProperties(context, node) { | ||
| 4 | + if ( | ||
| 5 | + node.type === 'CallExpression' && | ||
| 6 | + node.callee.name === 'ObjectGetOwnPropertyDescriptors' | ||
| 7 | + ) { | ||
| 8 | + context.report({ | ||
| 9 | + node, | ||
| 10 | + message: | ||
| 11 | + 'Property descriptors inherits from the Object prototype, therefore are subject to prototype pollution', | ||
| 12 | + }); | ||
| 13 | + } | ||
| 14 | + if (node.type !== 'ObjectExpression') return; | ||
| 15 | + for (const { key, value } of node.properties) { | ||
| 16 | + if ( | ||
| 17 | + key != null && value != null && | ||
| 18 | + !(key.type === 'Identifier' && key.name === '__proto__') && | ||
| 19 | + !(key.type === 'Literal' && key.value === '__proto__') | ||
| 20 | + ) { | ||
| 21 | + checkPropertyDescriptor(context, value); | ||
| 22 | + } | ||
| 23 | + } | ||
| 24 | + } | ||
| 25 | + | ||
| 26 | + function checkPropertyDescriptor(context, node) { | ||
| 27 | + if ( | ||
| 28 | + node.type === 'CallExpression' && | ||
| 29 | + (node.callee.name === 'ObjectGetOwnPropertyDescriptor' || | ||
| 30 | + node.callee.name === 'ReflectGetOwnPropertyDescriptor') | ||
| 31 | + ) { | ||
| 32 | + context.report({ | ||
| 33 | + node, | ||
| 34 | + message: | ||
| 35 | + 'Property descriptors inherits from the Object prototype, therefore are subject to prototype pollution', | ||
| 36 | + suggest: [{ | ||
| 37 | + desc: 'Wrap the property descriptor in a null-prototype object', | ||
| 38 | + fix(fixer) { | ||
| 39 | + return [ | ||
| 40 | + fixer.insertTextBefore(node, '{ __proto__: null,...'), | ||
| 41 | + fixer.insertTextAfter(node, ' }'), | ||
| 42 | + ]; | ||
| 43 | + }, | ||
| 44 | + }], | ||
| 45 | + }); | ||
| 46 | + } | ||
| 47 | + if (node.type !== 'ObjectExpression') return; | ||
| 48 | + | ||
| 49 | + for (const { key, value } of node.properties) { | ||
| 50 | + if ( | ||
| 51 | + key != null && value != null && | ||
| 52 | + ((key.type === 'Identifier' && key.name === '__proto__') || | ||
| 53 | + (key.type === 'Literal' && key.value === '__proto__')) && | ||
| 54 | + value.type === 'Literal' && value.value === null | ||
| 55 | + ) { | ||
| 56 | + return true; | ||
| 57 | + } | ||
| 58 | + } | ||
| 59 | + | ||
| 60 | + context.report({ | ||
| 61 | + node, | ||
| 62 | + message: 'Must use null-prototype object for property descriptors', | ||
| 63 | + }); | ||
| 64 | + } | ||
| 65 | + | ||
| 66 | + const CallExpression = 'ExpressionStatement[expression.type="CallExpression"]'; | ||
| 67 | + module.exports = { | ||
| 68 | + meta: { hasSuggestions: true }, | ||
| 69 | + create(context) { | ||
| 70 | + return { | ||
| 71 | + [`${CallExpression}[expression.callee.name=${/^(Object|Reflect)DefinePropert(ies|y)$/}]`]( | ||
| 72 | + node | ||
| 73 | + ) { | ||
| 74 | + switch (node.expression.callee.name) { | ||
| 75 | + case 'ObjectDefineProperties': | ||
| 76 | + checkProperties(context, node.expression.arguments[1]); | ||
| 77 | + break; | ||
| 78 | + case 'ReflectDefineProperty': | ||
| 79 | + case 'ObjectDefineProperty': | ||
| 80 | + checkPropertyDescriptor(context, node.expression.arguments[2]); | ||
| 81 | + break; | ||
| 82 | + default: | ||
| 83 | + throw new Error('Unreachable'); | ||
| 84 | + } | ||
| 85 | + }, | ||
| 86 | + | ||
| 87 | + [`${CallExpression}[expression.callee.name="ObjectCreate"][expression.arguments.length=2]`](node) { | ||
| 88 | + checkProperties(context, node.expression.arguments[1]); | ||
| 89 | + }, | ||
| 90 | + }; | ||
| 91 | + }, | ||
| 92 | + }; | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments