All Downloads are FREE. Search and download functionalities are using the official Maven repository.

goog.labs.html.scrubber_test.js Maven / Gradle / Ivy

Go to download

The Google Closure Library is a collection of JavaScript code designed for use with the Google Closure JavaScript Compiler. This non-official distribution was prepared by the ClojureScript team at http://clojure.org/

There is a newer version: 0.0-20230227-c7c0a541
Show newest version
// Copyright 2013 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.


goog.provide('goog.html.ScrubberTest');

goog.require('goog.labs.html.scrubber');
goog.require('goog.object');
goog.require('goog.string');
goog.require('goog.testing.jsunit');

goog.setTestOnly('goog.html.ScrubberTest');



var tagWhitelist = goog.object.createSet(
    'a', 'b', 'i', 'p', 'font', 'hr', 'br', 'span', 'ol', 'ul', 'li', 'table',
    'tr', 'td', 'th', 'tbody', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'img',
    'html', 'head', 'body', 'title');

var attrWhitelist = {
  // On any element,
  '*': {
    // allow unfiltered title attributes, and
    'title': function(title) { return title; },
    // specific dir values.
    'dir': function(dir) { return dir === 'ltr' || dir === 'rtl' ? dir : null; }
  },
  // Specifically on  elements,
  'a': {
    // allow an href but verify and rewrite, and
    'href': function(href) {
      return /^https?:\/\/google\.[a-z]{2,3}\/search\?/.test(href) ?
          href.replace(
              /[^A-Za-z0-9_\-.~:\/?#\[\]@!\$&()*+,;=%]+/, encodeURIComponent) :
          null;
    },
    // mask the generic title handler for no good reason.
    'title': function(title) { return '<' + title + '>'; }
  }
};


function run(input, golden, desc) {
  var actual =
      goog.labs.html.scrubber.scrub(tagWhitelist, attrWhitelist, input);
  assertEquals(desc, golden, actual);
}


function testEmptyString() {
  run('', '', 'Empty string');
}

function testHelloWorld() {
  run('Hello, World!', 'Hello, World!', 'Hello World');
}

function testNoEndTag() {
  run('Hello, World!', 'Hello, World!',
      'Hello World no end tag');
}

function testUnclosedTags() {
  run('Hello, <<World>>!' +
          '

Hello,
<>!', 'Hello, <<World>>!' + '

Hello,
<>!

', 'RCDATA content, different case, unclosed tags'); } function testListInList() { run('
  • foo
    • bar
', '
  • foo
    • bar
', 'list in list directly'); } function testHeaders() { run('

header

body' + '

sub-header

sub-body' + '

sub-sub-header
sub-sub-body

', '

header

body' + '

sub-header

sub-body' + '

sub-sub-header


sub-sub-body', 'headers'); } function testListNesting() { run('
    • foo
      • bar', '
          • foo
            • bar
        ', 'list nesting'); } function testTableNesting() { run('
        foo
        bar
        ', '
        foo' + '
        bar
        ' + '
        ', 'table nesting'); } function testNestingLimit() { run(goog.string.repeat('', 264) + goog.string.repeat('', 264), goog.string.repeat('', 256) + goog.string.repeat('', 256), '264 open spans'); } function testTableScopes() { run('

        Hi

        How are you

        \n' + '

        ' + '' + '' + '

        Cell

        \n

        Cell

        \n

        \n' + '

        x

        ', '

        Hi

        How are you

        \n' + '

        ' + '' + // The close

        tag does not close the whole table. + '' + '

        Cell

        \n

        Cell

        \n

        \n' + '

        x

        ', 'Table Scopes'); } function testConcatSafe() { run('<script>alert(1337)</script>', '<script>alert(1337)</script>', 'Concat safe'); } function testPrototypeMembersDoNotInfectTables() { // Constructor is all lower-case so will survive tag name // normalization. run('Foo', 'Foo', 'Object.prototype members'); } function testGenericAttributesAllowed() { run('', '', 'generic attrs allowed'); } function testValueWhitelisting() { run('LTREvil', 'LTREvil', 'value whitelisted'); } function testAttributeNormalization() { run('Click', 'Click', 'URL normalized'); } function testTagSpecificityOfAttributeFiltering() { run('', '', 'href blocked on img'); } function testTagSpecificAttributeFiltering() { run('Unclicky', 'Unclicky', 'bad href value blocked'); } function testNonWhitelistFunctionsNotCalled() { var called = false; Object.prototype.dontcallme = function() { called = true; return 'dontcallme was called despite being on the prototype'; }; try { run('Lorem Ipsum', 'Lorem Ipsum', 'non white-list fn not called'); } finally { delete Object.prototype.dontcallme; } assertFalse( 'Object.prototype.dontcallme should not have been called', called); } function testQuotesInAttributeValue() { run('Lorem Ipsum', 'Lorem Ipsum', 'quotes in attr value'); } function testAttributesNeverMentionedAreDropped() { run('evil', 'evil', 'attrs white-listed'); } function testAttributesNotOverEscaped() { run('/', '/', 'attr value not over-escaped'); } function testTagSpecificRulesTakePrecedence() { run('Link', 'Link', 'tag specific rules take precedence'); } function testAttributeRejectionLocalized() { run('Link', 'Link', 'failure of one attribute does not torpedo others'); } function testWeirdHtmlRulesFollowedForAttrValues() { run('Lorem Ipsum', 'Lorem Ipsum', 'same as browser on weird values'); } function testAttributesDisallowedOnCloseTags() { run('

        Header

        ', '

        Header

        ', 'attributes on close tags'); } function testRoundTrippingOfHtmlSafeAgainstIEBacktickProblems() { // Introducing a space at the end of an attribute forces IE to quote it when // turning a DOM into innerHTML which protects against a bunch of problems // with backticks since IE treats them as attribute value delimiters, allowing // foo.innerHTML += ... // to continue to "work" without introducing an XSS vector. // Adding a space at the end is innocuous since HTML attributes whose values // are structured content ignore spaces at the beginning or end. run('*', '*', 'not round-trippable on IE'); }