Skip to content

Commit bff4275

Browse files
committed
do not extend __proto__
1 parent a9ba7ba commit bff4275

File tree

2 files changed

+27
-8
lines changed

2 files changed

+27
-8
lines changed

packages/util/src/deepCopy.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ export function deepCopy<T>(value: T): T {
3333
*
3434
* Note that the target can be a function, in which case the properties in
3535
* the source Object are copied onto it as static properties of the Function.
36+
*
37+
* Note: we don't merge __proto__ to prevent prototype pollution
3638
*/
3739
export function deepExtend(target: unknown, source: unknown): unknown {
3840
if (!(source instanceof Object)) {
@@ -62,14 +64,19 @@ export function deepExtend(target: unknown, source: unknown): unknown {
6264
}
6365

6466
for (const prop in source) {
65-
if (!source.hasOwnProperty(prop)) {
67+
// use isValidKey to guard against prototype pollution. See https://snyk.io/vuln/SNYK-JS-LODASH-450202
68+
if (!source.hasOwnProperty(prop) || !isValidKey(prop)) {
6669
continue;
6770
}
68-
(target as { [key: string]: unknown })[prop] = deepExtend(
69-
(target as { [key: string]: unknown })[prop],
70-
(source as { [key: string]: unknown })[prop]
71+
(target as Record<string, unknown>)[prop] = deepExtend(
72+
(target as Record<string, unknown>)[prop],
73+
(source as Record<string, unknown>)[prop]
7174
);
7275
}
7376

7477
return target;
7578
}
79+
80+
function isValidKey(key: string): boolean {
81+
return key !== '__proto__';
82+
}

packages/util/test/deepCopy.test.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,14 @@
1414
* See the License for the specific language governing permissions and
1515
* limitations under the License.
1616
*/
17-
import { assert } from 'chai';
17+
import { assert, expect } from 'chai';
1818
import { deepCopy, deepExtend } from '../src/deepCopy';
1919

2020
describe('deepCopy()', () => {
2121
it('Scalars', () => {
22-
assert.strictEqual(deepCopy(true), true);
23-
assert.strictEqual(deepCopy(123), 123);
24-
assert.strictEqual(deepCopy('abc'), 'abc');
22+
expect(deepCopy(true)).to.equal(true);
23+
expect(deepCopy(123)).to.equal(123);
24+
expect(deepCopy('abc')).to.equal('abc');
2525
});
2626

2727
it('Date', () => {
@@ -103,4 +103,16 @@ describe('deepExtend', () => {
103103
assert.deepEqual({ a: source }, target);
104104
assert.strictEqual(source, target.a);
105105
});
106+
107+
it.only('does not extend property __proto__', () => {
108+
const src = JSON.parse('{ "__proto__": { "polluted": "polluted" } }');
109+
const a = {};
110+
deepExtend(a, src);
111+
112+
// @ts-expect-error
113+
expect(a.__proto__).to.equal(Object.prototype);
114+
115+
// @ts-expect-error
116+
expect(a.polluted).to.be.undefined;
117+
});
106118
});

0 commit comments

Comments
 (0)