From ecb80514e80bce4e6ec7e71db8ff79954f07c57e Mon Sep 17 00:00:00 2001 From: Jos de Jong Date: Sat, 10 Oct 2020 10:24:48 +0200 Subject: [PATCH] Fix object pollution vulnerability in `math.config` --- HISTORY.md | 5 ++++ src/utils/object.js | 4 ++- test/unit-tests/expression/security.test.js | 28 +++++++++++++++++++++ test/unit-tests/utils/object.test.js | 8 ++++++ 4 files changed, 44 insertions(+), 1 deletion(-) diff --git a/HISTORY.md b/HISTORY.md index ce240300de..ef5ccb8aa1 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,10 @@ # History +# not yet published, version 7.5.1 + +- Fix object pollution vulnerability in `math.config`. Thanks Snyk. + + # 2020-10-07, version 7.5.0 - Function `pickRandom` now allows randomly picking elements from matrices diff --git a/src/utils/object.js b/src/utils/object.js index 46cf4b3c7e..9c3abf7a5f 100644 --- a/src/utils/object.js +++ b/src/utils/object.js @@ -86,7 +86,9 @@ export function deepExtend (a, b) { } for (const prop in b) { - if (hasOwnProperty(b, prop)) { + // We check against prop not being in Object.prototype or Function.prototype + // to prevent polluting for example Object.__proto__. + if (hasOwnProperty(b, prop) && !(prop in Object.prototype) && !(prop in Function.prototype)) { if (b[prop] && b[prop].constructor === Object) { if (a[prop] === undefined) { a[prop] = {} diff --git a/test/unit-tests/expression/security.test.js b/test/unit-tests/expression/security.test.js index 1199f1aea6..ce3c31eeef 100644 --- a/test/unit-tests/expression/security.test.js +++ b/test/unit-tests/expression/security.test.js @@ -390,6 +390,34 @@ describe('security', function () { assert.strictEqual(math.expression.mathWithTransform.chain, undefined) assert.deepStrictEqual(math.evaluate('chain'), math.unit('chain')) }) + + it('should not allow polluting the Object prototype via config', () => { + const obj = {} + assert.strictEqual(obj.polluted, undefined) + + // change the configuration + const newConfig = JSON.parse('{"__proto__":{"polluted":"yes"}}') + math.config(newConfig) + assert.strictEqual(obj.polluted, undefined) + }) + + it('should not allow polluting the Object prototype via config via the expression parser', () => { + const obj = {} + assert.strictEqual(obj.polluted, undefined) + + // change the configuration + math.evaluate('config({"__proto__":{"polluted":"yes"}})') + assert.strictEqual(obj.polluted, undefined) + }) + + it('should not allow polluting the Object prototype by creating an object in the expression parser', () => { + const obj = {} + assert.strictEqual(obj.polluted, undefined) + + // change the configuration + math.evaluate('a = {"__proto__":{"polluted":"yes"}}') + assert.strictEqual(obj.polluted, undefined) + }) }) function isPlainObject (object) { diff --git a/test/unit-tests/utils/object.test.js b/test/unit-tests/utils/object.test.js index 3da1b443ff..e9b0710299 100644 --- a/test/unit-tests/utils/object.test.js +++ b/test/unit-tests/utils/object.test.js @@ -141,6 +141,14 @@ describe('object', function () { delete Object.prototype.foo }) + + it('should not pollute Object.__proto__', function () { + const obj = {} + assert.strictEqual(obj.polluted, undefined) + + deepExtend(obj, JSON.parse('{"__proto__":{"polluted":"yes"}}')) + assert.strictEqual(obj.polluted, undefined) + }) }) describe('deepEqual', function () {