@@ -12,39 +12,26 @@ var promiseWarning;
12
12
//
13
13
// As an example, consider the following Angular expression:
14
14
//
15
- // {}.toString.constructor(alert("evil JS code"))
16
- //
17
- // We want to prevent this type of access. For the sake of performance, during the lexing phase we
18
- // disallow any "dotted" access to any member named "constructor".
19
- //
20
- // For reflective calls (a[b]) we check that the value of the lookup is not the Function constructor
21
- // while evaluating the expression, which is a stronger but more expensive test. Since reflective
22
- // calls are expensive anyway, this is not such a big deal compared to static dereferencing.
15
+ // {}.toString.constructor('alert("evil JS code")')
23
16
//
24
17
// This sandboxing technique is not perfect and doesn't aim to be. The goal is to prevent exploits
25
18
// against the expression language, but not to prevent exploits that were enabled by exposing
26
19
// sensitive JavaScript or browser apis on Scope. Exposing such objects on a Scope is never a good
27
20
// practice and therefore we are not even trying to protect against interaction with an object
28
21
// explicitly exposed in this way.
29
22
//
30
- // A developer could foil the name check by aliasing the Function constructor under a different
31
- // name on the scope.
32
- //
33
23
// In general, it is not possible to access a Window object from an angular expression unless a
34
24
// window or some DOM object that has a reference to window is published onto a Scope.
25
+ // Similarly we prevent invocations of function known to be dangerous, as well as assignments to
26
+ // native objects.
27
+
35
28
36
29
function ensureSafeMemberName ( name , fullExpression ) {
37
- if ( name === "constructor" ) {
30
+ if ( name === "__defineGetter__" || name === "__defineSetter__"
31
+ || name === "__lookupGetter__" || name === "__lookupSetter__"
32
+ || name === "__proto__" ) {
38
33
throw $parseMinErr ( 'isecfld' ,
39
- 'Referencing "constructor" field in Angular expressions is disallowed! Expression: {0}' ,
40
- fullExpression ) ;
41
- } else if ( name === "__defineGetter__" || name === "__defineSetter__"
42
- || name === "__lookupGetter__" || name === "__lookupSetter__" ) {
43
- throw $parseMinErr ( 'isecgetset' ,
44
- 'Defining and looking up getters and setters in Angular expressions is disallowed! '
45
- + 'Expression: {0}' , fullExpression ) ;
46
- } else if ( name === "__proto__" ) {
47
- throw $parseMinErr ( 'isecproto' , 'Using __proto__ in Angular expressions is disallowed! '
34
+ 'Attempting to access a disallowed field in Angular expressions! '
48
35
+ 'Expression: {0}' , fullExpression ) ;
49
36
}
50
37
return name ;
@@ -58,7 +45,7 @@ function ensureSafeObject(obj, fullExpression) {
58
45
'Referencing Function in Angular expressions is disallowed! Expression: {0}' ,
59
46
fullExpression ) ;
60
47
} else if ( // isWindow(obj)
61
- obj . document && obj . location && obj . alert && obj . setInterval ) {
48
+ obj . window === obj ) {
62
49
throw $parseMinErr ( 'isecwindow' ,
63
50
'Referencing the Window in Angular expressions is disallowed! Expression: {0}' ,
64
51
fullExpression ) ;
@@ -67,21 +54,26 @@ function ensureSafeObject(obj, fullExpression) {
67
54
throw $parseMinErr ( 'isecdom' ,
68
55
'Referencing DOM nodes in Angular expressions is disallowed! Expression: {0}' ,
69
56
fullExpression ) ;
70
- } else if ( // isObject(obj)
71
- obj . getOwnPropertyNames || obj . getOwnPropertyDescriptor ) {
57
+ } else if ( // block Object so that we can't get hold of dangerous Object.* methods
58
+ obj === Object ) {
72
59
throw $parseMinErr ( 'isecobj' ,
73
60
'Referencing Object in Angular expressions is disallowed! Expression: {0}' ,
74
61
fullExpression ) ;
75
- } else if ( obj === ( { } ) . __defineGetter__ || obj === ( { } ) . __defineSetter__
76
- || obj === ( { } ) . __lookupGetter__ || obj === ( { } ) . __lookupSetter__ ) {
77
- throw $parseMinErr ( 'isecgetset' ,
78
- 'Defining and looking up getters and setters in Angular expressions is disallowed! '
79
- + 'Expression: {0}' , fullExpression ) ;
80
62
}
81
63
}
82
64
return obj ;
83
65
}
84
66
67
+ function ensureSafeFunction ( obj , fullExpression ) {
68
+ if ( obj ) {
69
+ if ( obj . constructor === obj ) {
70
+ throw $parseMinErr ( 'isecfn' ,
71
+ 'Referencing Function in Angular expressions is disallowed! Expression: {0}' ,
72
+ fullExpression ) ;
73
+ }
74
+ }
75
+ }
76
+
85
77
var OPERATORS = {
86
78
/* jshint bitwise : false */
87
79
'null' :function ( ) { return null ; } ,
@@ -716,10 +708,7 @@ Parser.prototype = {
716
708
i = indexFn ( self , locals ) ,
717
709
v , p ;
718
710
719
- if ( i === "__proto__" ) {
720
- throw $parseMinErr ( 'isecproto' , 'Using __proto__ in Angular expressions is disallowed! '
721
- + 'Expression: {0}' , parser . text ) ;
722
- }
711
+ ensureSafeMemberName ( i , parser . text ) ;
723
712
if ( ! o ) return undefined ;
724
713
v = ensureSafeObject ( o [ i ] , parser . text ) ;
725
714
if ( v && v . then && parser . options . unwrapPromises ) {
@@ -762,7 +751,7 @@ Parser.prototype = {
762
751
var fnPtr = fn ( scope , locals , context ) || noop ;
763
752
764
753
ensureSafeObject ( context , parser . text ) ;
765
- ensureSafeObject ( fnPtr , parser . text ) ;
754
+ ensureSafeFunction ( fnPtr , parser . text ) ;
766
755
767
756
// IE stupidity! (IE doesn't have apply for some native functions)
768
757
var v = fnPtr . apply
@@ -871,6 +860,8 @@ function setter(obj, path, setValue, fullExp, options) {
871
860
}
872
861
}
873
862
key = ensureSafeMemberName ( element . shift ( ) , fullExp ) ;
863
+ ensureSafeObject ( obj , fullExp ) ;
864
+ ensureSafeObject ( obj [ key ] , fullExp ) ;
874
865
obj [ key ] = setValue ;
875
866
return setValue ;
876
867
}
0 commit comments