|
|
@@ -30,6 +30,35 @@ export function notEmpty<T>(arg: T | undefined | null): arg is T {
|
|
|
return arg !== undefined && arg !== null;
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * filter dangerous key, prevent prototype pollution injection
|
|
|
+ * @param key key to be filtered
|
|
|
+ * @returns filtered key
|
|
|
+ */
|
|
|
+export const safeKey = (key: string): string => {
|
|
|
+ const dangerousProps = [
|
|
|
+ '__proto__',
|
|
|
+ 'constructor',
|
|
|
+ 'prototype',
|
|
|
+ '__defineGetter__',
|
|
|
+ '__defineSetter__',
|
|
|
+ '__lookupGetter__',
|
|
|
+ '__lookupSetter__',
|
|
|
+ 'hasOwnProperty',
|
|
|
+ 'isPrototypeOf',
|
|
|
+ 'propertyIsEnumerable',
|
|
|
+ 'toString',
|
|
|
+ 'valueOf',
|
|
|
+ 'toLocaleString',
|
|
|
+ ];
|
|
|
+
|
|
|
+ if (dangerousProps.includes(key.toLowerCase())) {
|
|
|
+ return '';
|
|
|
+ }
|
|
|
+
|
|
|
+ return key;
|
|
|
+};
|
|
|
+
|
|
|
/**
|
|
|
* `true` if the argument is an empty object. Otherwise, `false`.
|
|
|
*/
|
|
|
@@ -38,15 +67,15 @@ export function isEmpty(arg: Object): boolean {
|
|
|
}
|
|
|
|
|
|
export const each = <T = any, K = string>(obj: any, fn: (value: T, key: K) => void) =>
|
|
|
- keys(obj).forEach(key => fn(obj[key], key as any));
|
|
|
+ keys(obj).forEach((key) => fn(obj[key], key as any));
|
|
|
|
|
|
export const values = (obj: any) =>
|
|
|
- Object.values ? Object.values(obj) : keys(obj).map(k => obj[k]);
|
|
|
+ Object.values ? Object.values(obj) : keys(obj).map((k) => obj[k]);
|
|
|
|
|
|
export const filter = (obj: any, fn: (value: any, key: string) => boolean, dest?: any) =>
|
|
|
keys(obj).reduce(
|
|
|
(output, key) => (fn(obj[key], key) ? Object.assign(output, { [key]: obj[key] }) : output),
|
|
|
- dest || {},
|
|
|
+ dest || {}
|
|
|
);
|
|
|
|
|
|
export const pick = (obj: any, fields: string[], dest?: any) =>
|
|
|
@@ -58,7 +87,7 @@ export const omit = (obj: any, fields: string[], dest?: any) =>
|
|
|
export const reduce = <V = any, R = any>(
|
|
|
obj: any,
|
|
|
fn: (res: R, value: V, key: string) => any,
|
|
|
- res: R = {} as R,
|
|
|
+ res: R = {} as R
|
|
|
) => keys(obj).reduce((r, k) => fn(r, obj[k], k), res);
|
|
|
|
|
|
export const mapValues = <V = any>(obj: any, fn: (value: V, key: string) => any) =>
|
|
|
@@ -108,7 +137,7 @@ export function setByKey(
|
|
|
key: string,
|
|
|
newValue: any,
|
|
|
autoCreateObject = true,
|
|
|
- clone = false,
|
|
|
+ clone = false
|
|
|
): any {
|
|
|
if (typeof target !== 'object' || !key) return target;
|
|
|
if (clone) {
|
|
|
@@ -119,18 +148,18 @@ export function setByKey(
|
|
|
while (targetKeys.length > 0) {
|
|
|
key = targetKeys.shift()!;
|
|
|
if (targetKeys.length === 0) {
|
|
|
- target[key] = newValue;
|
|
|
+ target[safeKey(key)] = newValue;
|
|
|
return originTarget;
|
|
|
}
|
|
|
if (typeof target[key] !== 'object') {
|
|
|
if (!autoCreateObject) return originTarget;
|
|
|
- target[key] = {};
|
|
|
+ target[safeKey(key)] = {};
|
|
|
}
|
|
|
if (clone) {
|
|
|
if (Array.isArray(target[key])) {
|
|
|
- target[key] = target[key].slice();
|
|
|
+ target[safeKey(key)] = target[key].slice();
|
|
|
} else {
|
|
|
- target[key] = { ...target[key] };
|
|
|
+ target[safeKey(key)] = { ...target[key] };
|
|
|
}
|
|
|
}
|
|
|
target = target[key];
|