kibana/packages/kbn-safer-lodash-set
Thomas Watson fbdeffb48f
Fix eslint rule for restricting certain lodash imports (#151023)
Fixes #110422

TL;DR: The `lodash.set` function is unsafe and shouldn't be called.

Cause of error: If you specify multiple `no-restricted-imports` paths
for the same module, only the last path is used. Instead you need to
combine them into a single path as I've done in this PR.

This regression was introduced in #100277
2023-02-16 08:35:09 -07:00
..
fp rename @elastic/* packages to @kbn/* (#138957) 2022-08-18 08:54:42 -07:00
lodash rename @elastic/* packages to @kbn/* (#138957) 2022-08-18 08:54:42 -07:00
scripts rename @elastic/* packages to @kbn/* (#138957) 2022-08-18 08:54:42 -07:00
test rename @elastic/* packages to @kbn/* (#138957) 2022-08-18 08:54:42 -07:00
.gitignore rename @elastic/* packages to @kbn/* (#138957) 2022-08-18 08:54:42 -07:00
.npmignore rename @elastic/* packages to @kbn/* (#138957) 2022-08-18 08:54:42 -07:00
BUILD.bazel Transpile packages on demand, validate all TS projects (#146212) 2022-12-22 19:00:29 -06:00
index.d.ts rename @elastic/* packages to @kbn/* (#138957) 2022-08-18 08:54:42 -07:00
index.js rename @elastic/* packages to @kbn/* (#138957) 2022-08-18 08:54:42 -07:00
kibana.jsonc Transpile packages on demand, validate all TS projects (#146212) 2022-12-22 19:00:29 -06:00
LICENSE rename @elastic/* packages to @kbn/* (#138957) 2022-08-18 08:54:42 -07:00
package.json [packages] migrate all plugins to packages (#148130) 2023-02-08 21:06:50 -06:00
README.md Fix eslint rule for restricting certain lodash imports (#151023) 2023-02-16 08:35:09 -07:00
set.d.ts rename @elastic/* packages to @kbn/* (#138957) 2022-08-18 08:54:42 -07:00
set.js rename @elastic/* packages to @kbn/* (#138957) 2022-08-18 08:54:42 -07:00
setWith.d.ts rename @elastic/* packages to @kbn/* (#138957) 2022-08-18 08:54:42 -07:00
setWith.js rename @elastic/* packages to @kbn/* (#138957) 2022-08-18 08:54:42 -07:00
tsconfig.json Transpile packages on demand, validate all TS projects (#146212) 2022-12-22 19:00:29 -06:00

@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.prototype.hasOwnProperty.call(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!');
});

License

MIT