From f7b34bfbbde33e45bc213a2f6058bf0c5bf6bce7 Mon Sep 17 00:00:00 2001 From: Nico Jansen Date: Wed, 11 Dec 2024 09:14:11 +0100 Subject: [PATCH] fix(util): prevent prototype pollution in deepMerge (#5144) --- packages/util/src/deep-merge.ts | 22 ++++++++++++---------- packages/util/test/unit/deep-merge.spec.ts | 13 +++++++++++++ 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/packages/util/src/deep-merge.ts b/packages/util/src/deep-merge.ts index 00112e5da4..a05406fc3f 100644 --- a/packages/util/src/deep-merge.ts +++ b/packages/util/src/deep-merge.ts @@ -8,15 +8,17 @@ export type DeepPartial = { * @param overrides */ export function deepMerge(defaults: T, overrides: DeepPartial): void { - Object.keys(overrides).forEach((key) => { - const defaultValue = (defaults as any)[key]; - const overrideValue = (overrides as any)[key]; - if (overrideValue !== undefined) { - if (defaultValue === undefined || typeof defaultValue !== 'object' || typeof overrideValue !== 'object' || Array.isArray(defaultValue)) { - (defaults as any)[key] = overrideValue; - } else { - deepMerge(defaultValue, overrideValue as DeepPartial); + Object.keys(overrides) + .filter((key) => key !== '__proto__') + .forEach((key) => { + const defaultValue = (defaults as any)[key]; + const overrideValue = (overrides as any)[key]; + if (overrideValue !== undefined) { + if (defaultValue === undefined || typeof defaultValue !== 'object' || typeof overrideValue !== 'object' || Array.isArray(defaultValue)) { + (defaults as any)[key] = overrideValue; + } else { + deepMerge(defaultValue, overrideValue as DeepPartial); + } } - } - }); + }); } diff --git a/packages/util/test/unit/deep-merge.spec.ts b/packages/util/test/unit/deep-merge.spec.ts index 18f10e5923..6454525e30 100644 --- a/packages/util/test/unit/deep-merge.spec.ts +++ b/packages/util/test/unit/deep-merge.spec.ts @@ -60,4 +60,17 @@ describe(deepMerge.name, () => { const expected: Foo = { foo: '1' }; expect(foo).deep.eq(expected); }); + + it('should prevent prototype pollution', () => { + // Arrange + const someObj = {}; + + // Act + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument + deepMerge(someObj, JSON.parse('{"__proto__":{"pollutedKey":123}}')); + + // Assert + // @ts-expect-error This polluted key shouldn't be there, that's the point + expect({}.__proto__.pollutedKey).undefined; + }); });