Path: blob/trunk/third_party/closure/goog/html/sanitizer/unsafe_test.js
2868 views
// Copyright 2016 The Closure Library Authors. All Rights Reserved.1//2// Licensed under the Apache License, Version 2.0 (the "License");3// you may not use this file except in compliance with the License.4// You may obtain a copy of the License at5//6// http://www.apache.org/licenses/LICENSE-2.07//8// Unless required by applicable law or agreed to in writing, software9// distributed under the License is distributed on an "AS-IS" BASIS,10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.11// See the License for the specific language governing permissions and12// limitations under the License.1314/**15* @fileoverview Unit Test for the unsafe API of the HTML Sanitizer.16*/1718goog.setTestOnly();1920goog.require('goog.html.SafeHtml');21goog.require('goog.html.sanitizer.HtmlSanitizer');22goog.require('goog.html.sanitizer.TagBlacklist');23goog.require('goog.html.sanitizer.unsafe');2425goog.require('goog.string.Const');26goog.require('goog.testing.dom');27goog.require('goog.testing.jsunit');28goog.require('goog.userAgent');2930/**31* @return {boolean} Whether the browser is IE8 or below.32*/33function isIE8() {34return goog.userAgent.IE && !goog.userAgent.isVersionOrHigher(9);35}363738/**39* @return {boolean} Whether the browser is IE9.40*/41function isIE9() {42return goog.userAgent.IE && !goog.userAgent.isVersionOrHigher(10) && !isIE8();43}444546var just = goog.string.Const.from('test');474849/**50* Sanitizes the original HTML and asserts that it is the same as the expected51* HTML. Supports adding tags and attributes through the unsafe API.52* @param {string} originalHtml53* @param {string} expectedHtml54* @param {?Array<string>=} opt_tags55* @param {?Array<(string|!goog.html.sanitizer.HtmlSanitizerAttributePolicy)>=}56* opt_attrs57* @param {?goog.html.sanitizer.HtmlSanitizer.Builder=} opt_builder58*/59function assertSanitizedHtml(60originalHtml, expectedHtml, opt_tags, opt_attrs, opt_builder) {61var builder = opt_builder || new goog.html.sanitizer.HtmlSanitizer.Builder();62if (opt_tags)63builder = goog.html.sanitizer.unsafe.alsoAllowTags(just, builder, opt_tags);64if (opt_attrs)65builder = goog.html.sanitizer.unsafe.alsoAllowAttributes(66just, builder, opt_attrs);67var sanitizer = builder.build();68try {69var sanitized = sanitizer.sanitize(originalHtml);70if (isIE9()) {71assertEquals('', goog.html.SafeHtml.unwrap(sanitized));72return;73}74goog.testing.dom.assertHtmlMatches(75expectedHtml, goog.html.SafeHtml.unwrap(sanitized),76true /* opt_strictAttributes */);77} catch (err) {78if (!isIE8()) {79throw err;80}81}82}838485function testAllowEmptyTagList() {86var input = '<sdf><aaa></aaa></sdf><b></b>';87var expected = '<span><span></span></span><b></b>';88assertSanitizedHtml(input, expected, []);89}909192function testAllowBlacklistedTag() {93var input = '<div><script>aaa</script></div>';94var expected = '<div></div>';95assertSanitizedHtml(input, expected, ['SCriPT']);96}979899function testAllowUnknownTags() {100var input = '<hello><bye>aaa</bye></hello><zzz></zzz>';101var expected = '<hello><span>aaa</span></hello><zzz></zzz>';102assertSanitizedHtml(input, expected, ['HElLO', 'zZZ']);103}104105106function testAllowAlreadyWhiteListedTag() {107var input = '<hello><p><zzz></zzz></p></hello>';108var expected = '<span><p><zzz></zzz></p></span>';109assertSanitizedHtml(input, expected, ['p', 'ZZZ']);110}111112113function testAllowEmptyAttrList() {114var input = '<a href="#" qwe="nope">b</a>';115var expected = '<a href="#">b</a>';116assertSanitizedHtml(input, expected, null, []);117}118119120function testAllowUnknownAttributeSimple() {121var input = '<qqq zzz="3" nnn="no"></qqq>';122var expected = '<span zzz="3"></span>';123assertSanitizedHtml(input, expected, null, ['Zzz']);124}125126127function testAllowUnknownAttributeWildCard() {128var input = '<div ab="yes" bb="no"><img ab="yep" bb="no" /></div>';129var expected = '<div ab="yes"><img ab="yep" /></div>';130assertSanitizedHtml(131input, expected, null, [{tagName: '*', attributeName: 'aB'}]);132}133134135function testAllowUnknownAttributeOnSpecificTag() {136var input = '<a www="3" zzz="4">fff</a><img www="3" />';137var expected = '<a www="3">fff</a><img />';138assertSanitizedHtml(139input, expected, null, [{tagName: 'a', attributeName: 'WwW'}]);140}141142143function testAllowUnknownAttributePolicy() {144var input = '<img ab="yes" /><img ab="no" />';145var expected = '<img ab="yes" /><img />';146assertSanitizedHtml(input, expected, null, [{147tagName: '*',148attributeName: 'aB',149policy: function(value, hints) {150assertEquals(hints.attributeName, 'ab');151return value === 'yes' ? value : null;152}153}]);154}155156157function testAllowOverwriteAttrPolicy() {158var input = '<a href="yes"></a><a href="no"></a>';159var expected = '<a href="yes"></a><a></a>';160assertSanitizedHtml(161input, expected, null, [{162tagName: 'a',163attributeName: 'href',164policy: function(value) { return value === 'yes' ? value : null; }165}]);166}167168169function testWhitelistAliasing() {170var builder = new goog.html.sanitizer.HtmlSanitizer.Builder();171goog.html.sanitizer.unsafe.alsoAllowTags(just, builder, ['QqQ']);172goog.html.sanitizer.unsafe.alsoAllowAttributes(just, builder, ['QqQ']);173builder.build();174assertUndefined(goog.html.sanitizer.TagWhitelist['QQQ']);175assertUndefined(goog.html.sanitizer.TagWhitelist['QqQ']);176assertUndefined(goog.html.sanitizer.TagWhitelist['qqq']);177assertUndefined(goog.html.sanitizer.AttributeWhitelist['* QQQ']);178assertUndefined(goog.html.sanitizer.AttributeWhitelist['* QqQ']);179assertUndefined(goog.html.sanitizer.AttributeWhitelist['* qqq']);180}181182183function testTemplateUnsanitized() {184if (!goog.html.sanitizer.HTML_SANITIZER_TEMPLATE_SUPPORTED) {185return;186}187var input = '<template><div>a</div><script>qqq</script>' +188'<template>a</template></template>';189// TODO(pelizzi): use unblockTag once it's available190delete goog.html.sanitizer.TagBlacklist['TEMPLATE'];191var builder = new goog.html.sanitizer.HtmlSanitizer.Builder();192goog.html.sanitizer.unsafe.keepUnsanitizedTemplateContents(just, builder);193assertSanitizedHtml(input, input, ['TEMPLATE'], null, builder);194goog.html.sanitizer.TagBlacklist['TEMPLATE'] = true;195}196197198function testTemplateSanitizedUnsanitizedXSS() {199if (!goog.html.sanitizer.HTML_SANITIZER_TEMPLATE_SUPPORTED) {200return;201}202var input = '<template><p>a</p><script>aaaa;</script></template>';203var expected = '<span><p>a</p></span>';204delete goog.html.sanitizer.TagBlacklist['TEMPLATE'];205var builder = new goog.html.sanitizer.HtmlSanitizer.Builder();206goog.html.sanitizer.unsafe.keepUnsanitizedTemplateContents(just, builder);207assertSanitizedHtml(input, expected, null, null, builder);208goog.html.sanitizer.TagBlacklist['TEMPLATE'] = true;209}210211212function testTemplateUnsanitizedThrowsIE() {213if (goog.html.sanitizer.HTML_SANITIZER_TEMPLATE_SUPPORTED) {214return;215}216var builder = new goog.html.sanitizer.HtmlSanitizer.Builder();217assertThrows(function() {218goog.html.sanitizer.unsafe.keepUnsanitizedTemplateContents(just, builder);219});220}221222223function testAllowRelaxExistingAttributePolicyWildcard() {224var input = '<a href="javascript:alert(1)"></a>';225// define a tag-specific one, takes precedence226assertSanitizedHtml(227input, input, null,228[{tagName: 'a', attributeName: 'href', policy: goog.functions.identity}]);229// overwrite the global one230assertSanitizedHtml(231input, input, null,232[{tagName: '*', attributeName: 'href', policy: goog.functions.identity}]);233}234235236function testAllowRelaxExistingAttributePolicySpecific() {237var input = '<a target="foo"></a>';238var expected = '<a></a>';239// overwrite the global one, the specific one still has precedence240assertSanitizedHtml(input, expected, null, [241{tagName: '*', attributeName: 'target', policy: goog.functions.identity}242]);243// overwrite the tag-specific one, this one should take precedence244assertSanitizedHtml(input, input, null, [245{tagName: 'a', attributeName: 'target', policy: goog.functions.identity}246]);247}248249250