mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-05-27 15:04:02 +00:00
fix($compile): allow data: image URIs in img[src]
Ref: 1adf29af13
BREAKING CHANGE: img[src] URLs are now sanitized via a separate
whitelist regex instead of sharing the whitelist regex with a[href].
With this change, img[src] URLs may also be data: URI's matching
mime types image/*. mailto: URLs are disallowed (and do not make
sense for img[src] but were allowed under the a[href] whitelist used
before.)
This commit is contained in:
parent
e449c6df06
commit
3e39ac7e1b
2 changed files with 77 additions and 28 deletions
|
|
@ -153,7 +153,8 @@ function $CompileProvider($provide) {
|
||||||
Suffix = 'Directive',
|
Suffix = 'Directive',
|
||||||
COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\d\w\-_]+)\s+(.*)$/,
|
COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\d\w\-_]+)\s+(.*)$/,
|
||||||
CLASS_DIRECTIVE_REGEXP = /(([\d\w\-_]+)(?:\:([^;]+))?;?)/,
|
CLASS_DIRECTIVE_REGEXP = /(([\d\w\-_]+)(?:\:([^;]+))?;?)/,
|
||||||
urlSanitizationWhitelist = /^\s*(https?|ftp|mailto|file):/;
|
aHrefSanitizationWhitelist = /^\s*(https?|ftp|mailto|file):/,
|
||||||
|
imgSrcSanitizationWhitelist = /^\s*(https?|ftp|file):|data:image\//;
|
||||||
|
|
||||||
// Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes
|
// Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes
|
||||||
// The assumption is that future DOM event attribute names will begin with
|
// The assumption is that future DOM event attribute names will begin with
|
||||||
|
|
@ -213,32 +214,61 @@ function $CompileProvider($provide) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ngdoc function
|
* @ngdoc function
|
||||||
* @name ng.$compileProvider#urlSanitizationWhitelist
|
* @name ng.$compileProvider#aHrefSanitizationWhitelist
|
||||||
* @methodOf ng.$compileProvider
|
* @methodOf ng.$compileProvider
|
||||||
* @function
|
* @function
|
||||||
*
|
*
|
||||||
* @description
|
* @description
|
||||||
* Retrieves or overrides the default regular expression that is used for whitelisting of safe
|
* Retrieves or overrides the default regular expression that is used for whitelisting of safe
|
||||||
* urls during a[href] and img[src] sanitization.
|
* urls during a[href] sanitization.
|
||||||
*
|
*
|
||||||
* The sanitization is a security measure aimed at prevent XSS attacks via html links.
|
* The sanitization is a security measure aimed at prevent XSS attacks via html links.
|
||||||
*
|
*
|
||||||
* Any url about to be assigned to a[href] or img[src] via data-binding is first normalized and
|
* Any url about to be assigned to a[href] via data-binding is first normalized and turned into
|
||||||
* turned into an absolute url. Afterwards, the url is matched against the
|
* an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
|
||||||
* `urlSanitizationWhitelist` regular expression. If a match is found, the original url is written
|
* regular expression. If a match is found, the original url is written into the dom. Otherwise,
|
||||||
* into the dom. Otherwise, the absolute url is prefixed with `'unsafe:'` string and only then is
|
* the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
|
||||||
* it written into the DOM.
|
|
||||||
*
|
*
|
||||||
* @param {RegExp=} regexp New regexp to whitelist urls with.
|
* @param {RegExp=} regexp New regexp to whitelist urls with.
|
||||||
* @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
|
* @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
|
||||||
* chaining otherwise.
|
* chaining otherwise.
|
||||||
*/
|
*/
|
||||||
this.urlSanitizationWhitelist = function(regexp) {
|
this.aHrefSanitizationWhitelist = function(regexp) {
|
||||||
if (isDefined(regexp)) {
|
if (isDefined(regexp)) {
|
||||||
urlSanitizationWhitelist = regexp;
|
aHrefSanitizationWhitelist = regexp;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
return urlSanitizationWhitelist;
|
return aHrefSanitizationWhitelist;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ngdoc function
|
||||||
|
* @name ng.$compileProvider#imgSrcSanitizationWhitelist
|
||||||
|
* @methodOf ng.$compileProvider
|
||||||
|
* @function
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* Retrieves or overrides the default regular expression that is used for whitelisting of safe
|
||||||
|
* urls during img[src] sanitization.
|
||||||
|
*
|
||||||
|
* The sanitization is a security measure aimed at prevent XSS attacks via html links.
|
||||||
|
*
|
||||||
|
* Any url about to be assigned to img[src] via data-binding is first normalized and turned into an
|
||||||
|
* absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist` regular
|
||||||
|
* expression. If a match is found, the original url is written into the dom. Otherwise, the
|
||||||
|
* absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
|
||||||
|
*
|
||||||
|
* @param {RegExp=} regexp New regexp to whitelist urls with.
|
||||||
|
* @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
|
||||||
|
* chaining otherwise.
|
||||||
|
*/
|
||||||
|
this.imgSrcSanitizationWhitelist = function(regexp) {
|
||||||
|
if (isDefined(regexp)) {
|
||||||
|
imgSrcSanitizationWhitelist = regexp;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
return imgSrcSanitizationWhitelist;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -298,8 +328,11 @@ function $CompileProvider($provide) {
|
||||||
|
|
||||||
// href property always returns normalized absolute url, so we can match against that
|
// href property always returns normalized absolute url, so we can match against that
|
||||||
normalizedVal = urlSanitizationNode.href;
|
normalizedVal = urlSanitizationNode.href;
|
||||||
if (normalizedVal !== '' && !normalizedVal.match(urlSanitizationWhitelist)) {
|
if (normalizedVal !== '') {
|
||||||
this[key] = value = 'unsafe:' + normalizedVal;
|
if ((key === 'href' && !normalizedVal.match(aHrefSanitizationWhitelist)) ||
|
||||||
|
(key === 'src' && !normalizedVal.match(imgSrcSanitizationWhitelist))) {
|
||||||
|
this[key] = value = 'unsafe:' + normalizedVal;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2551,15 +2551,38 @@ describe('$compile', function() {
|
||||||
expect(element.attr('src')).toBe('unsafe:javascript:doEvilStuff()');
|
expect(element.attr('src')).toBe('unsafe:javascript:doEvilStuff()');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should sanitize data: urls', inject(function($compile, $rootScope) {
|
it('should sanitize non-image data: urls', inject(function($compile, $rootScope) {
|
||||||
element = $compile('<img src="{{testUrl}}"></a>')($rootScope);
|
element = $compile('<img src="{{testUrl}}"></a>')($rootScope);
|
||||||
$rootScope.testUrl = "data:evilPayload";
|
$rootScope.testUrl = "data:application/javascript;charset=US-ASCII,alert('evil!');";
|
||||||
$rootScope.$apply();
|
$rootScope.$apply();
|
||||||
|
expect(element.attr('src')).toBe("unsafe:data:application/javascript;charset=US-ASCII,alert('evil!');");
|
||||||
expect(element.attr('src')).toBe('unsafe:data:evilPayload');
|
$rootScope.testUrl = "data:,foo";
|
||||||
|
$rootScope.$apply();
|
||||||
|
expect(element.attr('src')).toBe("unsafe:data:,foo");
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
it('should not sanitize data: URIs for images', inject(function($compile, $rootScope) {
|
||||||
|
element = $compile('<img src="{{dataUri}}"></img>')($rootScope);
|
||||||
|
|
||||||
|
// image data uri
|
||||||
|
// ref: http://probablyprogramming.com/2009/03/15/the-tiniest-gif-ever
|
||||||
|
$rootScope.dataUri = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
|
||||||
|
$rootScope.$apply();
|
||||||
|
expect(element.attr('src')).toBe('data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==');
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
// Fails on IE < 10 with "TypeError: Access is denied" when trying to set img[src]
|
||||||
|
if (!msie || msie > 10) {
|
||||||
|
it('should sanitize mailto: urls', inject(function($compile, $rootScope) {
|
||||||
|
element = $compile('<img src="{{testUrl}}"></a>')($rootScope);
|
||||||
|
$rootScope.testUrl = "mailto:foo@bar.com";
|
||||||
|
$rootScope.$apply();
|
||||||
|
expect(element.attr('src')).toBe('unsafe:mailto:foo@bar.com');
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
it('should sanitize obfuscated javascript: urls', inject(function($compile, $rootScope) {
|
it('should sanitize obfuscated javascript: urls', inject(function($compile, $rootScope) {
|
||||||
element = $compile('<img src="{{testUrl}}"></img>')($rootScope);
|
element = $compile('<img src="{{testUrl}}"></img>')($rootScope);
|
||||||
|
|
||||||
|
|
@ -2636,13 +2659,6 @@ describe('$compile', function() {
|
||||||
$rootScope.$apply();
|
$rootScope.$apply();
|
||||||
expect(element.attr('src')).toBe('ftp://foo.com/bar');
|
expect(element.attr('src')).toBe('ftp://foo.com/bar');
|
||||||
|
|
||||||
// Fails on IE < 10 with "TypeError: Access is denied" when trying to set img[src]
|
|
||||||
if (!msie || msie > 10) {
|
|
||||||
$rootScope.testUrl = "mailto:foo@bar.com";
|
|
||||||
$rootScope.$apply();
|
|
||||||
expect(element.attr('src')).toBe('mailto:foo@bar.com');
|
|
||||||
}
|
|
||||||
|
|
||||||
$rootScope.testUrl = "file:///foo/bar.html";
|
$rootScope.testUrl = "file:///foo/bar.html";
|
||||||
$rootScope.$apply();
|
$rootScope.$apply();
|
||||||
expect(element.attr('src')).toBe('file:///foo/bar.html');
|
expect(element.attr('src')).toBe('file:///foo/bar.html');
|
||||||
|
|
@ -2660,8 +2676,8 @@ describe('$compile', function() {
|
||||||
|
|
||||||
it('should allow reconfiguration of the src whitelist', function() {
|
it('should allow reconfiguration of the src whitelist', function() {
|
||||||
module(function($compileProvider) {
|
module(function($compileProvider) {
|
||||||
expect($compileProvider.urlSanitizationWhitelist() instanceof RegExp).toBe(true);
|
expect($compileProvider.imgSrcSanitizationWhitelist() instanceof RegExp).toBe(true);
|
||||||
var returnVal = $compileProvider.urlSanitizationWhitelist(/javascript:/);
|
var returnVal = $compileProvider.imgSrcSanitizationWhitelist(/javascript:/);
|
||||||
expect(returnVal).toBe($compileProvider);
|
expect(returnVal).toBe($compileProvider);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -2812,8 +2828,8 @@ describe('$compile', function() {
|
||||||
|
|
||||||
it('should allow reconfiguration of the href whitelist', function() {
|
it('should allow reconfiguration of the href whitelist', function() {
|
||||||
module(function($compileProvider) {
|
module(function($compileProvider) {
|
||||||
expect($compileProvider.urlSanitizationWhitelist() instanceof RegExp).toBe(true);
|
expect($compileProvider.aHrefSanitizationWhitelist() instanceof RegExp).toBe(true);
|
||||||
var returnVal = $compileProvider.urlSanitizationWhitelist(/javascript:/);
|
var returnVal = $compileProvider.aHrefSanitizationWhitelist(/javascript:/);
|
||||||
expect(returnVal).toBe($compileProvider);
|
expect(returnVal).toBe($compileProvider);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue