Skip to content

Commit db713a1

Browse files
rodyhaddadIgorMinar
authored andcommitted
refactor($parse): move around previous security changes made to $parse
1 parent 6081f20 commit db713a1

File tree

4 files changed

+146
-204
lines changed

4 files changed

+146
-204
lines changed
+18-9
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,27 @@
11
@ngdoc error
22
@name $parse:isecfld
3-
@fullName Referencing 'constructor' Field in Expression
3+
@fullName Referencing Disallowed Field in Expression
44
@description
55

6-
Occurs when an expression attempts to access an objects constructor field.
6+
Occurs when an expression attempts to access one of the following fields:
77

8-
AngularJS bans constructor access from within expressions since constructor
9-
access is a known way to execute arbitrary Javascript code.
8+
* __proto__
9+
* __defineGetter__
10+
* __defineSetter__
11+
* __lookupGetter__
12+
* __lookupSetter__
1013

11-
To resolve this error, avoid constructor access. As a last resort, alias
12-
the constructor and access it through the alias instead.
14+
AngularJS bans access to these fields from within expressions since
15+
access is a known way to mess with native objects or
16+
to execute arbitrary Javascript code.
1317

14-
Example expression that would result in this error:
18+
To resolve this error, avoid using these fields in expressions. As a last resort,
19+
alias their value and access them through the alias instead.
20+
21+
Example expressions that would result in this error:
1522

1623
```
17-
<div>{{user.constructor.name}}</div>
18-
```
24+
<div>{{user.__proto__.hasOwnProperty = $emit}}</div>
25+
26+
<div>{{user.__defineGetter__('name', noop)}}</div>
27+
```
+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
@ngdoc error
2+
@name $parse:isecobj
3+
@fullName Referencing Object Disallowed
4+
@description
5+
6+
Occurs when an expression attempts to access the 'Object' object (Root object in JavaScript).
7+
8+
Angular bans access to Object from within expressions since access is a known way to modify
9+
the behaviour of existing objects.
10+
11+
To resolve this error, avoid Object access.

src/ng/parse.js

+25-34
Original file line numberDiff line numberDiff line change
@@ -10,39 +10,26 @@ var $parseMinErr = minErr('$parse');
1010
//
1111
// As an example, consider the following Angular expression:
1212
//
13-
// {}.toString.constructor(alert("evil JS code"))
14-
//
15-
// We want to prevent this type of access. For the sake of performance, during the lexing phase we
16-
// disallow any "dotted" access to any member named "constructor".
17-
//
18-
// For reflective calls (a[b]) we check that the value of the lookup is not the Function constructor
19-
// while evaluating the expression, which is a stronger but more expensive test. Since reflective
20-
// calls are expensive anyway, this is not such a big deal compared to static dereferencing.
13+
// {}.toString.constructor('alert("evil JS code")')
2114
//
2215
// This sandboxing technique is not perfect and doesn't aim to be. The goal is to prevent exploits
2316
// against the expression language, but not to prevent exploits that were enabled by exposing
2417
// sensitive JavaScript or browser apis on Scope. Exposing such objects on a Scope is never a good
2518
// practice and therefore we are not even trying to protect against interaction with an object
2619
// explicitly exposed in this way.
2720
//
28-
// A developer could foil the name check by aliasing the Function constructor under a different
29-
// name on the scope.
30-
//
3121
// In general, it is not possible to access a Window object from an angular expression unless a
3222
// window or some DOM object that has a reference to window is published onto a Scope.
23+
// Similarly we prevent invocations of function known to be dangerous, as well as assignments to
24+
// native objects.
25+
3326

3427
function ensureSafeMemberName(name, fullExpression) {
35-
if (name === "constructor") {
28+
if (name === "__defineGetter__" || name === "__defineSetter__"
29+
|| name === "__lookupGetter__" || name === "__lookupSetter__"
30+
|| name === "__proto__") {
3631
throw $parseMinErr('isecfld',
37-
'Referencing "constructor" field in Angular expressions is disallowed! Expression: {0}',
38-
fullExpression);
39-
} else if (name === "__defineGetter__" || name === "__defineSetter__"
40-
|| name === "__lookupGetter__" || name === "__lookupSetter__") {
41-
throw $parseMinErr('isecgetset',
42-
'Defining and looking up getters and setters in Angular expressions is disallowed! '
43-
+'Expression: {0}', fullExpression);
44-
} else if (name === "__proto__") {
45-
throw $parseMinErr('isecproto', 'Using __proto__ in Angular expressions is disallowed! '
32+
'Attempting to access a disallowed field in Angular expressions! '
4633
+'Expression: {0}', fullExpression);
4734
}
4835
return name;
@@ -56,7 +43,7 @@ function ensureSafeObject(obj, fullExpression) {
5643
'Referencing Function in Angular expressions is disallowed! Expression: {0}',
5744
fullExpression);
5845
} else if (// isWindow(obj)
59-
obj.document && obj.location && obj.alert && obj.setInterval) {
46+
obj.window === obj) {
6047
throw $parseMinErr('isecwindow',
6148
'Referencing the Window in Angular expressions is disallowed! Expression: {0}',
6249
fullExpression);
@@ -65,21 +52,26 @@ function ensureSafeObject(obj, fullExpression) {
6552
throw $parseMinErr('isecdom',
6653
'Referencing DOM nodes in Angular expressions is disallowed! Expression: {0}',
6754
fullExpression);
68-
} else if (// isObject(obj)
69-
obj.getOwnPropertyNames || obj.getOwnPropertyDescriptor) {
55+
} else if (// block Object so that we can't get hold of dangerous Object.* methods
56+
obj === Object) {
7057
throw $parseMinErr('isecobj',
7158
'Referencing Object in Angular expressions is disallowed! Expression: {0}',
7259
fullExpression);
73-
} else if (obj === ({}).__defineGetter__ || obj === ({}).__defineSetter__
74-
|| obj === ({}).__lookupGetter__ || obj === ({}).__lookupSetter__) {
75-
throw $parseMinErr('isecgetset',
76-
'Defining and looking up getters and setters in Angular expressions is disallowed! '
77-
+'Expression: {0}', fullExpression);
7860
}
7961
}
8062
return obj;
8163
}
8264

65+
function ensureSafeFunction(obj, fullExpression) {
66+
if (obj) {
67+
if (obj.constructor === obj) {
68+
throw $parseMinErr('isecfn',
69+
'Referencing Function in Angular expressions is disallowed! Expression: {0}',
70+
fullExpression);
71+
}
72+
}
73+
}
74+
8375
var OPERATORS = {
8476
/* jshint bitwise : false */
8577
'null':function(){return null;},
@@ -699,10 +691,7 @@ Parser.prototype = {
699691
i = indexFn(self, locals),
700692
v;
701693

702-
if (i === "__proto__") {
703-
throw $parseMinErr('isecproto', 'Using __proto__ in Angular expressions is disallowed! '
704-
+'Expression: {0}', parser.text);
705-
}
694+
ensureSafeMemberName(i, parser.text);
706695
if (!o) return undefined;
707696
v = ensureSafeObject(o[i], parser.text);
708697
return v;
@@ -737,7 +726,7 @@ Parser.prototype = {
737726
var fnPtr = fn(scope, locals, context) || noop;
738727

739728
ensureSafeObject(context, parser.text);
740-
ensureSafeObject(fnPtr, parser.text);
729+
ensureSafeFunction(fnPtr, parser.text);
741730

742731
// IE stupidity! (IE doesn't have apply for some native functions)
743732
var v = fnPtr.apply
@@ -832,6 +821,8 @@ function setter(obj, path, setValue, fullExp) {
832821
obj = propertyObj;
833822
}
834823
key = ensureSafeMemberName(element.shift(), fullExp);
824+
ensureSafeObject(obj, fullExp);
825+
ensureSafeObject(obj[key], fullExp);
835826
obj[key] = setValue;
836827
return setValue;
837828
}

0 commit comments

Comments
 (0)