From ed8be3ce0130f716dda84c7178fd19a215ee39b8 Mon Sep 17 00:00:00 2001 From: Lucas Galfaso Date: Sun, 20 Sep 2015 16:33:50 +0200 Subject: [PATCH] fix($parse): block assigning to fields of a constructor Throw when assigning to a field of a constructor. --- src/ng/parse.js | 22 ++++++++++++++++++++++ test/ng/parseSpec.js | 26 ++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/src/ng/parse.js b/src/ng/parse.js index 529d701b44a3..8389e0f951a3 100644 --- a/src/ng/parse.js +++ b/src/ng/parse.js @@ -102,6 +102,16 @@ function ensureSafeFunction(obj, fullExpression) { } } +function ensureSafeAssignContext(obj, fullExpression) { + if (obj) { + if (obj === (0).constructor || obj === (false).constructor || obj === ''.constructor || + obj === {}.constructor || obj === [].constructor || obj === Function.constructor) { + throw $parseMinErr('isecaf', + 'Assigning to a constructor is disallowed! Expression: {0}', fullExpression); + } + } +} + var OPERATORS = createMap(); forEach('+ - * / % === !== == != < > <= >= && || ! = |'.split(' '), function(operator) { OPERATORS[operator] = true; }); var ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'}; @@ -816,6 +826,7 @@ ASTCompiler.prototype = { 'ensureSafeMemberName', 'ensureSafeObject', 'ensureSafeFunction', + 'ensureSafeAssignContext', 'ifDefined', 'plus', 'text', @@ -824,6 +835,7 @@ ASTCompiler.prototype = { ensureSafeMemberName, ensureSafeObject, ensureSafeFunction, + ensureSafeAssignContext, ifDefined, plusFn, expression); @@ -1050,6 +1062,7 @@ ASTCompiler.prototype = { self.if_(self.notNull(left.context), function() { self.recurse(ast.right, right); self.addEnsureSafeObject(self.member(left.context, left.name, left.computed)); + self.addEnsureSafeAssignContext(left.context); expression = self.member(left.context, left.name, left.computed) + ast.operator + right; self.assign(intoId, expression); recursionFn(intoId || expression); @@ -1175,6 +1188,10 @@ ASTCompiler.prototype = { this.current().body.push(this.ensureSafeFunction(item), ';'); }, + addEnsureSafeAssignContext: function(item) { + this.current().body.push(this.ensureSafeAssignContext(item), ';'); + }, + ensureSafeObject: function(item) { return 'ensureSafeObject(' + item + ',text)'; }, @@ -1187,6 +1204,10 @@ ASTCompiler.prototype = { return 'ensureSafeFunction(' + item + ',text)'; }, + ensureSafeAssignContext: function(item) { + return 'ensureSafeAssignContext(' + item + ',text)'; + }, + lazyRecurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) { var self = this; return function() { @@ -1364,6 +1385,7 @@ ASTInterpreter.prototype = { var lhs = left(scope, locals, assign, inputs); var rhs = right(scope, locals, assign, inputs); ensureSafeObject(lhs.value, self.expression); + ensureSafeAssignContext(lhs.context); lhs.context[lhs.name] = rhs; return context ? {value: rhs} : rhs; }; diff --git a/test/ng/parseSpec.js b/test/ng/parseSpec.js index 5b477b464947..9d2543f2831d 100644 --- a/test/ng/parseSpec.js +++ b/test/ng/parseSpec.js @@ -2703,6 +2703,32 @@ describe('parser', function() { ''); }).toThrow(); }); + + it('should prevent assigning in the context of a constructor', function() { + expect(function() { + scope.$eval("''.constructor.join"); + }).not.toThrow(); + expect(function() { + scope.$eval("''.constructor.join = ''.constructor.join"); + }).toThrow(); + expect(function() { + scope.$eval("''.constructor[0] = ''"); + }).toThrow(); + expect(function() { + scope.$eval("(0).constructor[0] = ''"); + }).toThrow(); + expect(function() { + scope.$eval("{}.constructor[0] = ''"); + }).toThrow(); + // foo.constructor is the object constructor. + expect(function() { + scope.$eval("foo.constructor[0] = ''", {foo: {}}); + }).toThrow(); + // foo.constructor is not a constructor. + expect(function() { + scope.$eval("foo.constructor[0] = ''", {foo: {constructor: ''}}); + }).not.toThrow(); + }); }); it('should call the function from the received instance and not from a new one', function() {