Skip to content

Commit 69e4854

Browse files
Ignore __proto__ fields in deepMerge() (#2779)
This resolves a security issue within @apollo/gateway. The deepMerge function is open to a known attack vector called prototype pollution. Prototype pollution allows a user to "pollute" Object.prototype, thereby polluting all Objects with Object.prototype in their prototype chain. This function is currently only in use by @apollo/gateway.
1 parent ce459ce commit 69e4854

File tree

3 files changed

+64
-1
lines changed

3 files changed

+64
-1
lines changed

packages/apollo-gateway/CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
11
# Changelog
22

33
### vNEXT
4+
5+
# v0.6.2
6+
7+
* Resolve an issue with \__proto__ pollution in deepMerge() [#2779](https://github.com/apollographql/apollo-server/pull/2779)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { deepMerge } from '../deepMerge';
2+
3+
describe('deepMerge', () => {
4+
it('merges basic', () => {
5+
const target = {
6+
a: 1,
7+
b: 2,
8+
};
9+
10+
const source = {
11+
b: 3,
12+
c: 4,
13+
};
14+
15+
expect(deepMerge(target, source)).toEqual({
16+
a: 1,
17+
b: 3,
18+
c: 4,
19+
});
20+
});
21+
22+
it('merges nested objects', () => {
23+
const target = {
24+
a: 1,
25+
b: {
26+
someProperty: 1,
27+
overwrittenProperty: 'clean',
28+
},
29+
};
30+
31+
const source = {
32+
b: {
33+
overwrittenProperty: 'dirty',
34+
newProperty: 'new',
35+
},
36+
c: 4,
37+
};
38+
39+
expect(deepMerge(target, source)).toEqual({
40+
a: 1,
41+
b: {
42+
newProperty: 'new',
43+
overwrittenProperty: 'dirty',
44+
someProperty: 1,
45+
},
46+
c: 4,
47+
});
48+
});
49+
50+
it('ignores merging __proto__ fields', () => {
51+
const target = {};
52+
53+
// Bypass setters on __proto__
54+
const source = JSON.parse('{"__proto__": {"pollution": true}}');
55+
deepMerge(target, source);
56+
57+
expect(Object.prototype.hasOwnProperty('pollution')).toBe(false);
58+
});
59+
});

packages/apollo-gateway/src/utilities/deepMerge.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ export function deepMerge(target: any, source: any): any {
44
if (source === undefined || source === null) return target;
55

66
for (const key of Object.keys(source)) {
7-
if (source[key] === undefined) continue;
7+
if (source[key] === undefined || key === '__proto__') continue;
88

99
if (target[key] && isObject(source[key])) {
1010
deepMerge(target[key], source[key]);

0 commit comments

Comments
 (0)