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

Commit 3a6b2b5

Browse files
committed
fix($parse): block assigning to fields of a constructor prototype
Fixes #14939
1 parent c207bff commit 3a6b2b5

File tree

2 files changed

+127
-3
lines changed

2 files changed

+127
-3
lines changed

src/ng/parse.js

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,10 +113,28 @@ function ensureSafeFunction(obj, fullExpression) {
113113

114114
function ensureSafeAssignContext(obj, fullExpression) {
115115
if (obj) {
116-
if (obj === (0).constructor || obj === (false).constructor || obj === ''.constructor ||
117-
obj === {}.constructor || obj === [].constructor || obj === Function.constructor) {
116+
var booleanConstructor = (false).constructor;
117+
var numberConstructor = (0).constructor;
118+
var stringConstructor = ''.constructor;
119+
var objectConstructor = {}.constructor;
120+
var arrayConstructor = [].constructor;
121+
var functionConstructor = Function.constructor;
122+
123+
if (obj === booleanConstructor ||
124+
obj === numberConstructor ||
125+
obj === stringConstructor ||
126+
obj === objectConstructor ||
127+
obj === arrayConstructor ||
128+
obj === functionConstructor ||
129+
obj === booleanConstructor.prototype ||
130+
obj === numberConstructor.prototype ||
131+
obj === stringConstructor.prototype ||
132+
obj === objectConstructor.prototype ||
133+
obj === arrayConstructor.prototype ||
134+
obj === functionConstructor.prototype) {
118135
throw $parseMinErr('isecaf',
119-
'Assigning to a constructor is disallowed! Expression: {0}', fullExpression);
136+
'Assigning to a constructor or it\'s prototype is disallowed! Expression: {0}',
137+
fullExpression);
120138
}
121139
}
122140
}

test/ng/parseSpec.js

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3059,6 +3059,112 @@ describe('parser', function() {
30593059
scope.$eval('"a".constructor.prototype.charCodeAt = [].concat');
30603060
}).toThrowMinErr('$parse', 'isecaf');
30613061
});
3062+
3063+
it('should prevent assigning in the context of a constructor prototype', function() {
3064+
forEach({
3065+
'(true)': true,
3066+
'(1)': 1,
3067+
'"string"': 'string',
3068+
'[]': []
3069+
}, function(thing, expr) {
3070+
var constructorExpr = expr + '.constructor';
3071+
var prototypeExpr = constructorExpr + '.prototype';
3072+
3073+
expect(function() {
3074+
scope.$eval(prototypeExpr + '.boin');
3075+
}).not.toThrow();
3076+
expect(function() {
3077+
delete scope.foo;
3078+
scope.$eval('foo = ' + prototypeExpr + '.boin');
3079+
}).not.toThrow();
3080+
expect(function() {
3081+
scope.$eval(prototypeExpr + '.boin = ""');
3082+
}).toThrowMinErr('$parse', 'isecaf');
3083+
expect(function() {
3084+
scope.$eval(prototypeExpr + '[0] = ""');
3085+
}).toThrowMinErr('$parse', 'isecaf');
3086+
expect(function() {
3087+
delete scope.foo;
3088+
scope.$eval('foo = ' + constructorExpr + '; foo.prototype.boin = ""');
3089+
}).toThrowMinErr('$parse', 'isecaf');
3090+
expect(function() {
3091+
delete scope.foo;
3092+
scope.$eval('foo = ' + prototypeExpr + '; foo.boin = ""');
3093+
}).toThrowMinErr('$parse', 'isecaf');
3094+
3095+
expect(function() {
3096+
scope.foo = thing.constructor;
3097+
scope.$eval('foo.prototype[0] = ""');
3098+
}).toThrowMinErr('$parse', 'isecaf');
3099+
expect(function() {
3100+
delete scope.foo;
3101+
scope.$eval('foo.prototype[0] = ""', {foo: thing.constructor});
3102+
}).toThrowMinErr('$parse', 'isecaf');
3103+
expect(function() {
3104+
scope.foo = thing.constructor.prototype;
3105+
scope.$eval('foo[0] = ""');
3106+
}).toThrowMinErr('$parse', 'isecaf');
3107+
expect(function() {
3108+
delete scope.foo;
3109+
scope.$eval('foo[0] = ""', {foo: thing.constructor.prototype});
3110+
}).toThrowMinErr('$parse', 'isecaf');
3111+
});
3112+
3113+
// These might throw different error (e.g. isecobj, isecfn),
3114+
// but still having them here for good measure
3115+
forEach({
3116+
'{}': {},
3117+
'$eval': scope.$eval
3118+
}, function(thing, expr) {
3119+
var constructorExpr = expr + '.constructor';
3120+
var prototypeExpr = constructorExpr + '.prototype';
3121+
3122+
expect(function() {
3123+
scope.$eval(prototypeExpr + '.boin');
3124+
}).not.toThrowMinErr('$parse', 'isecaf');
3125+
expect(function() {
3126+
delete scope.foo;
3127+
scope.$eval('foo = ' + prototypeExpr + '.boin');
3128+
}).not.toThrowMinErr('$parse', 'isecaf');
3129+
expect(function() {
3130+
scope.$eval(prototypeExpr + '.boin = ""');
3131+
}).toThrow();
3132+
expect(function() {
3133+
scope.$eval(prototypeExpr + '[0] = ""');
3134+
}).toThrow();
3135+
expect(function() {
3136+
delete scope.foo;
3137+
scope.$eval('foo = ' + constructorExpr + '; foo.prototype.boin = ""');
3138+
}).toThrow();
3139+
expect(function() {
3140+
delete scope.foo;
3141+
scope.$eval('foo = ' + prototypeExpr + '; foo.boin = ""');
3142+
}).toThrow();
3143+
3144+
expect(function() {
3145+
scope.foo = thing.constructor;
3146+
scope.$eval('foo.prototype[0] = ""');
3147+
}).toThrowMinErr('$parse', 'isecaf');
3148+
expect(function() {
3149+
delete scope.foo;
3150+
scope.$eval('foo.prototype[0] = ""', {foo: thing.constructor});
3151+
}).toThrowMinErr('$parse', 'isecaf');
3152+
expect(function() {
3153+
scope.foo = thing.constructor.prototype;
3154+
scope.$eval('foo[0] = ""');
3155+
}).toThrowMinErr('$parse', 'isecaf');
3156+
expect(function() {
3157+
delete scope.foo;
3158+
scope.$eval('foo[0] = ""', {foo: thing.constructor.prototype});
3159+
}).toThrowMinErr('$parse', 'isecaf');
3160+
});
3161+
3162+
// foo.constructor.prototype is not a constructor prototype.
3163+
expect(function() {
3164+
delete scope.foo;
3165+
scope.$eval('foo.constructor.prototype[0] = ""', {foo: {constructor: {prototype: ''}}});
3166+
}).not.toThrow();
3167+
});
30623168
});
30633169

30643170
it('should call the function from the received instance and not from a new one', function() {

0 commit comments

Comments
 (0)