diff options
| -rw-r--r-- | docs/content/error/parse/isecdom.ngdoc | 16 | ||||
| -rw-r--r-- | docs/content/error/parse/isecwindow.ngdoc | 16 | ||||
| -rw-r--r-- | src/ng/parse.js | 15 | ||||
| -rw-r--r-- | test/ng/parseSpec.js | 215 | 
4 files changed, 175 insertions, 87 deletions
| diff --git a/docs/content/error/parse/isecdom.ngdoc b/docs/content/error/parse/isecdom.ngdoc new file mode 100644 index 00000000..666bf36c --- /dev/null +++ b/docs/content/error/parse/isecdom.ngdoc @@ -0,0 +1,16 @@ +@ngdoc error +@name $parse:isecdom +@fullName Referencing a DOM node in Expression +@description + +Occurs when an expression attempts to access a DOM node. + +AngularJS restricts access to DOM nodes from within expressions since it's a known way to +execute arbitrary Javascript code. + +This check is only performed on object index and function calls in Angular expressions.  These are +places that are harder for the developer to guard.  Dotted member access (such as a.b.c) does not +perform this check - it's up to the developer to not expose such sensitive and powerful objects +directly on the scope chain. + +To resolve this error, avoid access to DOM nodes. diff --git a/docs/content/error/parse/isecwindow.ngdoc b/docs/content/error/parse/isecwindow.ngdoc new file mode 100644 index 00000000..81adeea0 --- /dev/null +++ b/docs/content/error/parse/isecwindow.ngdoc @@ -0,0 +1,16 @@ +@ngdoc error +@name $parse:isecwindow +@fullName Referencing Window object in Expression +@description + +Occurs when an expression attempts to access a Window object. + +AngularJS restricts access to the Window object from within expressions since it's a known way to +execute arbitrary Javascript code. + +This check is only performed on object index and function calls in Angular expressions.  These are +places that are harder for the developer to guard.  Dotted member access (such as a.b.c) does not +perform this check - it's up to the developer to not expose such sensitive and powerful objects +directly on the scope chain. + +To resolve this error, avoid Window access. diff --git a/src/ng/parse.js b/src/ng/parse.js index 5597acd8..8f8c0f87 100644 --- a/src/ng/parse.js +++ b/src/ng/parse.js @@ -42,12 +42,20 @@ function ensureSafeObject(obj, fullExpression) {    if (obj && obj.constructor === obj) {      throw $parseMinErr('isecfn',          'Referencing Function in Angular expressions is disallowed! Expression: {0}', fullExpression); +  //  +  } else if (// isWindow(obj) +      obj && obj.document && obj.location && obj.alert && obj.setInterval) { +    throw $parseMinErr('isecwindow', +        'Referencing the Window in Angular expressions is disallowed! Expression: {0}', fullExpression); +  } else if (// isElement(obj) +      obj && (obj.nodeName || (obj.on && obj.find))) { +    throw $parseMinErr('isecdom', +        'Referencing DOM nodes in Angular expressions is disallowed! Expression: {0}', fullExpression);    } else {      return obj;    }  } -  var OPERATORS = {      'null':function(){return null;},      'true':function(){return true;}, @@ -688,6 +696,9 @@ function parser(text, json, $filter, csp){          args.push(argsFn[i](scope, locals));        }        var fnPtr = fn(scope, locals, context) || noop; + +      ensureSafeObject(fnPtr, text); +        // IE stupidity!        var v = fnPtr.apply            ? fnPtr.apply(context, args) @@ -703,7 +714,7 @@ function parser(text, json, $filter, csp){          v = v.$$v;        } -      return v; +      return ensureSafeObject(v, text);      };    } diff --git a/test/ng/parseSpec.js b/test/ng/parseSpec.js index a82f736f..44b648c5 100644 --- a/test/ng/parseSpec.js +++ b/test/ng/parseSpec.js @@ -555,100 +555,145 @@ describe('parser', function() {        });        describe('sandboxing', function() { -        it('should NOT allow access to Function constructor in getter', function() { -          expect(function() { -            scope.$eval('{}.toString.constructor'); -          }).toThrowMinErr( -                  '$parse', 'isecfld', 'Referencing "constructor" field in Angular expressions is disallowed! ' + -                  'Expression: {}.toString.constructor'); - -          expect(function() { -            scope.$eval('{}.toString.constructor("alert(1)")'); -          }).toThrowMinErr( -                  '$parse', 'isecfld', 'Referencing "constructor" field in Angular expressions is disallowed! ' + -                  'Expression: {}.toString.constructor("alert(1)")'); - -          expect(function() { -            scope.$eval('[].toString.constructor.foo'); -          }).toThrowMinErr( -                  '$parse', 'isecfld', 'Referencing "constructor" field in Angular expressions is disallowed! ' + -                  'Expression: [].toString.constructor.foo'); - -          expect(function() { -            scope.$eval('{}.toString["constructor"]'); -          }).toThrowMinErr( -                  '$parse', 'isecfn', 'Referencing Function in Angular expressions is disallowed! ' + -                  'Expression: {}.toString["constructor"]'); -          expect(function() { -            scope.$eval('{}["toString"]["constructor"]'); -          }).toThrowMinErr( -                  '$parse', 'isecfn', 'Referencing Function in Angular expressions is disallowed! ' + -                  'Expression: {}["toString"]["constructor"]'); - -          scope.a = []; -          expect(function() { -            scope.$eval('a.toString.constructor', scope); -          }).toThrowMinErr( -                  '$parse', 'isecfld', 'Referencing "constructor" field in Angular expressions is disallowed! ' + -                  'Expression: a.toString.constructor'); -          expect(function() { -            scope.$eval('a.toString["constructor"]', scope); -          }).toThrowMinErr( -                  '$parse', 'isecfn', 'Referencing Function in Angular expressions is disallowed! ' + -                  'Expression: a.toString["constructor"]'); -        }); - -        it('should NOT allow access to Function constructor in setter', function() { -          expect(function() { -            scope.$eval('{}.toString.constructor = 1'); -          }).toThrowMinErr( -                  '$parse', 'isecfld', 'Referencing "constructor" field in Angular expressions is disallowed! ' + -                  'Expression: {}.toString.constructor = 1'); +        describe('Function constructor', function() { +          it('should NOT allow access to Function constructor in getter', function() { +            expect(function() { +              scope.$eval('{}.toString.constructor'); +            }).toThrowMinErr( +                    '$parse', 'isecfld', 'Referencing "constructor" field in Angular expressions is disallowed! ' + +                    'Expression: {}.toString.constructor'); + +            expect(function() { +              scope.$eval('{}.toString.constructor("alert(1)")'); +            }).toThrowMinErr( +                    '$parse', 'isecfld', 'Referencing "constructor" field in Angular expressions is disallowed! ' + +                    'Expression: {}.toString.constructor("alert(1)")'); + +            expect(function() { +              scope.$eval('[].toString.constructor.foo'); +            }).toThrowMinErr( +                    '$parse', 'isecfld', 'Referencing "constructor" field in Angular expressions is disallowed! ' + +                    'Expression: [].toString.constructor.foo'); + +            expect(function() { +              scope.$eval('{}.toString["constructor"]'); +            }).toThrowMinErr( +                    '$parse', 'isecfn', 'Referencing Function in Angular expressions is disallowed! ' + +                    'Expression: {}.toString["constructor"]'); +            expect(function() { +              scope.$eval('{}["toString"]["constructor"]'); +            }).toThrowMinErr( +                    '$parse', 'isecfn', 'Referencing Function in Angular expressions is disallowed! ' + +                    'Expression: {}["toString"]["constructor"]'); + +            scope.a = []; +            expect(function() { +              scope.$eval('a.toString.constructor', scope); +            }).toThrowMinErr( +                    '$parse', 'isecfld', 'Referencing "constructor" field in Angular expressions is disallowed! ' + +                    'Expression: a.toString.constructor'); +            expect(function() { +              scope.$eval('a.toString["constructor"]', scope); +            }).toThrowMinErr( +                    '$parse', 'isecfn', 'Referencing Function in Angular expressions is disallowed! ' + +                    'Expression: a.toString["constructor"]'); +          }); -          expect(function() { -            scope.$eval('{}.toString.constructor.a = 1'); -          }).toThrowMinErr( -                  '$parse', 'isecfld', 'Referencing "constructor" field in Angular expressions is disallowed! ' + -                  'Expression: {}.toString.constructor.a = 1'); +          it('should NOT allow access to Function constructor in setter', function() { +            expect(function() { +              scope.$eval('{}.toString.constructor = 1'); +            }).toThrowMinErr( +                    '$parse', 'isecfld', 'Referencing "constructor" field in Angular expressions is disallowed! ' + +                    'Expression: {}.toString.constructor = 1'); + +            expect(function() { +              scope.$eval('{}.toString.constructor.a = 1'); +            }).toThrowMinErr( +                    '$parse', 'isecfld', 'Referencing "constructor" field in Angular expressions is disallowed! ' + +                    'Expression: {}.toString.constructor.a = 1'); + +            expect(function() { +              scope.$eval('{}.toString["constructor"]["constructor"] = 1'); +            }).toThrowMinErr( +                    '$parse', 'isecfn', 'Referencing Function in Angular expressions is disallowed! ' + +                    'Expression: {}.toString["constructor"]["constructor"] = 1'); + + +            scope.key1 = "const"; +            scope.key2 = "ructor"; +            expect(function() { +              scope.$eval('{}.toString[key1 + key2].foo = 1'); +            }).toThrowMinErr( +                    '$parse', 'isecfn', 'Referencing Function in Angular expressions is disallowed! ' + +                        'Expression: {}.toString[key1 + key2].foo = 1'); + +            expect(function() { +              scope.$eval('{}.toString["constructor"]["a"] = 1'); +            }).toThrowMinErr( +                    '$parse', 'isecfn', 'Referencing Function in Angular expressions is disallowed! ' + +                    'Expression: {}.toString["constructor"]["a"] = 1'); + +            scope.a = []; +            expect(function() { +              scope.$eval('a.toString.constructor = 1', scope); +            }).toThrowMinErr( +                    '$parse', 'isecfld', 'Referencing "constructor" field in Angular expressions is disallowed! ' + +                    'Expression: a.toString.constructor = 1'); +          }); -          expect(function() { -            scope.$eval('{}.toString["constructor"]["constructor"] = 1'); -          }).toThrowMinErr( -                  '$parse', 'isecfn', 'Referencing Function in Angular expressions is disallowed! ' + -                  'Expression: {}.toString["constructor"]["constructor"] = 1'); +          it('should NOT allow access to Function constructor that has been aliased', function() { +            scope.foo = { "bar": Function }; +            expect(function() { +              scope.$eval('foo["bar"]'); +            }).toThrowMinErr( +                    '$parse', 'isecfn', 'Referencing Function in Angular expressions is disallowed! ' + +                    'Expression: foo["bar"]'); -          scope.key1 = "const"; -          scope.key2 = "ructor"; -          expect(function() { -            scope.$eval('{}.toString[key1 + key2].foo = 1'); -          }).toThrowMinErr( -                  '$parse', 'isecfn', 'Referencing Function in Angular expressions is disallowed! ' + -                      'Expression: {}.toString[key1 + key2].foo = 1'); +          }); -          expect(function() { -            scope.$eval('{}.toString["constructor"]["a"] = 1'); -          }).toThrowMinErr( -                  '$parse', 'isecfn', 'Referencing Function in Angular expressions is disallowed! ' + -                  'Expression: {}.toString["constructor"]["a"] = 1'); -          scope.a = []; -          expect(function() { -            scope.$eval('a.toString.constructor = 1', scope); -          }).toThrowMinErr( -                  '$parse', 'isecfld', 'Referencing "constructor" field in Angular expressions is disallowed! ' + -                  'Expression: a.toString.constructor = 1'); +          it('should NOT allow access to Function constructor in getter', function() { +            expect(function() { +              scope.$eval('{}.toString.constructor'); +            }).toThrowMinErr( +                    '$parse', 'isecfld', 'Referencing "constructor" field in Angular expressions is disallowed! ' + +                    'Expression: {}.toString.constructor'); +          });          }); -        it('should NOT allow access to Function constructor that has been aliased', function() { -          scope.foo = { "bar": Function }; -          expect(function() { -            scope.$eval('foo["bar"]'); -          }).toThrowMinErr( -                  '$parse', 'isecfn', 'Referencing Function in Angular expressions is disallowed! ' + -                  'Expression: foo["bar"]'); - +        describe('Window and $element/node', function() { +          it('should NOT allow access to the Window or DOM when indexing', inject(function($window, $document) { +            scope.wrap = {w: $window, d: $document}; + +            expect(function() { +              scope.$eval('wrap["w"]', scope); +            }).toThrowMinErr( +                    '$parse', 'isecwindow', 'Referencing the Window in Angular expressions is ' + +                    'disallowed! Expression: wrap["w"]'); +            expect(function() { +              scope.$eval('wrap["d"]', scope); +            }).toThrowMinErr( +                    '$parse', 'isecdom', 'Referencing DOM nodes in Angular expressions is ' + +                    'disallowed! Expression: wrap["d"]'); +          })); + +          it('should NOT allow access to the Window or DOM returned from a function', inject(function($window, $document) { +            scope.getWin = valueFn($window); +            scope.getDoc = valueFn($document); + +            expect(function() { +              scope.$eval('getWin()', scope); +            }).toThrowMinErr( +                    '$parse', 'isecwindow', 'Referencing the Window in Angular expressions is ' + +                    'disallowed! Expression: getWin()'); +            expect(function() { +              scope.$eval('getDoc()', scope); +            }).toThrowMinErr( +                    '$parse', 'isecdom', 'Referencing DOM nodes in Angular expressions is ' + +                    'disallowed! Expression: getDoc()'); +          }));          });        }); | 
