diff --git a/packages/commons/src/index.ts b/packages/commons/src/index.ts index 9455252384..681608770e 100644 --- a/packages/commons/src/index.ts +++ b/packages/commons/src/index.ts @@ -75,6 +75,10 @@ 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 53d51978ab..4fef61514b 100644 --- a/packages/commons/test/utils.test.ts +++ b/packages/commons/test/utils.test.ts @@ -212,5 +212,14 @@ 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) + }) }) })