'use strict'; /* eslint-disable no-script-url */ describe('sanitizeUri', function() { var sanitizeHref, sanitizeImg, sanitizeUriProvider, testUrl; beforeEach(function() { module(function(_$$sanitizeUriProvider_) { sanitizeUriProvider = _$$sanitizeUriProvider_; }); inject(function($$sanitizeUri) { sanitizeHref = function(uri) { return $$sanitizeUri(uri, false); }; sanitizeImg = function(uri) { return $$sanitizeUri(uri, true); }; }); }); function isEvilInCurrentBrowser(uri) { var a = window.document.createElement('a'); a.setAttribute('href', uri); return a.href.substring(0, 4) !== 'http'; } describe('img[src] sanitization', function() { it('should sanitize javascript: urls', function() { testUrl = 'javascript:doEvilStuff()'; expect(sanitizeImg(testUrl)).toBe('unsafe:javascript:doEvilStuff()'); }); it('should sanitize javascript: urls with comments', function() { testUrl = 'javascript:alert(1)//data:image/'; expect(sanitizeImg(testUrl)).toBe('unsafe:javascript:alert(1)//data:image/'); }); it('should sanitize non-image data: urls', function() { testUrl = 'data:application/javascript;charset=US-ASCII,alert(\'evil!\');'; expect(sanitizeImg(testUrl)).toBe('unsafe:data:application/javascript;charset=US-ASCII,alert(\'evil!\');'); testUrl = 'data:,foo'; expect(sanitizeImg(testUrl)).toBe('unsafe:data:,foo'); }); it('should sanitize mailto: urls', function() { testUrl = 'mailto:foo@bar.com'; expect(sanitizeImg(testUrl)).toBe('unsafe:mailto:foo@bar.com'); }); it('should sanitize obfuscated javascript: urls', function() { // case-sensitive testUrl = 'JaVaScRiPt:doEvilStuff()'; expect(sanitizeImg(testUrl)).toBe('unsafe:javascript:doEvilStuff()'); // tab in protocol testUrl = 'java\u0009script:doEvilStuff()'; if (isEvilInCurrentBrowser(testUrl)) { expect(sanitizeImg(testUrl)).toEqual('unsafe:javascript:doEvilStuff()'); } // space before testUrl = ' javascript:doEvilStuff()'; expect(sanitizeImg(testUrl)).toBe('unsafe:javascript:doEvilStuff()'); // ws chars before testUrl = ' \u000e javascript:doEvilStuff()'; if (isEvilInCurrentBrowser(testUrl)) { expect(sanitizeImg(testUrl)).toEqual('unsafe:javascript:doEvilStuff()'); } // post-fixed with proper url testUrl = 'javascript:doEvilStuff(); http://make.me/look/good'; expect(sanitizeImg(testUrl)).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', function() { testUrl = 'javascript:doEvilStuff()'; expect(sanitizeImg(testUrl)).toBe('unsafe:javascript:doEvilStuff()'); }); it('should not sanitize valid urls', function() { testUrl = 'foo/bar'; expect(sanitizeImg(testUrl)).toBe('foo/bar'); testUrl = '/foo/bar'; expect(sanitizeImg(testUrl)).toBe('/foo/bar'); testUrl = '../foo/bar'; expect(sanitizeImg(testUrl)).toBe('../foo/bar'); testUrl = '#foo'; expect(sanitizeImg(testUrl)).toBe('#foo'); testUrl = 'http://foo.com/bar'; expect(sanitizeImg(testUrl)).toBe('http://foo.com/bar'); testUrl = ' http://foo.com/bar'; expect(sanitizeImg(testUrl)).toBe(' http://foo.com/bar'); testUrl = 'https://foo.com/bar'; expect(sanitizeImg(testUrl)).toBe('https://foo.com/bar'); testUrl = 'ftp://foo.com/bar'; expect(sanitizeImg(testUrl)).toBe('ftp://foo.com/bar'); testUrl = 'file:///foo/bar.html'; expect(sanitizeImg(testUrl)).toBe('file:///foo/bar.html'); }); it('should not sanitize blob urls', function() { testUrl = 'blob:///foo/bar.html'; expect(sanitizeImg(testUrl)).toBe('blob:///foo/bar.html'); }); it('should not sanitize data: URIs for images', function() { // image data uri // ref: http://probablyprogramming.com/2009/03/15/the-tiniest-gif-ever testUrl = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=='; expect(sanitizeImg(testUrl)).toBe('data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=='); }); it('should allow reconfiguration of the src whitelist', function() { var returnVal; expect(sanitizeUriProvider.imgSrcSanitizationWhitelist() instanceof RegExp).toBe(true); returnVal = sanitizeUriProvider.imgSrcSanitizationWhitelist(/javascript:/); expect(returnVal).toBe(sanitizeUriProvider); testUrl = 'javascript:doEvilStuff()'; expect(sanitizeImg(testUrl)).toBe('javascript:doEvilStuff()'); testUrl = 'http://recon/figured'; expect(sanitizeImg(testUrl)).toBe('unsafe:http://recon/figured'); }); }); describe('a[href] sanitization', function() { it('should sanitize javascript: urls', inject(function() { testUrl = 'javascript:doEvilStuff()'; expect(sanitizeHref(testUrl)).toBe('unsafe:javascript:doEvilStuff()'); })); it('should sanitize data: urls', inject(function() { testUrl = 'data:evilPayload'; expect(sanitizeHref(testUrl)).toBe('unsafe:data:evilPayload'); })); it('should sanitize obfuscated javascript: urls', inject(function() { // case-sensitive testUrl = 'JaVaScRiPt:doEvilStuff()'; expect(sanitizeHref(testUrl)).toBe('unsafe:javascript:doEvilStuff()'); // tab in protocol testUrl = 'java\u0009script:doEvilStuff()'; if (isEvilInCurrentBrowser(testUrl)) { expect(sanitizeHref(testUrl)).toEqual('unsafe:javascript:doEvilStuff()'); } // space before testUrl = ' javascript:doEvilStuff()'; expect(sanitizeHref(testUrl)).toBe('unsafe:javascript:doEvilStuff()'); // ws chars before testUrl = ' \u000e javascript:doEvilStuff()'; if (isEvilInCurrentBrowser(testUrl)) { expect(sanitizeHref(testUrl)).toEqual('unsafe:javascript:doEvilStuff()'); } // post-fixed with proper url testUrl = 'javascript:doEvilStuff(); http://make.me/look/good'; expect(sanitizeHref(testUrl)).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() { testUrl = 'javascript:doEvilStuff()'; expect(sanitizeHref(testUrl)).toBe('unsafe:javascript:doEvilStuff()'); })); it('should not sanitize valid urls', inject(function() { testUrl = 'foo/bar'; expect(sanitizeHref(testUrl)).toBe('foo/bar'); testUrl = '/foo/bar'; expect(sanitizeHref(testUrl)).toBe('/foo/bar'); testUrl = '../foo/bar'; expect(sanitizeHref(testUrl)).toBe('../foo/bar'); testUrl = '#foo'; expect(sanitizeHref(testUrl)).toBe('#foo'); testUrl = 'http://foo/bar'; expect(sanitizeHref(testUrl)).toBe('http://foo/bar'); testUrl = ' http://foo/bar'; expect(sanitizeHref(testUrl)).toBe(' http://foo/bar'); testUrl = 'https://foo/bar'; expect(sanitizeHref(testUrl)).toBe('https://foo/bar'); testUrl = 'ftp://foo/bar'; expect(sanitizeHref(testUrl)).toBe('ftp://foo/bar'); testUrl = 'sftp://foo/bar'; expect(sanitizeHref(testUrl)).toBe('sftp://foo/bar'); testUrl = 'mailto:foo@bar.com'; expect(sanitizeHref(testUrl)).toBe('mailto:foo@bar.com'); testUrl = 'file:///foo/bar.html'; expect(sanitizeHref(testUrl)).toBe('file:///foo/bar.html'); })); it('should allow reconfiguration of the href whitelist', function() { var returnVal; expect(sanitizeUriProvider.aHrefSanitizationWhitelist() instanceof RegExp).toBe(true); returnVal = sanitizeUriProvider.aHrefSanitizationWhitelist(/javascript:/); expect(returnVal).toBe(sanitizeUriProvider); testUrl = 'javascript:doEvilStuff()'; expect(sanitizeHref(testUrl)).toBe('javascript:doEvilStuff()'); testUrl = 'http://recon/figured'; expect(sanitizeHref(testUrl)).toBe('unsafe:http://recon/figured'); }); }); });