Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit be0b485

Browse files
committed
fix($parse): disallow access to window and dom in expressions
1 parent 4b71bbc commit be0b485

File tree

4 files changed

+175
-87
lines changed

4 files changed

+175
-87
lines changed
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
@ngdoc error
2+
@name $parse:isecdom
3+
@fullName Referencing a DOM node in Expression
4+
@description
5+
6+
Occurs when an expression attempts to access a DOM node.
7+
8+
AngularJS restricts access to DOM nodes from within expressions since it's a known way to
9+
execute arbitrary Javascript code.
10+
11+
This check is only performed on object index and function calls in Angular expressions. These are
12+
places that are harder for the developer to guard. Dotted member access (such as a.b.c) does not
13+
perform this check - it's up to the developer to not expose such sensitive and powerful objects
14+
directly on the scope chain.
15+
16+
To resolve this error, avoid access to DOM nodes.
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
@ngdoc error
2+
@name $parse:isecwindow
3+
@fullName Referencing Window object in Expression
4+
@description
5+
6+
Occurs when an expression attempts to access a Window object.
7+
8+
AngularJS restricts access to the Window object from within expressions since it's a known way to
9+
execute arbitrary Javascript code.
10+
11+
This check is only performed on object index and function calls in Angular expressions. These are
12+
places that are harder for the developer to guard. Dotted member access (such as a.b.c) does not
13+
perform this check - it's up to the developer to not expose such sensitive and powerful objects
14+
directly on the scope chain.
15+
16+
To resolve this error, avoid Window access.

src/ng/parse.js

+13-2
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,20 @@ function ensureSafeObject(obj, fullExpression) {
4242
if (obj && obj.constructor === obj) {
4343
throw $parseMinErr('isecfn',
4444
'Referencing Function in Angular expressions is disallowed! Expression: {0}', fullExpression);
45+
//
46+
} else if (// isWindow(obj)
47+
obj && obj.document && obj.location && obj.alert && obj.setInterval) {
48+
throw $parseMinErr('isecwindow',
49+
'Referencing the Window in Angular expressions is disallowed! Expression: {0}', fullExpression);
50+
} else if (// isElement(obj)
51+
obj && (obj.nodeName || (obj.on && obj.find))) {
52+
throw $parseMinErr('isecdom',
53+
'Referencing DOM nodes in Angular expressions is disallowed! Expression: {0}', fullExpression);
4554
} else {
4655
return obj;
4756
}
4857
}
4958

50-
5159
var OPERATORS = {
5260
'null':function(){return null;},
5361
'true':function(){return true;},
@@ -688,6 +696,9 @@ function parser(text, json, $filter, csp){
688696
args.push(argsFn[i](scope, locals));
689697
}
690698
var fnPtr = fn(scope, locals, context) || noop;
699+
700+
ensureSafeObject(fnPtr, text);
701+
691702
// IE stupidity!
692703
var v = fnPtr.apply
693704
? fnPtr.apply(context, args)
@@ -703,7 +714,7 @@ function parser(text, json, $filter, csp){
703714
v = v.$$v;
704715
}
705716

706-
return v;
717+
return ensureSafeObject(v, text);
707718
};
708719
}
709720

test/ng/parseSpec.js

+130-85
Original file line numberDiff line numberDiff line change
@@ -555,100 +555,145 @@ describe('parser', function() {
555555
});
556556

557557
describe('sandboxing', function() {
558-
it('should NOT allow access to Function constructor in getter', function() {
559-
expect(function() {
560-
scope.$eval('{}.toString.constructor');
561-
}).toThrowMinErr(
562-
'$parse', 'isecfld', 'Referencing "constructor" field in Angular expressions is disallowed! ' +
563-
'Expression: {}.toString.constructor');
564-
565-
expect(function() {
566-
scope.$eval('{}.toString.constructor("alert(1)")');
567-
}).toThrowMinErr(
568-
'$parse', 'isecfld', 'Referencing "constructor" field in Angular expressions is disallowed! ' +
569-
'Expression: {}.toString.constructor("alert(1)")');
570-
571-
expect(function() {
572-
scope.$eval('[].toString.constructor.foo');
573-
}).toThrowMinErr(
574-
'$parse', 'isecfld', 'Referencing "constructor" field in Angular expressions is disallowed! ' +
575-
'Expression: [].toString.constructor.foo');
576-
577-
expect(function() {
578-
scope.$eval('{}.toString["constructor"]');
579-
}).toThrowMinErr(
580-
'$parse', 'isecfn', 'Referencing Function in Angular expressions is disallowed! ' +
581-
'Expression: {}.toString["constructor"]');
582-
expect(function() {
583-
scope.$eval('{}["toString"]["constructor"]');
584-
}).toThrowMinErr(
585-
'$parse', 'isecfn', 'Referencing Function in Angular expressions is disallowed! ' +
586-
'Expression: {}["toString"]["constructor"]');
587-
588-
scope.a = [];
589-
expect(function() {
590-
scope.$eval('a.toString.constructor', scope);
591-
}).toThrowMinErr(
592-
'$parse', 'isecfld', 'Referencing "constructor" field in Angular expressions is disallowed! ' +
593-
'Expression: a.toString.constructor');
594-
expect(function() {
595-
scope.$eval('a.toString["constructor"]', scope);
596-
}).toThrowMinErr(
597-
'$parse', 'isecfn', 'Referencing Function in Angular expressions is disallowed! ' +
598-
'Expression: a.toString["constructor"]');
599-
});
600-
601-
it('should NOT allow access to Function constructor in setter', function() {
602-
expect(function() {
603-
scope.$eval('{}.toString.constructor = 1');
604-
}).toThrowMinErr(
605-
'$parse', 'isecfld', 'Referencing "constructor" field in Angular expressions is disallowed! ' +
606-
'Expression: {}.toString.constructor = 1');
558+
describe('Function constructor', function() {
559+
it('should NOT allow access to Function constructor in getter', function() {
560+
expect(function() {
561+
scope.$eval('{}.toString.constructor');
562+
}).toThrowMinErr(
563+
'$parse', 'isecfld', 'Referencing "constructor" field in Angular expressions is disallowed! ' +
564+
'Expression: {}.toString.constructor');
565+
566+
expect(function() {
567+
scope.$eval('{}.toString.constructor("alert(1)")');
568+
}).toThrowMinErr(
569+
'$parse', 'isecfld', 'Referencing "constructor" field in Angular expressions is disallowed! ' +
570+
'Expression: {}.toString.constructor("alert(1)")');
571+
572+
expect(function() {
573+
scope.$eval('[].toString.constructor.foo');
574+
}).toThrowMinErr(
575+
'$parse', 'isecfld', 'Referencing "constructor" field in Angular expressions is disallowed! ' +
576+
'Expression: [].toString.constructor.foo');
577+
578+
expect(function() {
579+
scope.$eval('{}.toString["constructor"]');
580+
}).toThrowMinErr(
581+
'$parse', 'isecfn', 'Referencing Function in Angular expressions is disallowed! ' +
582+
'Expression: {}.toString["constructor"]');
583+
expect(function() {
584+
scope.$eval('{}["toString"]["constructor"]');
585+
}).toThrowMinErr(
586+
'$parse', 'isecfn', 'Referencing Function in Angular expressions is disallowed! ' +
587+
'Expression: {}["toString"]["constructor"]');
588+
589+
scope.a = [];
590+
expect(function() {
591+
scope.$eval('a.toString.constructor', scope);
592+
}).toThrowMinErr(
593+
'$parse', 'isecfld', 'Referencing "constructor" field in Angular expressions is disallowed! ' +
594+
'Expression: a.toString.constructor');
595+
expect(function() {
596+
scope.$eval('a.toString["constructor"]', scope);
597+
}).toThrowMinErr(
598+
'$parse', 'isecfn', 'Referencing Function in Angular expressions is disallowed! ' +
599+
'Expression: a.toString["constructor"]');
600+
});
607601

608-
expect(function() {
609-
scope.$eval('{}.toString.constructor.a = 1');
610-
}).toThrowMinErr(
611-
'$parse', 'isecfld', 'Referencing "constructor" field in Angular expressions is disallowed! ' +
612-
'Expression: {}.toString.constructor.a = 1');
602+
it('should NOT allow access to Function constructor in setter', function() {
603+
expect(function() {
604+
scope.$eval('{}.toString.constructor = 1');
605+
}).toThrowMinErr(
606+
'$parse', 'isecfld', 'Referencing "constructor" field in Angular expressions is disallowed! ' +
607+
'Expression: {}.toString.constructor = 1');
608+
609+
expect(function() {
610+
scope.$eval('{}.toString.constructor.a = 1');
611+
}).toThrowMinErr(
612+
'$parse', 'isecfld', 'Referencing "constructor" field in Angular expressions is disallowed! ' +
613+
'Expression: {}.toString.constructor.a = 1');
614+
615+
expect(function() {
616+
scope.$eval('{}.toString["constructor"]["constructor"] = 1');
617+
}).toThrowMinErr(
618+
'$parse', 'isecfn', 'Referencing Function in Angular expressions is disallowed! ' +
619+
'Expression: {}.toString["constructor"]["constructor"] = 1');
620+
621+
622+
scope.key1 = "const";
623+
scope.key2 = "ructor";
624+
expect(function() {
625+
scope.$eval('{}.toString[key1 + key2].foo = 1');
626+
}).toThrowMinErr(
627+
'$parse', 'isecfn', 'Referencing Function in Angular expressions is disallowed! ' +
628+
'Expression: {}.toString[key1 + key2].foo = 1');
629+
630+
expect(function() {
631+
scope.$eval('{}.toString["constructor"]["a"] = 1');
632+
}).toThrowMinErr(
633+
'$parse', 'isecfn', 'Referencing Function in Angular expressions is disallowed! ' +
634+
'Expression: {}.toString["constructor"]["a"] = 1');
635+
636+
scope.a = [];
637+
expect(function() {
638+
scope.$eval('a.toString.constructor = 1', scope);
639+
}).toThrowMinErr(
640+
'$parse', 'isecfld', 'Referencing "constructor" field in Angular expressions is disallowed! ' +
641+
'Expression: a.toString.constructor = 1');
642+
});
613643

614-
expect(function() {
615-
scope.$eval('{}.toString["constructor"]["constructor"] = 1');
616-
}).toThrowMinErr(
617-
'$parse', 'isecfn', 'Referencing Function in Angular expressions is disallowed! ' +
618-
'Expression: {}.toString["constructor"]["constructor"] = 1');
619644

645+
it('should NOT allow access to Function constructor that has been aliased', function() {
646+
scope.foo = { "bar": Function };
647+
expect(function() {
648+
scope.$eval('foo["bar"]');
649+
}).toThrowMinErr(
650+
'$parse', 'isecfn', 'Referencing Function in Angular expressions is disallowed! ' +
651+
'Expression: foo["bar"]');
620652

621-
scope.key1 = "const";
622-
scope.key2 = "ructor";
623-
expect(function() {
624-
scope.$eval('{}.toString[key1 + key2].foo = 1');
625-
}).toThrowMinErr(
626-
'$parse', 'isecfn', 'Referencing Function in Angular expressions is disallowed! ' +
627-
'Expression: {}.toString[key1 + key2].foo = 1');
653+
});
628654

629-
expect(function() {
630-
scope.$eval('{}.toString["constructor"]["a"] = 1');
631-
}).toThrowMinErr(
632-
'$parse', 'isecfn', 'Referencing Function in Angular expressions is disallowed! ' +
633-
'Expression: {}.toString["constructor"]["a"] = 1');
634655

635-
scope.a = [];
636-
expect(function() {
637-
scope.$eval('a.toString.constructor = 1', scope);
638-
}).toThrowMinErr(
639-
'$parse', 'isecfld', 'Referencing "constructor" field in Angular expressions is disallowed! ' +
640-
'Expression: a.toString.constructor = 1');
656+
it('should NOT allow access to Function constructor in getter', function() {
657+
expect(function() {
658+
scope.$eval('{}.toString.constructor');
659+
}).toThrowMinErr(
660+
'$parse', 'isecfld', 'Referencing "constructor" field in Angular expressions is disallowed! ' +
661+
'Expression: {}.toString.constructor');
662+
});
641663
});
642664

643665

644-
it('should NOT allow access to Function constructor that has been aliased', function() {
645-
scope.foo = { "bar": Function };
646-
expect(function() {
647-
scope.$eval('foo["bar"]');
648-
}).toThrowMinErr(
649-
'$parse', 'isecfn', 'Referencing Function in Angular expressions is disallowed! ' +
650-
'Expression: foo["bar"]');
651-
666+
describe('Window and $element/node', function() {
667+
it('should NOT allow access to the Window or DOM when indexing', inject(function($window, $document) {
668+
scope.wrap = {w: $window, d: $document};
669+
670+
expect(function() {
671+
scope.$eval('wrap["w"]', scope);
672+
}).toThrowMinErr(
673+
'$parse', 'isecwindow', 'Referencing the Window in Angular expressions is ' +
674+
'disallowed! Expression: wrap["w"]');
675+
expect(function() {
676+
scope.$eval('wrap["d"]', scope);
677+
}).toThrowMinErr(
678+
'$parse', 'isecdom', 'Referencing DOM nodes in Angular expressions is ' +
679+
'disallowed! Expression: wrap["d"]');
680+
}));
681+
682+
it('should NOT allow access to the Window or DOM returned from a function', inject(function($window, $document) {
683+
scope.getWin = valueFn($window);
684+
scope.getDoc = valueFn($document);
685+
686+
expect(function() {
687+
scope.$eval('getWin()', scope);
688+
}).toThrowMinErr(
689+
'$parse', 'isecwindow', 'Referencing the Window in Angular expressions is ' +
690+
'disallowed! Expression: getWin()');
691+
expect(function() {
692+
scope.$eval('getDoc()', scope);
693+
}).toThrowMinErr(
694+
'$parse', 'isecdom', 'Referencing DOM nodes in Angular expressions is ' +
695+
'disallowed! Expression: getDoc()');
696+
}));
652697
});
653698
});
654699

0 commit comments

Comments
 (0)