diff --git a/src/set.ts b/src/set.ts index 128a172..c8493a2 100644 --- a/src/set.ts +++ b/src/set.ts @@ -1,12 +1,16 @@ import each from 'lodash/each' import get from 'lodash/get' import _set from 'lodash/set' +import { disallowProtoPath } from './utils' type Many = T | ReadonlyArray export const set = (object: T, value: any, ...paths: Array>) => { - each(paths, path => _set(object, path, typeof value !== 'function' ? value : value(get(object, path), path, object))) + each(paths, path => { + disallowProtoPath(path) + return _set(object, path, typeof value !== 'function' ? value : value(get(object, path), path, object)) + }) return object diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 0000000..bd4244c --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,26 @@ +/** + * Utilities and helper functions + */ + +import { toPath } from "lodash"; + +type Many = T | ReadonlyArray + +const ILLEGAL_KEYS = new Set(["constructor", "__proto__"]); + +export const isIllegalKey = (key: Many): boolean => typeof key === "string" + ? ILLEGAL_KEYS.has(key) + : false + +export const isProtoPath = (path: Array> | Many): boolean => Array.isArray(path) + ? path.some(isIllegalKey) + : typeof path === "string" + ? isIllegalKey(path) + : false; + +export const disallowProtoPath = (paths: Array> | Many): void => { + const path = toPath(paths) + if (isProtoPath(path)) { + throw new Error(`Unsafe path encountered: ${path.toString()}`) + } +} \ No newline at end of file diff --git a/test/set.test.ts b/test/set.test.ts index 1e6a27f..147e11f 100644 --- a/test/set.test.ts +++ b/test/set.test.ts @@ -10,4 +10,8 @@ test(`Value can bea function that invoked to preduce the actual value to set`, ( prop: 2, anotherProp: 1, 0: [1] -})) \ No newline at end of file +})) + +test(`Disallow protopath override by throwing exception when unsafe path encountered`, () => expect(() => set({}, 'test', '__proto__.polluted')).toThrow()) + +test(`Disallow protopath override by throwing exception when unsafe path encountered`, () => expect(() => set({}, 'test', 'constructor.prototype.polluted')).toThrow()) \ No newline at end of file