'use strict'; describe('HTML', function() { var expectHTML; beforeEach(module('ngSanitize')); beforeEach(function() { expectHTML = function(html){ var sanitize; inject(function($sanitize) { sanitize = $sanitize; }); return expect(sanitize(html)); }; }); describe('htmlParser', function() { if (angular.isUndefined(window.htmlParser)) return; var handler, start, text, comment; beforeEach(function() { handler = { start: function(tag, attrs, unary){ start = { tag: tag, attrs: attrs, unary: unary }; // Since different browsers handle newlines differently we trim // so that it is easier to write tests. angular.forEach(attrs, function(value, key) { attrs[key] = value.replace(/^\s*/, '').replace(/\s*$/, '') }); }, chars: function(text_){ text = text_; }, end:function(tag) { expect(tag).toEqual(start.tag); }, comment:function(comment_) { comment = comment_; } }; }); it('should parse comments', function() { htmlParser('', handler); expect(comment).toEqual('FOOBAR'); }); it('should throw an exception for invalid comments', function() { var caught=false; try { htmlParser('', handler); } catch (ex) { caught = true; // expected an exception due to a bad parse } expect(caught).toBe(true); }); it('double-dashes are not allowed in a comment', function() { var caught=false; try { htmlParser('', handler); } catch (ex) { caught = true; // expected an exception due to a bad parse } expect(caught).toBe(true); }); it('should parse basic format', function() { htmlParser('text', handler); expect(start).toEqual({tag:'tag', attrs:{attr:'value'}, unary:false}); expect(text).toEqual('text'); }); it('should parse newlines in tags', function() { htmlParser('<\ntag\n attr="value"\n>text<\n/\ntag\n>', handler); expect(start).toEqual({tag:'tag', attrs:{attr:'value'}, unary:false}); expect(text).toEqual('text'); }); it('should parse newlines in attributes', function() { htmlParser('text', handler); expect(start).toEqual({tag:'tag', attrs:{attr:'value'}, unary:false}); expect(text).toEqual('text'); }); it('should parse namespace', function() { htmlParser('text', handler); expect(start).toEqual({tag:'ns:t-a-g', attrs:{'ns:a-t-t-r':'value'}, unary:false}); expect(text).toEqual('text'); }); it('should parse empty value attribute of node', function() { htmlParser('', handler); expect(start).toEqual({tag:'option', attrs:{selected:'', value:''}, unary:false}); expect(text).toEqual('abc'); }); }); // THESE TESTS ARE EXECUTED WITH COMPILED ANGULAR it('should echo html', function() { expectHTML('helloworld.'). toEqual('helloworld.'); }); it('should remove script', function() { expectHTML('ac.').toEqual('ac.'); }); it('should remove double nested script', function() { expectHTML('ailc.').toEqual('ac.'); }); it('should remove unknown names', function() { expectHTML('abc').toEqual('abc'); }); it('should remove unsafe value', function() { expectHTML('').toEqual(''); }); it('should handle self closed elements', function() { expectHTML('a
c').toEqual('a
c'); }); it('should handle namespace', function() { expectHTML('abc').toEqual('abc'); }); it('should handle entities', function() { var everything = '
' + '!@#$%^&*()_+-={}[]:";\'<>?,./`~ ħ
'; expectHTML(everything).toEqual(everything); }); it('should handle improper html', function() { expectHTML('< div rel="" alt=abc dir=\'"\' >text< /div>'). toEqual('
text
'); }); it('should handle improper html2', function() { expectHTML('< div rel="" / >'). toEqual('
'); }); it('should ignore back slash as escape', function() { expectHTML('xxx\\'). toEqual('xxx\\'); }); it('should ignore object attributes', function() { expectHTML(':)'). toEqual(':)'); expectHTML(':)'). toEqual(''); }); it('should keep spaces as prefix/postfix', function() { expectHTML(' a ').toEqual(' a '); }); it('should allow multiline strings', function() { expectHTML('\na\n').toEqual(' a\ '); }); describe('htmlSanitizerWriter', function() { if (angular.isUndefined(window.htmlSanitizeWriter)) return; var writer, html, uriValidator; beforeEach(function() { html = ''; uriValidator = jasmine.createSpy('uriValidator'); writer = htmlSanitizeWriter({push:function(text){html+=text;}}, uriValidator); }); it('should write basic HTML', function() { writer.chars('before'); writer.start('div', {rel:'123'}, false); writer.chars('in'); writer.end('div'); writer.chars('after'); expect(html).toEqual('before
in
after'); }); it('should escape text nodes', function() { writer.chars('a
&
c'); expect(html).toEqual('a<div>&</div>c'); }); it('should escape IE script', function() { writer.chars('&<>{}'); expect(html).toEqual('&<>{}'); }); it('should escape attributes', function() { writer.start('div', {rel:'!@#$%^&*()_+-={}[]:";\'<>?,./`~ \n\0\r\u0127'}); expect(html).toEqual('
'); }); it('should ignore missformed elements', function() { writer.start('d>i&v', {}); expect(html).toEqual(''); }); it('should ignore unknown attributes', function() { writer.start('div', {unknown:""}); expect(html).toEqual('
'); }); describe('explicitly disallow', function() { it('should not allow attributes', function() { writer.start('div', {id:'a', name:'a', style:'a'}); expect(html).toEqual('
'); }); it('should not allow tags', function() { function tag(name) { writer.start(name, {}); writer.end(name); } tag('frameset'); tag('frame'); tag('form'); tag('param'); tag('object'); tag('embed'); tag('textarea'); tag('input'); tag('button'); tag('option'); tag('select'); tag('script'); tag('style'); tag('link'); tag('base'); tag('basefont'); expect(html).toEqual(''); }); }); describe('uri validation', function() { it('should call the uri validator', function() { writer.start('a', {href:'someUrl'}, false); expect(uriValidator).toHaveBeenCalledWith('someUrl', false); uriValidator.reset(); writer.start('img', {src:'someImgUrl'}, false); expect(uriValidator).toHaveBeenCalledWith('someImgUrl', true); uriValidator.reset(); writer.start('someTag', {src:'someNonUrl'}, false); expect(uriValidator).not.toHaveBeenCalled(); }); it('should drop non valid uri attributes', function() { uriValidator.andReturn(false); writer.start('a', {href:'someUrl'}, false); expect(html).toEqual(''); html = ''; uriValidator.andReturn(true); writer.start('a', {href:'someUrl'}, false); expect(html).toEqual(''); }); }); }); describe('uri checking', function() { beforeEach(function() { this.addMatchers({ toBeValidUrl: function() { var sanitize; inject(function($sanitize) { sanitize = $sanitize; }); var input = ''; return sanitize(input) === input; }, toBeValidImageSrc: function() { var sanitize; inject(function($sanitize) { sanitize = $sanitize; }); var input = ''; return sanitize(input) === input; } }); }); it('should use $$sanitizeUri for links', function() { var $$sanitizeUri = jasmine.createSpy('$$sanitizeUri'); module(function($provide) { $provide.value('$$sanitizeUri', $$sanitizeUri); }); inject(function() { $$sanitizeUri.andReturn('someUri'); expectHTML('').toEqual(''); expect($$sanitizeUri).toHaveBeenCalledWith('someUri', false); $$sanitizeUri.andReturn('unsafe:someUri'); expectHTML('').toEqual(''); }); }); it('should use $$sanitizeUri for links', function() { var $$sanitizeUri = jasmine.createSpy('$$sanitizeUri'); module(function($provide) { $provide.value('$$sanitizeUri', $$sanitizeUri); }); inject(function() { $$sanitizeUri.andReturn('someUri'); expectHTML('').toEqual(''); expect($$sanitizeUri).toHaveBeenCalledWith('someUri', true); $$sanitizeUri.andReturn('unsafe:someUri'); expectHTML('').toEqual(''); }); }); it('should be URI', function() { expect('').toBeValidUrl(); expect('http://abc').toBeValidUrl(); expect('HTTP://abc').toBeValidUrl(); expect('https://abc').toBeValidUrl(); expect('HTTPS://abc').toBeValidUrl(); expect('ftp://abc').toBeValidUrl(); expect('FTP://abc').toBeValidUrl(); expect('mailto:me@example.com').toBeValidUrl(); expect('MAILTO:me@example.com').toBeValidUrl(); expect('tel:123-123-1234').toBeValidUrl(); expect('TEL:123-123-1234').toBeValidUrl(); expect('#anchor').toBeValidUrl(); expect('/page1.md').toBeValidUrl(); }); it('should not be URI', function() { expect('javascript:alert').not.toBeValidUrl(); }); describe('javascript URLs', function() { it('should ignore javascript:', function() { expect('JavaScript:abc').not.toBeValidUrl(); expect(' \n Java\n Script:abc').not.toBeValidUrl(); expect('http://JavaScript/my.js').toBeValidUrl(); }); it('should ignore dec encoded javascript:', function() { expect('javascript:').not.toBeValidUrl(); expect('javascript:').not.toBeValidUrl(); expect('j avascript:').not.toBeValidUrl(); }); it('should ignore decimal with leading 0 encodede javascript:', function() { expect('javascript:').not.toBeValidUrl(); expect('j avascript:').not.toBeValidUrl(); expect('j avascript:').not.toBeValidUrl(); }); it('should ignore hex encoded javascript:', function() { expect('javascript:').not.toBeValidUrl(); expect('javascript:').not.toBeValidUrl(); expect('j avascript:').not.toBeValidUrl(); }); it('should ignore hex encoded whitespace javascript:', function() { expect('jav ascript:alert();').not.toBeValidUrl(); expect('jav ascript:alert();').not.toBeValidUrl(); expect('jav ascript:alert();').not.toBeValidUrl(); expect('jav\u0000ascript:alert();').not.toBeValidUrl(); expect('java\u0000\u0000script:alert();').not.toBeValidUrl(); expect('  java\u0000\u0000script:alert();').not.toBeValidUrl(); }); }); }); describe('sanitizeText', function() { it('should escape text', function() { expect(sanitizeText('a
&
c')).toEqual('a<div>&</div>c'); }); }); }); describe('decodeEntities', function() { var handler, text, origHiddenPre = window.hiddenPre; beforeEach(function() { text = ''; handler = { start: function() {}, chars: function(text_){ text = text_; }, end: function() {}, comment: function() {} }; module('ngSanitize'); }); afterEach(function() { window.hiddenPre = origHiddenPre; }); it('should use innerText if textContent is not available (IE<9)', function() { window.hiddenPre = { innerText: 'INNER_TEXT' }; inject(function($sanitize) { htmlParser('text', handler); expect(text).toEqual('INNER_TEXT'); }); }); it('should use textContent if available', function() { window.hiddenPre = { textContent: 'TEXT_CONTENT', innerText: 'INNER_TEXT' }; inject(function($sanitize) { htmlParser('text', handler); expect(text).toEqual('TEXT_CONTENT'); }); }); it('should use textContent even if empty', function() { window.hiddenPre = { textContent: '', innerText: 'INNER_TEXT' }; inject(function($sanitize) { htmlParser('text', handler); expect(text).toEqual(''); }); }); });