Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit 698af19

Browse files
committed
fix($parse): do not convert to string computed properties multiple times
Do not convert to string properties multiple times.
1 parent 7a413df commit 698af19

File tree

2 files changed

+35
-8
lines changed

2 files changed

+35
-8
lines changed

src/ng/parse.js

+26-8
Original file line numberDiff line numberDiff line change
@@ -38,20 +38,30 @@ var $parseMinErr = minErr('$parse');
3838

3939

4040
function ensureSafeMemberName(name, fullExpression) {
41+
if (name === "__defineGetter__" || name === "__defineSetter__"
42+
|| name === "__lookupGetter__" || name === "__lookupSetter__"
43+
|| name === "__proto__") {
44+
throw $parseMinErr('isecfld',
45+
'Attempting to access a disallowed field in Angular expressions! '
46+
+ 'Expression: {0}', fullExpression);
47+
}
48+
return name;
49+
}
50+
51+
function getStringValue(name, fullExpression) {
4152
// From the JavaScript docs:
4253
// Property names must be strings. This means that non-string objects cannot be used
4354
// as keys in an object. Any non-string object, including a number, is typecasted
4455
// into a string via the toString method.
4556
//
4657
// So, to ensure that we are checking the same `name` that JavaScript would use,
47-
// we cast it to a string, if possible
48-
name = (isObject(name) && name.toString) ? name.toString() : name;
49-
50-
if (name === "__defineGetter__" || name === "__defineSetter__"
51-
|| name === "__lookupGetter__" || name === "__lookupSetter__"
52-
|| name === "__proto__") {
53-
throw $parseMinErr('isecfld',
54-
'Attempting to access a disallowed field in Angular expressions! '
58+
// we cast it to a string, if possible.
59+
// Doing `name + ''` can cause a repl error if the result to `toString` is not a string,
60+
// this is, this will handle objects that misbehave.
61+
name = name + '';
62+
if (!isString(name)) {
63+
throw $parseMinErr('iseccst',
64+
'Cannot convert object to primitive value! '
5565
+ 'Expression: {0}', fullExpression);
5666
}
5767
return name;
@@ -816,6 +826,7 @@ ASTCompiler.prototype = {
816826
'ensureSafeMemberName',
817827
'ensureSafeObject',
818828
'ensureSafeFunction',
829+
'getStringValue',
819830
'ifDefined',
820831
'plus',
821832
'text',
@@ -824,6 +835,7 @@ ASTCompiler.prototype = {
824835
ensureSafeMemberName,
825836
ensureSafeObject,
826837
ensureSafeFunction,
838+
getStringValue,
827839
ifDefined,
828840
plusFn,
829841
expression);
@@ -967,6 +979,7 @@ ASTCompiler.prototype = {
967979
if (ast.computed) {
968980
right = self.nextId();
969981
self.recurse(ast.property, right);
982+
self.getStringValue(right);
970983
self.addEnsureSafeMemberName(right);
971984
if (create && create !== 1) {
972985
self.if_(self.not(self.computedMember(left, right)), self.lazyAssign(self.computedMember(left, right), '{}'));
@@ -1187,6 +1200,10 @@ ASTCompiler.prototype = {
11871200
return 'ensureSafeFunction(' + item + ',text)';
11881201
},
11891202

1203+
getStringValue: function(item) {
1204+
this.assign(item, 'getStringValue(' + item + ',text)');
1205+
},
1206+
11901207
lazyRecurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
11911208
var self = this;
11921209
return function() {
@@ -1561,6 +1578,7 @@ ASTInterpreter.prototype = {
15611578
var value;
15621579
if (lhs != null) {
15631580
rhs = right(scope, locals, assign, inputs);
1581+
rhs = getStringValue(rhs);
15641582
ensureSafeMemberName(rhs, expression);
15651583
if (create && create !== 1 && lhs && !(lhs[rhs])) {
15661584
lhs[rhs] = {};

test/ng/parseSpec.js

+9
Original file line numberDiff line numberDiff line change
@@ -2692,6 +2692,15 @@ describe('parser', function() {
26922692
});
26932693
});
26942694

2695+
it('should prevent the exploit', function() {
2696+
expect(function() {
2697+
scope.$eval('(1)[{0: "__proto__", 1: "__proto__", 2: "__proto__", 3: "safe", length: 4, toString: [].pop}].foo = 1');
2698+
}).toThrow();
2699+
if (!msie || msie > 10) {
2700+
expect((1)['__proto__'].foo).toBeUndefined();
2701+
}
2702+
});
2703+
26952704
it('should prevent the exploit', function() {
26962705
expect(function() {
26972706
scope.$eval('' +

0 commit comments

Comments
 (0)