Skip to content

Commit

Permalink
fix(util): prevent prototype pollution in deepMerge (#5144)
Browse files Browse the repository at this point in the history
  • Loading branch information
nicojs authored Dec 11, 2024
1 parent 6493b62 commit f7b34bf
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 10 deletions.
22 changes: 12 additions & 10 deletions packages/util/src/deep-merge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,17 @@ export type DeepPartial<T> = {
* @param overrides
*/
export function deepMerge<T>(defaults: T, overrides: DeepPartial<T>): 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<T>);
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<T>);
}
}
}
});
});
}
13 changes: 13 additions & 0 deletions packages/util/test/unit/deep-merge.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
});
});

0 comments on commit f7b34bf

Please sign in to comment.