Skip to content

Commit

Permalink
More security fixes + unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
josdejong committed Mar 31, 2017
1 parent 43c4fe9 commit 3c3517d
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 10 deletions.
5 changes: 5 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
# History


## 2017-03-31, version 3.10.3

- More security fixes related to the ones fixed in `v3.10.2`.


## 2017-03-31, version 3.10.2

- Fixed a security vulnerability in the expression parser allowing
Expand Down
17 changes: 15 additions & 2 deletions lib/expression/node/FunctionNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -428,19 +428,32 @@ function factory (type, config, load, typed, math) {
* else throws an error
*/
function customs (fn) {
if (fn === jsEval) {
throw new Error('Calling "eval" is not allowed');
}

if (fn === Function) {
throw new Error('Calling "Function" is not allowed');
}

if (fn === jsEval) {
throw new Error('Calling "eval" is not allowed');
if (fn === Function.apply) {
throw new Error('Calling "apply" is not allowed');
}

if (fn === Function.call) {
throw new Error('Calling "call" is not allowed');
}

if (fn === Function.bind) {
throw new Error('Calling "bind" is not allowed');
}

// this function is ok
return fn;
}

var jsEval = eval
var jsApply = eval

This comment has been minimized.

Copy link
@eirmag

eirmag Apr 2, 2017

Quick question, what is the use of this declared but never used variable?

This comment has been minimized.

Copy link
@josdejong

josdejong Apr 2, 2017

Author Owner

Thanks for checking out the code. This is just a sloppy left-over, I've removed it.


exports.name = 'FunctionNode';
exports.path = 'expression.node';
Expand Down
44 changes: 36 additions & 8 deletions test/expression/parse.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2043,28 +2043,56 @@ describe('parse', function() {

describe('security', function () {

it ('should not allow calling Function from an object property', function () {
it ('should not allow calling Function/eval via a symbol', function () {
assert.throws(function () {
math.eval('[].map.constructor("console.log(\\"hacked...\\")")()')
math.eval('disguised("console.log(\\"hacked...\\")")()', {disguised: eval})
}, /Error: Calling "eval" is not allowed/)

assert.throws(function () {
math.eval('disguised("console.log(\\"hacked...\\")")()', {disguised: Function})
}, /Error: Calling "Function" is not allowed/)
})

it ('should not allow calling Function', function () {
it ('should not allow calling Function/eval via an object property', function () {
assert.throws(function () {
math.eval('[].map.constructor("console.log(\\"hacked...\\")")()')
}, /Error: Calling "Function" is not allowed/)

assert.throws(function () {
math.eval('obj.disguised("console.log(\\"hacked...\\")")()', {obj: {disguised: eval}})
}, /Error: Calling "eval" is not allowed/)

assert.throws(function () {
math.eval('disguised("console.log(\\"hacked...\\")")()', {disguised: Function})
math.eval('obj.disguised("console.log(\\"hacked...\\")")()', {obj: {disguised: Function}})
math.eval('fn()("console.log(\\"hacked...\\")")()', {fn: function () {return Function}})
}, /Error: Calling "Function" is not allowed/)
})

it ('should not allow calling eval', function () {
it ('should not allow calling Function/eval when returned by a function', function () {
assert.throws(function () {
math.eval('fn()("console.log(\\"hacked...\\")")()', {fn: function () {return Function}})
}, /Error: Calling "Function" is not allowed/)

assert.throws(function () {
math.eval('disguised("console.log(\\"hacked...\\")")()', {disguised: eval})
math.eval('obj.disguised("console.log(\\"hacked...\\")")()', {obj: {disguised: eval}})
math.eval('fn()("console.log(\\"hacked...\\")")()', {fn: function () {return eval}})
}, /Error: Calling "eval" is not allowed/)
})

it ('should not allow calling Function/eval via call/apply', function () {
assert.throws(function () {
math.eval('[].map.constructor.call(null, "console.log(\\"hacked...\\")")()')
}, /Error: Calling "call" is not allowed/)

assert.throws(function () {
math.eval('[].map.constructor.apply(null, ["console.log(\\"hacked...\\")"])()')
}, /Error: Calling "apply" is not allowed/)
})

it ('should not allow calling Function/eval via bind', function () {
assert.throws(function () {
math.eval('[].map.constructor.bind()("console.log(\\"hacked...\\")")()')
}, /Error: Calling "bind" is not allowed/)
})

});

});

0 comments on commit 3c3517d

Please sign in to comment.