Skip to content

fix: patch prototype pollution vulnerability in deepMix and mix - #131

Merged
hustcc merged 5 commits into
masterfrom
copilot/fix-prototype-pollution-vulnerability
Oct 9, 2026
Merged

hustcc merged 5 commits into
masterfrom
copilot/fix-prototype-pollution-vulnerability

Conversation

Copilot AI commented Mar 3, 2026 •

Copy link
Copy Markdown
Contributor
  • Fix _deepMix in src/lodash/deep-mix.ts to skip dangerous keys (__proto__, constructor, prototype)
  • Add unit tests for deepMix with prototype pollution regression cases
  • Fix _mix in src/lodash/mix.ts to also skip __proto__ and prototype keys
  • Add unit tests for mix with prototype pollution regression cases
  • Bump version to 3.3.12
Original prompt

The Issue #130 in the @antv/util repository highlights a critical Prototype Pollution vulnerability in the deepMix function for versions <= 3.3.11. The deepMix function does not validate keys when merging objects, leading to potential modifications of Object.prototype properties. Attackers can exploit this to execute Denial of Service (DoS), authentication bypass, or remote code execution (RCE).

Proposed Fix:

  1. Update the deepMix function to include validation to prevent prototype pollution.
  2. Specifically, check keys such as __proto__, constructor, and prototype in the for-in loop before assignment.
  3. Add a regression test to ensure the deepMix function is resilient against object prototype pollution attacks.

Tasks:

  1. Modify the deepMix function in the file src/esm/lodash/deep-mix.js to prevent prototype modification.
  2. Write unit tests to validate the deepMix implementation with edge cases and malicious payloads from Issue Reference.
  3. Refactor and document the changes with in-place comments explaining the implementation.
  4. Update other references within the codebase to include the fixed deepMix function.
  5. Provide fixes and bash files supporting NPM validation when hit locally.

This pull request was created from Copilot chat.


🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.

Copilot AI and others added 2 commits March 3, 2026 14:16
Co-authored-by: hustcc <7856674+hustcc@users.noreply.github.com>
Co-authored-by: hustcc <7856674+hustcc@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix prototype pollution vulnerability in deepMix function fix: patch prototype pollution vulnerability in deepMix Mar 3, 2026
@hustcc
hustcc marked this pull request as ready for review March 3, 2026 14:29
@hustcc

hustcc commented Apr 5, 2026

Copy link
Copy Markdown
Member

@copilot mix 方法是不是也有类似的问题

Copilot AI commented Apr 5, 2026

Copy link
Copy Markdown
Contributor Author

@copilot mix 方法是不是也有类似的问题

是的,mix 也有同样的问题——它只屏蔽了 constructor,但缺少对 __proto__ 和 prototype 的防护。已在 cfde1e3 修复,同时新增了回归测试。

Copilot AI changed the title fix: patch prototype pollution vulnerability in deepMix fix: patch prototype pollution vulnerability in deepMix and mix Apr 5, 2026
Copilot AI requested a review from hustcc April 5, 2026 23:54
@hustcc

hustcc commented Apr 6, 2026

Copy link
Copy Markdown
Member

@copilot 升级版本号,我准备发版本。

Agent-Logs-Url: https://github.com/antvis/util/sessions/4c19cc22-f38e-4578-b3f6-9bc3d30b8ccf

Co-authored-by: hustcc <7856674+hustcc@users.noreply.github.com>

Copilot AI commented Apr 6, 2026

Copy link
Copy Markdown
Contributor Author

@copilot 升级版本号,我准备发版本。

已在 965c783 将版本号从 3.3.11 升级到 3.3.12。

Copilot AI requested a review from hustcc April 6, 2026 00:03

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR patches a prototype pollution vulnerability in the object-merge utilities (deepMix and mix) by skipping dangerous keys during merges, and adds regression tests to prevent reintroduction.

Changes:

  • Add __proto__ / constructor / prototype key skipping guards in src/lodash/deep-mix.ts and src/lodash/mix.ts.
  • Add new Jest unit tests covering standard merge behavior and prototype pollution vectors for both functions.
  • Update .gitignore to ignore package-lock.json.

Reviewed changes

Copilot reviewed 5 out of 6 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/lodash/mix.ts Adds an early-continue guard for prototype-pollution keys during shallow merge.
src/lodash/deep-mix.ts Adds a similar guard inside the hasOwn check during deep merge recursion.
__tests__/unit/lodash/mix.spec.ts Introduces regression tests for mix merge behavior and pollution vectors.
__tests__/unit/lodash/deep-mix.spec.ts Introduces regression tests for deepMix merge behavior and pollution vectors.
.gitignore Ignores package-lock.json.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/lodash/mix.ts
) {
continue;
}
if (obj.hasOwnProperty(key) && obj[key] !== undefined) {

Copilot AI Apr 6, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_mix uses obj.hasOwnProperty(key), which can throw for Object.create(null) inputs and can be overridden on attacker-controlled objects. Use a safe check like Object.prototype.hasOwnProperty.call(obj, key) (or reuse the hasOwn helper pattern from deep-mix.ts) to avoid DoS and ensure the guard is reliable.

Suggested change
if (obj.hasOwnProperty(key) && obj[key] !== undefined) {
if (Object.prototype.hasOwnProperty.call(obj, key) && obj[key] !== undefined) {

Copilot uses AI. Check for mistakes.
@@ -0,0 +1,25 @@
import mix from '../../../src/lodash/mix';

Copilot AI Apr 6, 2026

Copy link

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 to import { mix } from '../../../src/lodash'.

Suggested change
import mix from '../../../src/lodash/mix';
import { mix } from '../../../src/lodash';

Copilot uses AI. Check for mistakes.
Comment on lines +9 to +24
it('does not pollute Object.prototype via __proto__', () => {
const payload = JSON.parse('{"__proto__": {"polluted": true}}');
mix({}, payload);
expect((Object.prototype as any).polluted).toBeUndefined();
});

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

it('does not pollute via prototype key', () => {
mix({} as any, { prototype: { polluted: true } } as any);
expect((Object.prototype as any).polluted).toBeUndefined();
});

Copilot AI Apr 6, 2026

Copy link

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.

Copilot uses AI. Check for mistakes.
@@ -0,0 +1,30 @@
import deepMix from '../../../src/lodash/deep-mix';

Copilot AI Apr 6, 2026

Copy link

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 to import { deepMix } from '../../../src/lodash'.

Suggested change
import deepMix from '../../../src/lodash/deep-mix';
import { deepMix } from '../../../src/lodash';

Copilot uses AI. Check for mistakes.
Comment on lines +14 to +29
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();
});

Copilot AI Apr 6, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prototype-pollution regression tests can leave Object.prototype modified if the guard breaks, affecting later tests and making failures harder to interpret. Add cleanup in beforeEach/afterEach (e.g., delete (Object.prototype as any).polluted) to keep tests isolated.

Copilot uses AI. Check for mistakes.

@nrps9909 nrps9909 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI assistance disclosure: Codex was used to trace the downstream Ant Design Charts report to this utility, reproduce the published exploit, inspect this exact diff, run the repository validation, and construct the additional adversarial probes below. I verified every reported result before submitting this review.

Reviewed exact head 965c783 against base 426c79b.

The dangerous-key guard in deepMix does block the currently published payload: after building this head, merging JSON.parse of the proto payload leaves Object.prototype unchanged. However, the security hardening added to mix still trusts the attacker-controlled source method at src/lodash/mix.ts:12:

obj.hasOwnProperty(key)

Two independent exact-head regressions fail before any safe property can be copied:

  1. mix({}, Object.assign(Object.create(null), { safe: true }))
    TypeError: obj.hasOwnProperty is not a function
  2. mix({}, JSON.parse of { hasOwnProperty: null, safe: true })
    TypeError: obj.hasOwnProperty is not a function

Because mix is now explicitly included in this vulnerability fix, source ownership needs to be checked independently of the source object, for example with Object.prototype.hasOwnProperty.call or the same null-safe hasOwn helper already present in deep-mix.ts. Please add both null-prototype and shadowed-hasOwnProperty regressions so an untrusted merge source cannot turn the security path into a controllable denial of service.

There is also one changed-file gate failure: Prettier reports src/lodash/mix.ts as unformatted and collapses the multiline dangerous-key condition.

Validation summary:

  • existing focused deepMix and mix tests: 8 passed
  • independent security matrix: original pollution case passed; the two ownership cases failed
  • complete repository: 35 suites and 107 tests passed
  • CommonJS, ESM, and UMD builds plus both size limits passed
  • ESLint reported zero errors and three pre-existing warnings
  • full lint remained red for three Prettier files, including changed src/lodash/mix.ts; the other two are unchanged base files

This is a narrowly scoped request: retain the current dangerous-key guard, make the ownership check source-independent, add the two regressions, and format the changed source. The downstream @ant-design/plots 2.6.8 / @antv/g2 5.4.8 reproduction remains exploitable while @antv/util 3.3.11 is the published version.

@hustcc
hustcc merged commit dffe157 into master Oct 9, 2026
1 check passed
@hustcc
hustcc deleted the copilot/fix-prototype-pollution-vulnerability branch October 9, 2026 01:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants