diff --git a/packages/commons/src/utils.ts b/packages/commons/src/utils.ts index c6858d4979..25b63e7f59 100644 --- a/packages/commons/src/utils.ts +++ b/packages/commons/src/utils.ts @@ -75,6 +75,11 @@ export const _ = { merge (target: any, source: any) { if (_.isObject(target) && _.isObject(source)) { Object.keys(source).forEach(key => { + // Skip prototype-polluting keys (e.g. JSON-parsed `__proto__`) + if (key === '__proto__' || key === 'constructor' || key === 'prototype') { + return; + } + if (_.isObject(source[key])) { if (!target[key]) { Object.assign(target, { [key]: {} }); diff --git a/packages/commons/test/utils.test.ts b/packages/commons/test/utils.test.ts index 0a3a152f18..25e035db78 100644 --- a/packages/commons/test/utils.test.ts +++ b/packages/commons/test/utils.test.ts @@ -167,6 +167,15 @@ describe('@feathersjs/commons utils', () => { assert.equal(_.merge('hello', {}), 'hello'); }); + + it('merge does not pollute Object.prototype', () => { + _.merge({}, JSON.parse('{"__proto__":{"polluted":"x"}}')); + _.merge({}, JSON.parse('{"constructor":{"prototype":{"polluted2":"y"}}}')); + assert.strictEqual(({} as any).polluted, undefined); + assert.strictEqual(({} as any).polluted2, undefined); + assert.strictEqual((Object.prototype as any).polluted, undefined); + assert.strictEqual((Object.prototype as any).polluted2, undefined); + }); }); describe('makeUrl', function () {