fix(core): Prevent augmentObject from creating infinitely deep proxies (#5893)

fixes #5848
This commit is contained in:
कारतोफ्फेलस्क्रिप्ट™ 2023-04-03 17:18:52 +02:00 committed by कारतोफ्फेलस्क्रिप्ट™
parent 35cf783e83
commit 6906b00b0e

View file

@ -1,18 +1,9 @@
import type { IDataObject } from './Interfaces';
const augmentedObjects = new WeakSet<object>();
function augment<T>(value: T): T {
if (
typeof value !== 'object' ||
value === null ||
value instanceof RegExp ||
augmentedObjects.has(value)
)
return value;
// Track augmented objects to prevent infinite recursion in cases where an object contains circular references
augmentedObjects.add(value);
if (typeof value !== 'object' || value === null || value instanceof RegExp) return value;
if (value instanceof Date) return new Date(value.valueOf()) as T;
// eslint-disable-next-line @typescript-eslint/no-use-before-define
@ -23,6 +14,8 @@ function augment<T>(value: T): T {
}
export function augmentArray<T>(data: T[]): T[] {
if (augmentedObjects.has(data)) return data;
let newData: unknown[] | undefined = undefined;
function getData(): unknown[] {
@ -32,7 +25,7 @@ export function augmentArray<T>(data: T[]): T[] {
return newData;
}
return new Proxy(data, {
const proxy = new Proxy(data, {
deleteProperty(target, key: string) {
return Reflect.deleteProperty(getData(), key);
},
@ -63,24 +56,25 @@ export function augmentArray<T>(data: T[]): T[] {
return Reflect.ownKeys(newData !== undefined ? newData : target);
},
set(target, key: string, newValue: unknown) {
if (newValue !== null && typeof newValue === 'object') {
// Always proxy all objects. Like that we can check in get simply if it
// is a proxy and it does then not matter if it was already there from the
// beginning and it got proxied at some point or set later and so theoretically
// does not have to get proxied
newValue = new Proxy(newValue, {});
}
return Reflect.set(getData(), key, newValue);
// Always proxy all objects. Like that we can check in get simply if it
// is a proxy and it does then not matter if it was already there from the
// beginning and it got proxied at some point or set later and so theoretically
// does not have to get proxied
return Reflect.set(getData(), key, augment(newValue));
},
});
augmentedObjects.add(proxy);
return proxy;
}
export function augmentObject<T extends object>(data: T): T {
if (augmentedObjects.has(data)) return data;
const newData = {} as IDataObject;
const deletedProperties: Array<string | symbol> = [];
return new Proxy(data, {
const proxy = new Proxy(data, {
get(target, key: string, receiver): unknown {
if (deletedProperties.indexOf(key) !== -1) {
return undefined;
@ -144,4 +138,7 @@ export function augmentObject<T extends object>(data: T): T {
};
},
});
augmentedObjects.add(proxy);
return proxy;
}