Skip to content

Commit a60f3c8

Browse files
committed
Fixed a security issue where forbidden properties like constructor could be replaced by using unicode characters when creating an object
1 parent 8d2d48d commit a60f3c8

4 files changed

Lines changed: 32 additions & 3 deletions

File tree

HISTORY.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
- Fixed a security issue in `typed-function` allowing arbitrary code execution
88
in the JavaScript engine by creating a typed function with JavaScript code
99
in the name. Thanks Masato Kinugawa.
10+
- Fixed a security issue where forbidden properties like constructor could be
11+
replaced by using unicode characters when creating an object. No known exploit,
12+
but could possibly allow arbitrary code execution. Thanks Masato Kinugawa.
1013

1114

1215
## 2017-10-18, version 3.16.5

lib/expression/node/ObjectNode.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,15 @@ function factory (type, config, load, typed) {
6060
var entries = [];
6161
for (var key in node.properties) {
6262
if (hasOwnProperty(node.properties, key)) {
63-
if (!isSafeProperty(node.properties, key)) {
64-
throw new Error('No access to property "' + key + '"');
63+
// we stringify/parse the key here to resolve unicode characters,
64+
// so you cannot create a key like {"co\\u006Estructor": null}
65+
var stringifiedKey = stringify(key)
66+
var parsedKey = JSON.parse(stringifiedKey)
67+
if (!isSafeProperty(node.properties, parsedKey)) {
68+
throw new Error('No access to property "' + parsedKey + '"');
6569
}
6670

67-
entries.push(stringify(key) + ': ' + compile(node.properties[key], defs, args));
71+
entries.push(stringifiedKey + ': ' + compile(node.properties[key], defs, args));
6872
}
6973
}
7074
return '{' + entries.join(', ') + '}';

test/expression/security.test.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,23 @@ describe('security', function () {
8383
}, /Error: No access to property "bind/);
8484
})
8585

86+
it ('should not allow disguising forbidden properties with unicode characters', function () {
87+
var scope = {
88+
a: {}
89+
};
90+
91+
assert.throws(function () { math.eval('a.co\u006Estructor', scope); }, /Error: No access to property "constructor"/);
92+
assert.throws(function () { math.eval('a["co\\u006Estructor"]', scope); }, /Error: No access to property "constructor"/);
93+
assert.throws(function () { math.eval('a.constructor', scope); }, /Error: No access to property "constructor"/);
94+
assert.throws(function () { math.eval('a.constructor = 2', scope); }, /Error: No access to property "constructor"/);
95+
assert.throws(function () { math.eval('a["constructor"] = 2', scope); }, /Error: No access to property "constructor"/);
96+
assert.throws(function () { math.eval('a["co\\u006Estructor"] = 2', scope); }, /Error: No access to property "constructor"/);
97+
assert.throws(function () { math.eval('a = {"constructor": 2}', scope); }, /Error: No access to property "constructor"/);
98+
assert.throws(function () { math.eval('a = {constructor: 2}', scope); }, /Error: No access to property "constructor"/);
99+
assert.throws(function () { math.eval('a = {"co\\u006Estructor": 2}', scope); }, /Error: No access to property "constructor"/);
100+
assert.throws(function () { math.eval('a = {co\u006Estructor: 2}', scope); }, /Error: No access to property "constructor"/);
101+
})
102+
86103
it ('should not allow calling Function via imported, overridden function', function () {
87104
assert.throws(function () {
88105
var math2 = math.create();

test/utils/customs.test.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ describe ('customs', function () {
8585

8686
// non existing method
8787
assert.equal(customs.isSafeMethod(matrix, 'nonExistingMethod'), false);
88+
89+
// method with unicode chars
90+
assert.equal(customs.isSafeMethod(matrix, 'co\u006Estructor'), false);
8891
});
8992

9093
});
@@ -113,6 +116,8 @@ describe ('customs', function () {
113116
// non existing property
114117
assert.equal(customs.isSafeProperty(object, 'bar'), true);
115118

119+
// property with unicode chars
120+
assert.equal(customs.isSafeProperty(object, 'co\u006Estructor'), false);
116121
});
117122

118123
it ('should test inherited properties on plain objects ', function () {

0 commit comments

Comments
 (0)