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

Commit 0421cb4

Browse files
chirayukIgorMinar
authored andcommitted
fix($compile): secure form[action] & iframe[srcdoc]
Require bindings to form[action] to be $sce.RESOURCE_URL and bindings to iframe[srcdoc] to be $sce.HTML Closes #4927 Closes #4933
1 parent 6f1050d commit 0421cb4

File tree

2 files changed

+77
-2
lines changed

2 files changed

+77
-2
lines changed

src/ng/compile.js

+7-2
Original file line numberDiff line numberDiff line change
@@ -1780,10 +1780,15 @@ function $CompileProvider($provide) {
17801780

17811781

17821782
function getTrustedContext(node, attrNormalizedName) {
1783+
if (attrNormalizedName == "srcdoc") {
1784+
return $sce.HTML;
1785+
}
1786+
var tag = nodeName_(node);
17831787
// maction[xlink:href] can source SVG. It's not limited to <maction>.
17841788
if (attrNormalizedName == "xlinkHref" ||
1785-
(nodeName_(node) != "IMG" && (attrNormalizedName == "src" ||
1786-
attrNormalizedName == "ngSrc"))) {
1789+
(tag == "FORM" && attrNormalizedName == "action") ||
1790+
(tag != "IMG" && (attrNormalizedName == "src" ||
1791+
attrNormalizedName == "ngSrc"))) {
17871792
return $sce.RESOURCE_URL;
17881793
}
17891794
}

test/ng/compileSpec.js

+70
Original file line numberDiff line numberDiff line change
@@ -4232,6 +4232,76 @@ describe('$compile', function() {
42324232
}));
42334233
});
42344234

4235+
describe('form[action]', function() {
4236+
it('should pass through action attribute for the same domain', inject(function($compile, $rootScope, $sce) {
4237+
element = $compile('<form action="{{testUrl}}"></form>')($rootScope);
4238+
$rootScope.testUrl = "different_page";
4239+
$rootScope.$apply();
4240+
expect(element.attr('action')).toEqual('different_page');
4241+
}));
4242+
4243+
it('should clear out action attribute for a different domain', inject(function($compile, $rootScope, $sce) {
4244+
element = $compile('<form action="{{testUrl}}"></form>')($rootScope);
4245+
$rootScope.testUrl = "http://a.different.domain.example.com";
4246+
expect(function() { $rootScope.$apply() }).toThrowMinErr(
4247+
"$interpolate", "interr", "Can't interpolate: {{testUrl}}\nError: [$sce:insecurl] Blocked " +
4248+
"loading resource from url not allowed by $sceDelegate policy. URL: " +
4249+
"http://a.different.domain.example.com");
4250+
}));
4251+
4252+
it('should clear out JS action attribute', inject(function($compile, $rootScope, $sce) {
4253+
element = $compile('<form action="{{testUrl}}"></form>')($rootScope);
4254+
$rootScope.testUrl = "javascript:alert(1);";
4255+
expect(function() { $rootScope.$apply() }).toThrowMinErr(
4256+
"$interpolate", "interr", "Can't interpolate: {{testUrl}}\nError: [$sce:insecurl] Blocked " +
4257+
"loading resource from url not allowed by $sceDelegate policy. URL: " +
4258+
"javascript:alert(1);");
4259+
}));
4260+
4261+
it('should clear out non-resource_url action attribute', inject(function($compile, $rootScope, $sce) {
4262+
element = $compile('<form action="{{testUrl}}"></form>')($rootScope);
4263+
$rootScope.testUrl = $sce.trustAsUrl("javascript:doTrustedStuff()");
4264+
expect($rootScope.$apply).toThrowMinErr(
4265+
"$interpolate", "interr", "Can't interpolate: {{testUrl}}\nError: [$sce:insecurl] Blocked " +
4266+
"loading resource from url not allowed by $sceDelegate policy. URL: javascript:doTrustedStuff()");
4267+
}));
4268+
4269+
it('should pass through $sce.trustAs() values in action attribute', inject(function($compile, $rootScope, $sce) {
4270+
element = $compile('<form action="{{testUrl}}"></form>')($rootScope);
4271+
$rootScope.testUrl = $sce.trustAsResourceUrl("javascript:doTrustedStuff()");
4272+
$rootScope.$apply();
4273+
4274+
expect(element.attr('action')).toEqual('javascript:doTrustedStuff()');
4275+
}));
4276+
});
4277+
4278+
if (!msie || msie >= 11) {
4279+
describe('iframe[srcdoc]', function() {
4280+
it('should NOT set iframe contents for untrusted values', inject(function($compile, $rootScope, $sce) {
4281+
element = $compile('<iframe srcdoc="{{html}}"></iframe>')($rootScope);
4282+
$rootScope.html = '<div onclick="">hello</div>';
4283+
expect(function() { $rootScope.$digest(); }).toThrowMinErr('$interpolate', 'interr', new RegExp(
4284+
/Can't interpolate: {{html}}\n/.source +
4285+
/[^[]*\[\$sce:unsafe\] Attempting to use an unsafe value in a safe context./.source));
4286+
}));
4287+
4288+
it('should NOT set html for wrongly typed values', inject(function($rootScope, $compile, $sce) {
4289+
element = $compile('<iframe srcdoc="{{html}}"></iframe>')($rootScope);
4290+
$rootScope.html = $sce.trustAsCss('<div onclick="">hello</div>');
4291+
expect(function() { $rootScope.$digest(); }).toThrowMinErr('$interpolate', 'interr', new RegExp(
4292+
/Can't interpolate: {{html}}\n/.source +
4293+
/[^[]*\[\$sce:unsafe\] Attempting to use an unsafe value in a safe context./.source));
4294+
}));
4295+
4296+
it('should set html for trusted values', inject(function($rootScope, $compile, $sce) {
4297+
element = $compile('<iframe srcdoc="{{html}}"></iframe>')($rootScope);
4298+
$rootScope.html = $sce.trustAsHtml('<div onclick="">hello</div>');
4299+
$rootScope.$digest();
4300+
expect(angular.lowercase(element[0].srcdoc)).toEqual('<div onclick="">hello</div>');
4301+
}));
4302+
});
4303+
}
4304+
42354305
describe('ngAttr* attribute binding', function() {
42364306

42374307
it('should bind after digest but not before', inject(function($compile, $rootScope) {

0 commit comments

Comments
 (0)