## 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). |
||
---|---|---|
.. | ||
fp | ||
lodash | ||
scripts | ||
test | ||
.gitignore | ||
.npmignore | ||
BUILD.bazel | ||
index.d.ts | ||
index.js | ||
kibana.jsonc | ||
LICENSE | ||
package.json | ||
README.md | ||
set.d.ts | ||
set.js | ||
setWith.d.ts | ||
setWith.js | ||
tsconfig.json |
@kbn/safer-lodash-set
This module adds protection against prototype pollution to the set
and setWith
functions from Lodash and are API compatible with
Lodash v4.x.
Example Usage
const { set } = require('@kbn/safer-lodash-set');
const object = { a: [{ b: { c: 3 } }] };
set(object, 'a[0].b.c', 4);
console.log(object.a[0].b.c); // => 4
set(object, ['x', '0', 'y', 'z'], 5);
console.log(object.x[0].y.z); // => 5
API
The main module exposes two functions, set
and setWith
:
const { set, setWith } = require('@kbn/safer-lodash-set');
Besides the main module, it's also possible to require each function individually:
const set = require('@kbn/safer-lodash-set/set');
const setWith = require('@kbn/safer-lodash-set/setWith');
The APIs of these functions are identical to the equivalent Lodash
set
and setWith
functions. Please refer to the Lodash
documentation for the respective functions for details.
Functional Programming support (fp)
This module also supports the lodash/fp
api and hence exposes the
following fp compatible functions:
const { set, setWith } = require('@kbn/safer-lodash-set/fp');
Besides the main fp module, it's also possible to require each function individually:
const set = require('@kbn/safer-lodash-set/fp/set');
const setWith = require('@kbn/safer-lodash-set/fp/setWith');
Limitations
The safety improvements in this module is achieved by adding the
following limitations to the algorithm used to walk the path
given as
the 2nd argument to the set
and setWith
functions:
Only own properties are followed when walking the path
const parent = { foo: 1 };
const child = { bar: 2 };
Object.setPrototypeOf(child, parent);
// Now `child` can access `foo` through prototype inheritance
console.log(child.foo); // 1
set(child, 'foo', 3);
// A different `foo` property has now been added directly to the `child`
// object and the `parent` object has not been modified:
console.log(child.foo); // 3
console.log(parent.foo); // 1
console.log(Object.hasOwn(child, 'foo')); // true
The path
must not access function prototypes
const object = {
fn1: function () {},
fn2: () => {},
};
// Attempting to access any function prototype will result in an
// exception being thrown:
assert.throws(() => {
// Throws: Illegal access of function prototype
set(object, 'fn1.prototype.toString', 'bang!');
});
// This also goes for arrow functions even though they don't have a
// prototype property. This is just to keep things consistent:
assert.throws(() => {
// Throws: Illegal access of function prototype
set(object, 'fn2.prototype.toString', 'bang!');
});