From 333523483f3ce6dd3177b697a5e5a7177ca364c8 Mon Sep 17 00:00:00 2001
From: Tobias Bosch
Date: Mon, 25 Nov 2013 15:40:18 -0800
Subject: fix($sanitize): Use same whitelist mechanism as $compile does.
`$sanitize` now uses the same mechanism as `$compile` to validate uris.
By this, the validation in `$sanitize` is more general and can be
configured in the same way as the one in `$compile`.
Changes
- Creates the new private service `$$sanitizeUri`.
- Moves related specs from `compileSpec.js` into `sanitizeUriSpec.js`.
- Refactors the `linky` filter to be less dependent on `$sanitize`
internal functions.
Fixes #3748.
---
test/ng/compileSpec.js | 296 +++++++--------------------------------------
test/ng/sanitizeUriSpec.js | 230 +++++++++++++++++++++++++++++++++++
2 files changed, 274 insertions(+), 252 deletions(-)
create mode 100644 test/ng/sanitizeUriSpec.js
(limited to 'test/ng')
diff --git a/test/ng/compileSpec.js b/test/ng/compileSpec.js
index 80788b02..6de96f65 100755
--- a/test/ng/compileSpec.js
+++ b/test/ng/compileSpec.js
@@ -3834,6 +3834,7 @@ describe('$compile', function() {
describe('img[src] sanitization', function() {
+
it('should NOT require trusted values for img src', inject(function($rootScope, $compile, $sce) {
element = $compile('')($rootScope);
$rootScope.testUrl = 'http://example.com/image.png';
@@ -3845,127 +3846,6 @@ describe('$compile', function() {
expect(element.attr('src')).toEqual('http://example.com/image2.png');
}));
- it('should sanitize javascript: urls', inject(function($compile, $rootScope) {
- element = $compile('
')($rootScope);
- $rootScope.testUrl = "javascript:doEvilStuff()";
- $rootScope.$apply();
- expect(element.attr('src')).toBe('unsafe:javascript:doEvilStuff()');
- }));
-
- it('should sanitize non-image data: urls', inject(function($compile, $rootScope) {
- element = $compile('
')($rootScope);
- $rootScope.testUrl = "data:application/javascript;charset=US-ASCII,alert('evil!');";
- $rootScope.$apply();
- expect(element.attr('src')).toBe("unsafe:data:application/javascript;charset=US-ASCII,alert('evil!');");
- $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('
')($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('
')($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) {
- element = $compile('
')($rootScope);
-
- // case-sensitive
- $rootScope.testUrl = "JaVaScRiPt:doEvilStuff()";
- $rootScope.$apply();
- expect(element[0].src).toBe('unsafe:javascript:doEvilStuff()');
-
- // tab in protocol
- $rootScope.testUrl = "java\u0009script:doEvilStuff()";
- $rootScope.$apply();
- expect(element[0].src).toMatch(/(http:\/\/|unsafe:javascript:doEvilStuff\(\))/);
-
- // space before
- $rootScope.testUrl = " javascript:doEvilStuff()";
- $rootScope.$apply();
- expect(element[0].src).toBe('unsafe:javascript:doEvilStuff()');
-
- // ws chars before
- $rootScope.testUrl = " \u000e javascript:doEvilStuff()";
- $rootScope.$apply();
- expect(element[0].src).toMatch(/(http:\/\/|unsafe:javascript:doEvilStuff\(\))/);
-
- // post-fixed with proper url
- $rootScope.testUrl = "javascript:doEvilStuff(); http://make.me/look/good";
- $rootScope.$apply();
- expect(element[0].src).toBeOneOf(
- 'unsafe:javascript:doEvilStuff(); http://make.me/look/good',
- 'unsafe:javascript:doEvilStuff();%20http://make.me/look/good'
- );
- }));
-
- it('should sanitize ng-src bindings as well', inject(function($compile, $rootScope) {
- element = $compile('
')($rootScope);
- $rootScope.testUrl = "javascript:doEvilStuff()";
- $rootScope.$apply();
-
- expect(element[0].src).toBe('unsafe:javascript:doEvilStuff()');
- }));
-
-
- it('should not sanitize valid urls', inject(function($compile, $rootScope) {
- element = $compile('
')($rootScope);
-
- $rootScope.testUrl = "foo/bar";
- $rootScope.$apply();
- expect(element.attr('src')).toBe('foo/bar');
-
- $rootScope.testUrl = "/foo/bar";
- $rootScope.$apply();
- expect(element.attr('src')).toBe('/foo/bar');
-
- $rootScope.testUrl = "../foo/bar";
- $rootScope.$apply();
- expect(element.attr('src')).toBe('../foo/bar');
-
- $rootScope.testUrl = "#foo";
- $rootScope.$apply();
- expect(element.attr('src')).toBe('#foo');
-
- $rootScope.testUrl = "http://foo.com/bar";
- $rootScope.$apply();
- expect(element.attr('src')).toBe('http://foo.com/bar');
-
- $rootScope.testUrl = " http://foo.com/bar";
- $rootScope.$apply();
- expect(element.attr('src')).toBe(' http://foo.com/bar');
-
- $rootScope.testUrl = "https://foo.com/bar";
- $rootScope.$apply();
- expect(element.attr('src')).toBe('https://foo.com/bar');
-
- $rootScope.testUrl = "ftp://foo.com/bar";
- $rootScope.$apply();
- expect(element.attr('src')).toBe('ftp://foo.com/bar');
-
- $rootScope.testUrl = "file:///foo/bar.html";
- $rootScope.$apply();
- expect(element.attr('src')).toBe('file:///foo/bar.html');
- }));
-
-
it('should not sanitize attributes other than src', inject(function($compile, $rootScope) {
element = $compile('
')($rootScope);
$rootScope.testUrl = "javascript:doEvilStuff()";
@@ -3974,141 +3854,42 @@ describe('$compile', function() {
expect(element.attr('title')).toBe('javascript:doEvilStuff()');
}));
+ it('should use $$sanitizeUriProvider for reconfiguration of the src whitelist', function() {
+ module(function($compileProvider, $$sanitizeUriProvider) {
+ var newRe = /javascript:/,
+ returnVal;
+ expect($compileProvider.imgSrcSanitizationWhitelist()).toBe($$sanitizeUriProvider.imgSrcSanitizationWhitelist());
- it('should allow reconfiguration of the src whitelist', function() {
- module(function($compileProvider) {
- expect($compileProvider.imgSrcSanitizationWhitelist() instanceof RegExp).toBe(true);
- var returnVal = $compileProvider.imgSrcSanitizationWhitelist(/javascript:/);
+ returnVal = $compileProvider.imgSrcSanitizationWhitelist(newRe);
expect(returnVal).toBe($compileProvider);
+ expect($$sanitizeUriProvider.imgSrcSanitizationWhitelist()).toBe(newRe);
+ expect($compileProvider.imgSrcSanitizationWhitelist()).toBe(newRe);
});
+ inject(function() {
+ // needed to the module definition above is run...
+ });
+ });
+ it('should use $$sanitizeUri', function() {
+ var $$sanitizeUri = jasmine.createSpy('$$sanitizeUri');
+ module(function($provide) {
+ $provide.value('$$sanitizeUri', $$sanitizeUri);
+ });
inject(function($compile, $rootScope) {
element = $compile('
')($rootScope);
+ $rootScope.testUrl = "someUrl";
- // Fails on IE <= 11 with "TypeError: Object doesn't support this property or method" when
- // trying to set img[src]
- if (!msie || msie > 11) {
- $rootScope.testUrl = "javascript:doEvilStuff()";
- $rootScope.$apply();
- expect(element.attr('src')).toBe('javascript:doEvilStuff()');
- }
-
- $rootScope.testUrl = "http://recon/figured";
+ $$sanitizeUri.andReturn('someSanitizedUrl');
$rootScope.$apply();
- expect(element.attr('src')).toBe('unsafe:http://recon/figured');
+ expect(element.attr('src')).toBe('someSanitizedUrl');
+ expect($$sanitizeUri).toHaveBeenCalledWith($rootScope.testUrl, true);
});
});
-
});
describe('a[href] sanitization', function() {
- it('should sanitize javascript: urls', inject(function($compile, $rootScope) {
- element = $compile('')($rootScope);
- $rootScope.testUrl = "javascript:doEvilStuff()";
- $rootScope.$apply();
-
- expect(element.attr('href')).toBe('unsafe:javascript:doEvilStuff()');
- }));
-
-
- it('should sanitize data: urls', inject(function($compile, $rootScope) {
- element = $compile('')($rootScope);
- $rootScope.testUrl = "data:evilPayload";
- $rootScope.$apply();
-
- expect(element.attr('href')).toBe('unsafe:data:evilPayload');
- }));
-
-
- it('should sanitize obfuscated javascript: urls', inject(function($compile, $rootScope) {
- element = $compile('')($rootScope);
-
- // case-sensitive
- $rootScope.testUrl = "JaVaScRiPt:doEvilStuff()";
- $rootScope.$apply();
- expect(element[0].href).toBe('unsafe:javascript:doEvilStuff()');
-
- // tab in protocol
- $rootScope.testUrl = "java\u0009script:doEvilStuff()";
- $rootScope.$apply();
- expect(element[0].href).toMatch(/(http:\/\/|unsafe:javascript:doEvilStuff\(\))/);
-
- // space before
- $rootScope.testUrl = " javascript:doEvilStuff()";
- $rootScope.$apply();
- expect(element[0].href).toBe('unsafe:javascript:doEvilStuff()');
-
- // ws chars before
- $rootScope.testUrl = " \u000e javascript:doEvilStuff()";
- $rootScope.$apply();
- expect(element[0].href).toMatch(/(http:\/\/|unsafe:javascript:doEvilStuff\(\))/);
-
- // post-fixed with proper url
- $rootScope.testUrl = "javascript:doEvilStuff(); http://make.me/look/good";
- $rootScope.$apply();
- expect(element[0].href).toBeOneOf(
- 'unsafe:javascript:doEvilStuff(); http://make.me/look/good',
- 'unsafe:javascript:doEvilStuff();%20http://make.me/look/good'
- );
- }));
-
-
- it('should sanitize ngHref bindings as well', inject(function($compile, $rootScope) {
- element = $compile('')($rootScope);
- $rootScope.testUrl = "javascript:doEvilStuff()";
- $rootScope.$apply();
-
- expect(element[0].href).toBe('unsafe:javascript:doEvilStuff()');
- }));
-
-
- it('should not sanitize valid urls', inject(function($compile, $rootScope) {
- element = $compile('')($rootScope);
-
- $rootScope.testUrl = "foo/bar";
- $rootScope.$apply();
- expect(element.attr('href')).toBe('foo/bar');
-
- $rootScope.testUrl = "/foo/bar";
- $rootScope.$apply();
- expect(element.attr('href')).toBe('/foo/bar');
-
- $rootScope.testUrl = "../foo/bar";
- $rootScope.$apply();
- expect(element.attr('href')).toBe('../foo/bar');
-
- $rootScope.testUrl = "#foo";
- $rootScope.$apply();
- expect(element.attr('href')).toBe('#foo');
-
- $rootScope.testUrl = "http://foo/bar";
- $rootScope.$apply();
- expect(element.attr('href')).toBe('http://foo/bar');
-
- $rootScope.testUrl = " http://foo/bar";
- $rootScope.$apply();
- expect(element.attr('href')).toBe(' http://foo/bar');
-
- $rootScope.testUrl = "https://foo/bar";
- $rootScope.$apply();
- expect(element.attr('href')).toBe('https://foo/bar');
-
- $rootScope.testUrl = "ftp://foo/bar";
- $rootScope.$apply();
- expect(element.attr('href')).toBe('ftp://foo/bar');
-
- $rootScope.testUrl = "mailto:foo@bar.com";
- $rootScope.$apply();
- expect(element.attr('href')).toBe('mailto:foo@bar.com');
-
- $rootScope.testUrl = "file:///foo/bar.html";
- $rootScope.$apply();
- expect(element.attr('href')).toBe('file:///foo/bar.html');
- }));
-
-
it('should not sanitize href on elements other than anchor', inject(function($compile, $rootScope) {
element = $compile('