diff --git a/packages/feathers/src/commons.test.ts b/packages/feathers/src/commons.test.ts index 424d82f08..588a772cb 100644 --- a/packages/feathers/src/commons.test.ts +++ b/packages/feathers/src/commons.test.ts @@ -213,5 +213,14 @@ describe('feathers/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) + }) }) }) diff --git a/packages/feathers/src/commons.ts b/packages/feathers/src/commons.ts index a6d06cbda..1a65d7e6c 100644 --- a/packages/feathers/src/commons.ts +++ b/packages/feathers/src/commons.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]: {} })