aboutsummaryrefslogtreecommitdiffstats
path: root/src/ng/parse.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/ng/parse.js')
-rw-r--r--src/ng/parse.js34
1 files changed, 25 insertions, 9 deletions
diff --git a/src/ng/parse.js b/src/ng/parse.js
index c93d07de..ede3f24b 100644
--- a/src/ng/parse.js
+++ b/src/ng/parse.js
@@ -8,18 +8,23 @@ var promiseWarning;
// ------------------------------
// Angular expressions are generally considered safe because these expressions only have direct
// access to $scope and locals. However, one can obtain the ability to execute arbitrary JS code by
-// obtaining a reference to native JS functions such as the Function constructor.
+// obtaining a reference to native JS functions such as the Function constructor, thw global Window
+// or Document object. In addition, many powerful functions for use by JavaScript code are
+// published on scope that shouldn't be available from within an Angular expression.
//
// As an example, consider the following Angular expression:
//
// {}.toString.constructor(alert("evil JS code"))
//
// We want to prevent this type of access. For the sake of performance, during the lexing phase we
-// disallow any "dotted" access to any member named "constructor".
+// disallow any "dotted" access to any member named "constructor" or to any member whose name begins
+// or ends with an underscore. The latter allows one to exclude the private / JavaScript only API
+// available on the scope and controllers from the context of an Angular expression.
//
-// For reflective calls (a[b]) we check that the value of the lookup is not the Function constructor
-// while evaluating the expression, which is a stronger but more expensive test. Since reflective
-// calls are expensive anyway, this is not such a big deal compared to static dereferencing.
+// For reflective calls (a[b]), we check that the value of the lookup is not the Function
+// constructor, Window or DOM node while evaluating the expression, which is a stronger but more
+// expensive test. Since reflective calls are expensive anyway, this is not such a big deal compared
+// to static dereferencing.
//
// This sandboxing technique is not perfect and doesn't aim to be. The goal is to prevent exploits
// against the expression language, but not to prevent exploits that were enabled by exposing
@@ -33,12 +38,20 @@ var promiseWarning;
// In general, it is not possible to access a Window object from an angular expression unless a
// window or some DOM object that has a reference to window is published onto a Scope.
-function ensureSafeMemberName(name, fullExpression) {
- if (name === "constructor") {
+function ensureSafeMemberName(name, fullExpression, allowConstructor) {
+ if (typeof name !== 'string' && toString.apply(name) !== "[object String]") {
+ return name;
+ }
+ if (name === "constructor" && !allowConstructor) {
throw $parseMinErr('isecfld',
'Referencing "constructor" field in Angular expressions is disallowed! Expression: {0}',
fullExpression);
}
+ if (name.charAt(0) === '_' || name.charAt(name.length-1) === '_') {
+ throw $parseMinErr('isecprv',
+ 'Referencing private fields in Angular expressions is disallowed! Expression: {0}',
+ fullExpression);
+ }
return name;
}
@@ -722,7 +735,10 @@ Parser.prototype = {
return extend(function(self, locals) {
var o = obj(self, locals),
- i = indexFn(self, locals),
+ // In the getter, we will not block looking up "constructor" by name in order to support user defined
+ // constructors. However, if value looked up is the Function constructor, we will still block it in the
+ // ensureSafeObject call right after we look up o[i] (a few lines below.)
+ i = ensureSafeMemberName(indexFn(self, locals), parser.text, true /* allowConstructor */),
v, p;
if (!o) return undefined;
@@ -738,7 +754,7 @@ Parser.prototype = {
return v;
}, {
assign: function(self, value, locals) {
- var key = indexFn(self, locals);
+ var key = ensureSafeMemberName(indexFn(self, locals), parser.text);
// prevent overwriting of Function.constructor which would break ensureSafeObject check
var safe = ensureSafeObject(obj(self, locals), parser.text);
return safe[key] = value;