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

feat(sce): trust resourceUrls where url is in a $cache #4879

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 19 additions & 9 deletions src/ng/sce.js
Original file line number Diff line number Diff line change
Expand Up @@ -205,35 +205,44 @@ function $SceDelegateProvider() {
var htmlSanitizer = function htmlSanitizer(html) {
throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
};
var cacheFactory;

if ($injector.has('$sanitize')) {
htmlSanitizer = $injector.get('$sanitize');
}
if ($injector.has('$cacheFactory')) {
cacheFactory = $injector.get('$cacheFactory');
}


function matchUrl(matcher, parsedUrl) {
function matchUrl(matcher, parsedUrl, cache) {
if (matcher === 'self') {
if (cacheFactory && (cache === true || typeof cache === 'string')) {
cache = cacheFactory.get((cache === true && 'templates') || cache);
if (cache && typeof cache.get(parsedUrl.href) === 'string') {
return true;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The resource url is assumed to be trustworthy if we've already cached it somewhere. (is === 'string' the best way to do this?)

}
}
return urlIsSameOrigin(parsedUrl);
} else {
// definitely a regex. See adjustMatchers()
return !!matcher.exec(parsedUrl.href);
}
}

function isResourceUrlAllowedByPolicy(url) {
function isResourceUrlAllowedByPolicy(url, cache) {
var parsedUrl = urlResolve(url.toString());
var i, n, allowed = false;
// Ensure that at least one item from the whitelist allows this url.
for (i = 0, n = resourceUrlWhitelist.length; i < n; i++) {
if (matchUrl(resourceUrlWhitelist[i], parsedUrl)) {
if (matchUrl(resourceUrlWhitelist[i], parsedUrl, cache)) {
allowed = true;
break;
}
}
if (allowed) {
// Ensure that no item from the blacklist blocked this url.
for (i = 0, n = resourceUrlBlacklist.length; i < n; i++) {
if (matchUrl(resourceUrlBlacklist[i], parsedUrl)) {
if (matchUrl(resourceUrlBlacklist[i], parsedUrl, cache)) {
allowed = false;
break;
}
Expand Down Expand Up @@ -362,7 +371,7 @@ function $SceDelegateProvider() {
// 1. sanitize the value for the requested type, or
// 2. throw an exception.
if (type === SCE_CONTEXTS.RESOURCE_URL) {
if (isResourceUrlAllowedByPolicy(maybeTrusted)) {
if (isResourceUrlAllowedByPolicy(maybeTrusted, arguments[2])) {
return maybeTrusted;
} else {
throw $sceMinErr('insecurl',
Expand Down Expand Up @@ -796,8 +805,9 @@ function $SceProvider() {
if (parsed.literal && parsed.constant) {
return parsed;
} else {
var args = Array.prototype.slice.call(arguments, 2);
return function sceParseAsTrusted(self, locals) {
return sce.getTrusted(type, parsed(self, locals));
return sce.getTrusted.apply(null, concat([type, parsed(self, locals)], args));
};
}
};
Expand Down Expand Up @@ -1068,10 +1078,10 @@ function $SceProvider() {
forEach(SCE_CONTEXTS, function (enumValue, name) {
var lName = lowercase(name);
sce[camelCase("parse_as_" + lName)] = function (expr) {
return parse(enumValue, expr);
return parse.apply(null, concat([enumValue], arguments));
};
sce[camelCase("get_trusted_" + lName)] = function (value) {
return getTrusted(enumValue, value);
return getTrusted.apply(null, concat([enumValue], arguments));
};
sce[camelCase("trust_as_" + lName)] = function (value) {
return trustAs(enumValue, value);
Expand Down
34 changes: 34 additions & 0 deletions test/ng/sceSpecs.js
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,40 @@ describe('SCE', function() {
/[^[]*\[\$sce:imatcher\] Matchers may only be "self", string patterns or RegExp objects/.source));
});

it('should accept $templateCache-stored resourceUrls when passed `true`', inject(function($templateCache, $sce, $rootScope) {
$rootScope.template = 'test.tpl';
$templateCache.put('test.tpl', '<div>This is trusted because I put it here myself.</div>');
expect($sce.getTrustedResourceUrl('test.tpl', true)).toEqual('test.tpl');
expect($sce.parseAsResourceUrl("template", true)($rootScope)).toEqual('test.tpl');
}));

it('should reject $templateCache-stored resourceUrls by default', inject(function($templateCache, $sce, $rootScope) {
$rootScope.template = 'http://not-my-house.com/test.tpl';
$templateCache.put('http://not-my-house.com/test.tpl', '<div>This is trusted because I put it here myself.</div>');
expect(function() { $sce.getTrustedResourceUrl('http://not-my-house.com/test.tpl'); }).toThrowMinErr(
'$sce', 'insecurl', 'Blocked loading resource from url not allowed by $sceDelegate policy. URL: http://not-my-house.com/test.tpl');
expect(function() { $sce.parseAsResourceUrl("template")($rootScope); }).toThrowMinErr(
'$sce', 'insecurl', 'Blocked loading resource from url not allowed by $sceDelegate policy. URL: http://not-my-house.com/test.tpl');
}));

it('should accept cache-stored resourceUrls when passed cache name', inject(function($cacheFactory, $sce, $rootScope) {
var $templateCache = $cacheFactory('resourceUrls');
$rootScope.template = 'test.tpl';
$templateCache.put('test.tpl', '<div>This is trusted because I put it here myself.</div>');
expect($sce.getTrustedResourceUrl('test.tpl', 'resourceUrls')).toEqual('test.tpl');
expect($sce.parseAsResourceUrl("template", 'resourceUrls')($rootScope)).toEqual('test.tpl');
}));

it('should reject cache-stored resourceUrls by default', inject(function($cacheFactory, $sce, $rootScope) {
var $templateCache = $cacheFactory('resourceUrls');
$rootScope.template = 'http://not-my-house.com/test.tpl';
$templateCache.put('http://not-my-house.com/test.tpl', '<div>This is trusted because I put it here myself.</div>');
expect(function() { $sce.getTrustedResourceUrl('http://not-my-house.com/test.tpl'); }).toThrowMinErr(
'$sce', 'insecurl', 'Blocked loading resource from url not allowed by $sceDelegate policy. URL: http://not-my-house.com/test.tpl');
expect(function() { $sce.parseAsResourceUrl("template")($rootScope); }).toThrowMinErr(
'$sce', 'insecurl', 'Blocked loading resource from url not allowed by $sceDelegate policy. URL: http://not-my-house.com/test.tpl');
}));

describe('adjustMatcher', function() {
it('should rewrite regex into regex and add ^ & $ on either end', function() {
expect(adjustMatcher(/a.*b/).exec('a.b')).not.toBeNull();
Expand Down