Migrate codebase to use Object.hasOwn instead of Object.hasOwnProperty (#186829)

## Summary

This PR has breadth, but not depth. This adds 3 new `eslint` rules. The
first two protect against the use of code generated from strings (`eval`
and friends), which will not work client-side due to our CSP, and is not
something we wish to support server-side. The last rule aims to prevent
a subtle class of bugs, and to defend against a subset of prototype
pollution exploits:

- `no-new-func` to be compliant with our CSP, and to prevent code
execution from strings server-side:
https://eslint.org/docs/latest/rules/no-new-func
- `no-implied-eval` to be compliant with our CSP, and to prevent code
execution from strings server-side:
https://eslint.org/docs/latest/rules/no-implied-eval. Note that this
function implies that it prevents no-new-func, but I don't see [test
cases](https://github.com/eslint/eslint/blob/main/tests/lib/rules/no-implied-eval.js)
covering this behavior, so I think we should play it safe and enable
both rules.
- `no-prototype-builtins` to prevent accessing shadowed properties:
https://eslint.org/docs/latest/rules/no-prototype-builtins


In order to be compliant with `no-prototype-builtins`, I've migrated all
usages and variants of `Object.hasOwnProperty` to use the newer
[`Object.hasOwn`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwn).
This commit is contained in:
Larry Gregory 2024-08-13 11:30:19 -04:00 committed by GitHub
parent 386d290ea4
commit 74d88580a5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
230 changed files with 458 additions and 437 deletions

View file

@ -49,7 +49,7 @@ function walk(obj: any, keys: string[], value: any, path: string[]) {
return;
}
if (!obj.hasOwnProperty(key)) {
if (!Object.hasOwn(obj, key)) {
obj[key] = {};
}

View file

@ -11,12 +11,6 @@ interface StackItem {
previousKey: string | null;
}
// we have to do Object.prototype.hasOwnProperty because when you create an object using
// Object.create(null), and I assume other methods, you get an object without a prototype,
// so you can't use current.hasOwnProperty
const hasOwnProperty = (obj: any, property: string) =>
Object.prototype.hasOwnProperty.call(obj, property);
const isObject = (obj: any) => typeof obj === 'object' && obj !== null;
// we're using a stack instead of recursion so we aren't limited by the call stack
@ -40,11 +34,11 @@ export function ensureNoUnsafeProperties(obj: any) {
continue;
}
if (hasOwnProperty(value, '__proto__')) {
if (Object.hasOwn(value, '__proto__')) {
throw new Error(`'__proto__' is an invalid key`);
}
if (hasOwnProperty(value, 'prototype') && previousKey === 'constructor') {
if (Object.hasOwn(value, 'prototype') && previousKey === 'constructor') {
throw new Error(`'constructor.prototype' is an invalid key`);
}