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

Commit f35f334

Browse files
rjametgkalpak
authored andcommitted
fix($compile): secure link[href] as a RESOURCE_URLs 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: ``` <link href="{{ 'http://mydomain.org/unsafe.css' }}" rel="stylesheet"> ``` 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)`.
1 parent dd4ce50 commit f35f334

File tree

2 files changed

+33
-0
lines changed

2 files changed

+33
-0
lines changed

src/ng/compile.js

+2
Original file line numberDiff line numberDiff line change
@@ -1880,6 +1880,8 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
18801880
// maction[xlink:href] can source SVG. It's not limited to <maction>.
18811881
if (attrNormalizedName == "xlinkHref" ||
18821882
(tag == "FORM" && attrNormalizedName == "action") ||
1883+
// links can be stylesheets or imports, which can run script in the current origin
1884+
(tag == "LINK" && attrNormalizedName == "href") ||
18831885
(tag != "IMG" && (attrNormalizedName == "src" ||
18841886
attrNormalizedName == "ngSrc"))) {
18851887
return $sce.RESOURCE_URL;

test/ng/compileSpec.js

+31
Original file line numberDiff line numberDiff line change
@@ -4913,6 +4913,7 @@ describe('$compile', function() {
49134913
"loading resource from url not allowed by $sceDelegate policy. URL: javascript:doTrustedStuff()");
49144914
}));
49154915

4916+
49164917
it('should pass through $sce.trustAs() values in action attribute', inject(function($compile, $rootScope, $sce) {
49174918
/* jshint scripturl:true */
49184919
element = $compile('<form action="{{testUrl}}"></form>')($rootScope);
@@ -4923,6 +4924,36 @@ describe('$compile', function() {
49234924
}));
49244925
});
49254926

4927+
describe('link[href]', function() {
4928+
it('should reject invalid RESOURCE_URLs', inject(function($compile, $rootScope) {
4929+
element = $compile('<link href="{{testUrl}}" rel="stylesheet" />')($rootScope);
4930+
$rootScope.testUrl = "https://evil.example.org/css.css";
4931+
expect(function() { $rootScope.$apply(); }).toThrowMinErr(
4932+
"$interpolate", "interr", "Can't interpolate: {{testUrl}}\nError: [$sce:insecurl] Blocked " +
4933+
"loading resource from url not allowed by $sceDelegate policy. URL: " +
4934+
"https://evil.example.org/css.css");
4935+
}));
4936+
4937+
it('should accept valid RESOURCE_URLs', inject(function($compile, $rootScope, $sce) {
4938+
element = $compile('<link href="{{testUrl}}" rel="stylesheet" />')($rootScope);
4939+
4940+
$rootScope.testUrl = "./css1.css";
4941+
$rootScope.$apply();
4942+
expect(element.attr('href')).toContain('css1.css');
4943+
4944+
$rootScope.testUrl = $sce.trustAsResourceUrl('https://elsewhere.example.org/css2.css');
4945+
$rootScope.$apply();
4946+
expect(element.attr('href')).toContain('https://elsewhere.example.org/css2.css');
4947+
}));
4948+
4949+
it('should accept valid constants', inject(function($compile, $rootScope) {
4950+
element = $compile('<link href="https://elsewhere.example.org/css2.css" rel="stylesheet" />')($rootScope);
4951+
4952+
$rootScope.$apply();
4953+
expect(element.attr('href')).toContain('https://elsewhere.example.org/css2.css');
4954+
}));
4955+
});
4956+
49264957
if (!msie || msie >= 11) {
49274958
describe('iframe[srcdoc]', function() {
49284959
it('should NOT set iframe contents for untrusted values', inject(function($compile, $rootScope, $sce) {

0 commit comments

Comments
 (0)