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';
Copy link

Copilot AI Apr 6, 2026

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.

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
Copy link

Copilot AI Apr 6, 2026

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.
});
25 changes: 25 additions & 0 deletions __tests__/unit/lodash/mix.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import mix from '../../../src/lodash/mix';
Copy link

Copilot AI Apr 6, 2026

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.

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

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();
});
Comment on lines +9 to +24
Copy link

Copilot AI Apr 6, 2026

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.
});
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@antv/util",
"version": "3.3.11",
"version": "3.3.12",
"license": "MIT",
"sideEffects": false,
"main": "lib/index.js",
Expand Down
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
10 changes: 9 additions & 1 deletion src/lodash/mix.ts
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) {
Copy link

Copilot AI Apr 6, 2026

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.
(<any>dist)[key] = obj[key];
}
}
Expand Down