diff --git a/packages/apollo-gateway/CHANGELOG.md b/packages/apollo-gateway/CHANGELOG.md index 5f53195c109..ac10a9435ac 100644 --- a/packages/apollo-gateway/CHANGELOG.md +++ b/packages/apollo-gateway/CHANGELOG.md @@ -1,3 +1,7 @@ # Changelog ### vNEXT + +# v0.6.2 + +* Resolve an issue with \__proto__ pollution in deepMerge() [#2779](https://github.com/apollographql/apollo-server/pull/2779) diff --git a/packages/apollo-gateway/src/utilities/__tests__/deepMerge.test.ts b/packages/apollo-gateway/src/utilities/__tests__/deepMerge.test.ts new file mode 100644 index 00000000000..b8d915b7ce7 --- /dev/null +++ b/packages/apollo-gateway/src/utilities/__tests__/deepMerge.test.ts @@ -0,0 +1,59 @@ +import { deepMerge } from '../deepMerge'; + +describe('deepMerge', () => { + it('merges basic', () => { + const target = { + a: 1, + b: 2, + }; + + const source = { + b: 3, + c: 4, + }; + + expect(deepMerge(target, source)).toEqual({ + a: 1, + b: 3, + c: 4, + }); + }); + + it('merges nested objects', () => { + const target = { + a: 1, + b: { + someProperty: 1, + overwrittenProperty: 'clean', + }, + }; + + const source = { + b: { + overwrittenProperty: 'dirty', + newProperty: 'new', + }, + c: 4, + }; + + expect(deepMerge(target, source)).toEqual({ + a: 1, + b: { + newProperty: 'new', + overwrittenProperty: 'dirty', + someProperty: 1, + }, + c: 4, + }); + }); + + it('ignores merging __proto__ fields', () => { + const target = {}; + + // Bypass setters on __proto__ + const source = JSON.parse('{"__proto__": {"pollution": true}}'); + deepMerge(target, source); + + expect(Object.prototype.hasOwnProperty('pollution')).toBe(false); + }); +}); diff --git a/packages/apollo-gateway/src/utilities/deepMerge.ts b/packages/apollo-gateway/src/utilities/deepMerge.ts index 27f135103dc..6756a36cac2 100644 --- a/packages/apollo-gateway/src/utilities/deepMerge.ts +++ b/packages/apollo-gateway/src/utilities/deepMerge.ts @@ -4,7 +4,7 @@ export function deepMerge(target: any, source: any): any { if (source === undefined || source === null) return target; for (const key of Object.keys(source)) { - if (source[key] === undefined) continue; + if (source[key] === undefined || key === '__proto__') continue; if (target[key] && isObject(source[key])) { deepMerge(target[key], source[key]);