From 04cad41d26ebaf44b5ee0c29a152d61f235f3efa Mon Sep 17 00:00:00 2001 From: Raphael Jamet Date: Fri, 27 May 2016 14:13:09 +0200 Subject: [PATCH] fix($compile): secure `link[href]` as a `RESOURCE_URL`s in `$sce`. User-controlled imports or stylesheets can run script in your origin, which warrants that we require that they are safe `RESOURCE_URL`s. Closes #14687 BREAKING CHANGE `link[href]` attributes are now protected via `$sce`, which prevents interpolated values that fail the `RESOURCE_URL` context tests from being used in interpolation. For example if the application is running at `https://docs.angularjs.org` then the following will fail: ``` ``` By default, `RESOURCE_URL` safe URLs are only allowed from the same domain and protocol as the application document. To use URLs from other domains and/or protocols, you may either whitelist them or wrap it into a trusted value by calling `$sce.trustAsResourceUrl(url)`. --- src/ng/compile.js | 2 ++ test/ng/compileSpec.js | 31 +++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/src/ng/compile.js b/src/ng/compile.js index 65200b2fb..2fb653093 100644 --- a/src/ng/compile.js +++ b/src/ng/compile.js @@ -2971,6 +2971,8 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { // maction[xlink:href] can source SVG. It's not limited to . if (attrNormalizedName === "xlinkHref" || (tag === "form" && attrNormalizedName === "action") || + // links can be stylesheets or imports, which can run script in the current origin + (tag === "link" && attrNormalizedName === "href") || (tag !== "img" && (attrNormalizedName === "src" || attrNormalizedName === "ngSrc"))) { return $sce.RESOURCE_URL; diff --git a/test/ng/compileSpec.js b/test/ng/compileSpec.js index 0a6fe4b11..dde7eeb4d 100755 --- a/test/ng/compileSpec.js +++ b/test/ng/compileSpec.js @@ -10171,6 +10171,7 @@ describe('$compile', function() { "loading resource from url not allowed by $sceDelegate policy. URL: javascript:doTrustedStuff()"); })); + it('should pass through $sce.trustAs() values in action attribute', inject(function($compile, $rootScope, $sce) { /* jshint scripturl:true */ element = $compile('
')($rootScope); @@ -10181,6 +10182,36 @@ describe('$compile', function() { })); }); + describe('link[href]', function() { + it('should reject invalid RESOURCE_URLs', inject(function($compile, $rootScope) { + element = $compile('')($rootScope); + $rootScope.testUrl = "https://evil.example.org/css.css"; + expect(function() { $rootScope.$apply(); }).toThrowMinErr( + "$interpolate", "interr", "Can't interpolate: {{testUrl}}\nError: [$sce:insecurl] Blocked " + + "loading resource from url not allowed by $sceDelegate policy. URL: " + + "https://evil.example.org/css.css"); + })); + + it('should accept valid RESOURCE_URLs', inject(function($compile, $rootScope, $sce) { + element = $compile('')($rootScope); + + $rootScope.testUrl = "./css1.css"; + $rootScope.$apply(); + expect(element.attr('href')).toContain('css1.css'); + + $rootScope.testUrl = $sce.trustAsResourceUrl('https://elsewhere.example.org/css2.css'); + $rootScope.$apply(); + expect(element.attr('href')).toContain('https://elsewhere.example.org/css2.css'); + })); + + it('should accept valid constants', inject(function($compile, $rootScope) { + element = $compile('')($rootScope); + + $rootScope.$apply(); + expect(element.attr('href')).toContain('https://elsewhere.example.org/css2.css'); + })); + }); + if (!msie || msie >= 11) { describe('iframe[srcdoc]', function() { it('should NOT set iframe contents for untrusted values', inject(function($compile, $rootScope, $sce) {