Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ lerna-debug.log*
# Node
node_modules/
yarn.lock
package-lock.json

# Build
dist
Expand Down
30 changes: 30 additions & 0 deletions __tests__/unit/lodash/deep-mix.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import deepMix from '../../../src/lodash/deep-mix';

describe('deepMix', () => {
it('merges plain objects', () => {
const result = deepMix({}, { a: 1 }, { b: 2 });
expect(result).toEqual({ a: 1, b: 2 });
});

it('deep merges nested objects', () => {
const result = deepMix({ a: { x: 1 } }, { a: { y: 2 } });
expect(result).toEqual({ a: { x: 1, y: 2 } });
});

it('does not pollute Object.prototype via __proto__', () => {
const payload = JSON.parse('{"__proto__": {"polluted": true}}');
deepMix({}, payload);
expect((Object.prototype as any).polluted).toBeUndefined();
});

it('does not pollute via constructor.prototype', () => {
const payload = JSON.parse('{"constructor": {"prototype": {"polluted": true}}}');
deepMix({}, payload);
expect((Object.prototype as any).polluted).toBeUndefined();
});

it('does not pollute via prototype key', () => {
deepMix({}, { prototype: { polluted: true } });
expect((Object.prototype as any).polluted).toBeUndefined();
});
});
4 changes: 4 additions & 0 deletions src/lodash/deep-mix.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ function _deepMix(dist, src, level?, maxLevel?) {
maxLevel = maxLevel || MAX_MIX_LEVEL;
for (const key in src) {
if (hasOwn(src, key)) {
// Prevent prototype pollution by skipping dangerous keys
if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
continue;
}
const value = src[key];
if (value !== null && isPlainObject(value)) {
if (!isPlainObject(dist[key])) {
Expand Down
Loading