Skip to content

Commit f7b34bf

Browse files
authored
fix(util): prevent prototype pollution in deepMerge (#5144)
1 parent 6493b62 commit f7b34bf

File tree

2 files changed

+25
-10
lines changed

2 files changed

+25
-10
lines changed

packages/util/src/deep-merge.ts

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,17 @@ export type DeepPartial<T> = {
88
* @param overrides
99
*/
1010
export function deepMerge<T>(defaults: T, overrides: DeepPartial<T>): void {
11-
Object.keys(overrides).forEach((key) => {
12-
const defaultValue = (defaults as any)[key];
13-
const overrideValue = (overrides as any)[key];
14-
if (overrideValue !== undefined) {
15-
if (defaultValue === undefined || typeof defaultValue !== 'object' || typeof overrideValue !== 'object' || Array.isArray(defaultValue)) {
16-
(defaults as any)[key] = overrideValue;
17-
} else {
18-
deepMerge(defaultValue, overrideValue as DeepPartial<T>);
11+
Object.keys(overrides)
12+
.filter((key) => key !== '__proto__')
13+
.forEach((key) => {
14+
const defaultValue = (defaults as any)[key];
15+
const overrideValue = (overrides as any)[key];
16+
if (overrideValue !== undefined) {
17+
if (defaultValue === undefined || typeof defaultValue !== 'object' || typeof overrideValue !== 'object' || Array.isArray(defaultValue)) {
18+
(defaults as any)[key] = overrideValue;
19+
} else {
20+
deepMerge(defaultValue, overrideValue as DeepPartial<T>);
21+
}
1922
}
20-
}
21-
});
23+
});
2224
}

packages/util/test/unit/deep-merge.spec.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,4 +60,17 @@ describe(deepMerge.name, () => {
6060
const expected: Foo = { foo: '1' };
6161
expect(foo).deep.eq(expected);
6262
});
63+
64+
it('should prevent prototype pollution', () => {
65+
// Arrange
66+
const someObj = {};
67+
68+
// Act
69+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
70+
deepMerge(someObj, JSON.parse('{"__proto__":{"pollutedKey":123}}'));
71+
72+
// Assert
73+
// @ts-expect-error This polluted key shouldn't be there, that's the point
74+
expect({}.__proto__.pollutedKey).undefined;
75+
});
6376
});

0 commit comments

Comments
 (0)