-
Notifications
You must be signed in to change notification settings - Fork 28
fix: patch prototype pollution vulnerability in deepMix and mix #131
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 4 commits
09edb08
584a7e6
879be4a
cfde1e3
965c783
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,6 +13,7 @@ lerna-debug.log* | |
| # Node | ||
| node_modules/ | ||
| yarn.lock | ||
| package-lock.json | ||
|
|
||
| # Build | ||
| dist | ||
|
|
||
| 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(); | ||
| }); | ||
|
Comment on lines
+14
to
+29
|
||
| }); | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,25 @@ | ||||||
| import mix from '../../../src/lodash/mix'; | ||||||
|
||||||
| import mix from '../../../src/lodash/mix'; | |
| import { mix } from '../../../src/lodash'; |
Copilot
AI
Apr 6, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Prototype-pollution regression tests mutate global state if the protection ever regresses, which can cascade failures into unrelated tests. Add a beforeEach/afterEach cleanup (e.g., delete (Object.prototype as any).polluted) to keep the suite isolated and deterministic.
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -1,7 +1,15 @@ | ||||||
| // FIXME: Mutable param should be forbidden in static lang. | ||||||
| function _mix<Base, Source>(dist: Base & Source, obj: Source): void { | ||||||
| for (const key in obj) { | ||||||
| if (obj.hasOwnProperty(key) && key !== 'constructor' && obj[key] !== undefined) { | ||||||
| // Prevent prototype pollution by skipping dangerous keys | ||||||
| if ( | ||||||
| key === '__proto__' || | ||||||
| key === 'constructor' || | ||||||
| key === 'prototype' | ||||||
| ) { | ||||||
| continue; | ||||||
| } | ||||||
| if (obj.hasOwnProperty(key) && obj[key] !== undefined) { | ||||||
|
||||||
| if (obj.hasOwnProperty(key) && obj[key] !== undefined) { | |
| if (Object.prototype.hasOwnProperty.call(obj, key) && obj[key] !== undefined) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These tests import the implementation file directly. Most existing lodash unit tests import via
../../../src/lodash(public entrypoint), which better matches consumer usage and also ensures the index exports stay correct. Consider switching toimport { deepMix } from '../../../src/lodash'.