fix($parse): disallow access to window and dom in expressions

This commit is contained in:
Chirayu Krishnappa 2013-08-09 14:47:13 -07:00
parent 4b71bbc988
commit be0b485669
4 changed files with 169 additions and 81 deletions

View file

@ -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.

View file

@ -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.

View file

@ -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);
};
}

View file

@ -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');
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("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.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"]');
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"]');
});
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');
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.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');
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');
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');
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');
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 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"]');
});
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()');
}));
});
});