@@ -10,39 +10,26 @@ var $parseMinErr = minErr('$parse');
10
10
//
11
11
// As an example, consider the following Angular expression:
12
12
//
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")')
21
14
//
22
15
// This sandboxing technique is not perfect and doesn't aim to be. The goal is to prevent exploits
23
16
// against the expression language, but not to prevent exploits that were enabled by exposing
24
17
// sensitive JavaScript or browser apis on Scope. Exposing such objects on a Scope is never a good
25
18
// practice and therefore we are not even trying to protect against interaction with an object
26
19
// explicitly exposed in this way.
27
20
//
28
- // A developer could foil the name check by aliasing the Function constructor under a different
29
- // name on the scope.
30
- //
31
21
// In general, it is not possible to access a Window object from an angular expression unless a
32
22
// 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
+
33
26
34
27
function ensureSafeMemberName ( name , fullExpression ) {
35
- if ( name === "constructor" ) {
28
+ if ( name === "__defineGetter__" || name === "__defineSetter__"
29
+ || name === "__lookupGetter__" || name === "__lookupSetter__"
30
+ || name === "__proto__" ) {
36
31
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! '
46
33
+ 'Expression: {0}' , fullExpression ) ;
47
34
}
48
35
return name ;
@@ -56,7 +43,7 @@ function ensureSafeObject(obj, fullExpression) {
56
43
'Referencing Function in Angular expressions is disallowed! Expression: {0}' ,
57
44
fullExpression ) ;
58
45
} else if ( // isWindow(obj)
59
- obj . document && obj . location && obj . alert && obj . setInterval ) {
46
+ obj . window === obj ) {
60
47
throw $parseMinErr ( 'isecwindow' ,
61
48
'Referencing the Window in Angular expressions is disallowed! Expression: {0}' ,
62
49
fullExpression ) ;
@@ -65,21 +52,26 @@ function ensureSafeObject(obj, fullExpression) {
65
52
throw $parseMinErr ( 'isecdom' ,
66
53
'Referencing DOM nodes in Angular expressions is disallowed! Expression: {0}' ,
67
54
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 ) {
70
57
throw $parseMinErr ( 'isecobj' ,
71
58
'Referencing Object in Angular expressions is disallowed! Expression: {0}' ,
72
59
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 ) ;
78
60
}
79
61
}
80
62
return obj ;
81
63
}
82
64
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
+
83
75
var OPERATORS = {
84
76
/* jshint bitwise : false */
85
77
'null' :function ( ) { return null ; } ,
@@ -699,10 +691,7 @@ Parser.prototype = {
699
691
i = indexFn ( self , locals ) ,
700
692
v ;
701
693
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 ) ;
706
695
if ( ! o ) return undefined ;
707
696
v = ensureSafeObject ( o [ i ] , parser . text ) ;
708
697
return v ;
@@ -737,7 +726,7 @@ Parser.prototype = {
737
726
var fnPtr = fn ( scope , locals , context ) || noop ;
738
727
739
728
ensureSafeObject ( context , parser . text ) ;
740
- ensureSafeObject ( fnPtr , parser . text ) ;
729
+ ensureSafeFunction ( fnPtr , parser . text ) ;
741
730
742
731
// IE stupidity! (IE doesn't have apply for some native functions)
743
732
var v = fnPtr . apply
@@ -832,6 +821,8 @@ function setter(obj, path, setValue, fullExp) {
832
821
obj = propertyObj ;
833
822
}
834
823
key = ensureSafeMemberName ( element . shift ( ) , fullExp ) ;
824
+ ensureSafeObject ( obj , fullExp ) ;
825
+ ensureSafeObject ( obj [ key ] , fullExp ) ;
835
826
obj [ key ] = setValue ;
836
827
return setValue ;
837
828
}
0 commit comments