github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/bluemonday/sanitize_test.go (about)

     1  // Copyright (c) 2014, David Kitchen <david@buro9.com>
     2  //
     3  // All rights reserved.
     4  //
     5  // Redistribution and use in source and binary forms, with or without
     6  // modification, are permitted provided that the following conditions are met:
     7  //
     8  // * Redistributions of source code must retain the above copyright notice, this
     9  //   list of conditions and the following disclaimer.
    10  //
    11  // * Redistributions in binary form must reproduce the above copyright notice,
    12  //   this list of conditions and the following disclaimer in the documentation
    13  //   and/or other materials provided with the distribution.
    14  //
    15  // * Neither the name of the organisation (Microcosm) nor the names of its
    16  //   contributors may be used to endorse or promote products derived from
    17  //   this software without specific prior written permission.
    18  //
    19  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    20  // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    21  // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    22  // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
    23  // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
    24  // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
    25  // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
    26  // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
    27  // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    28  // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    29  
    30  package bluemonday
    31  
    32  import (
    33  	"encoding/base64"
    34  	"net/url"
    35  	"regexp"
    36  	"strings"
    37  	"sync"
    38  	"testing"
    39  )
    40  
    41  // test is a simple input vs output struct used to construct a slice of many
    42  // tests to run within a single test method.
    43  type test struct {
    44  	in       string
    45  	expected string
    46  }
    47  
    48  func TestEmpty(t *testing.T) {
    49  	p := StrictPolicy()
    50  
    51  	if "" != p.Sanitize(``) {
    52  		t.Error("Empty string is not empty")
    53  	}
    54  }
    55  
    56  func TestSignatureBehaviour(t *testing.T) {
    57  	// https://yougam/libraries/microcosm-cc/bluemonday/issues/8
    58  	p := UGCPolicy()
    59  
    60  	input := "Hi.\n"
    61  
    62  	if output := p.Sanitize(input); output != input {
    63  		t.Errorf(`Sanitize() input = %s, output = %s`, input, output)
    64  	}
    65  
    66  	if output := string(p.SanitizeBytes([]byte(input))); output != input {
    67  		t.Errorf(`SanitizeBytes() input = %s, output = %s`, input, output)
    68  	}
    69  
    70  	if output := p.SanitizeReader(
    71  		strings.NewReader(input),
    72  	).String(); output != input {
    73  
    74  		t.Errorf(`SanitizeReader() input = %s, output = %s`, input, output)
    75  	}
    76  
    77  	input = "\t\n \n\t"
    78  
    79  	if output := p.Sanitize(input); output != input {
    80  		t.Errorf(`Sanitize() input = %s, output = %s`, input, output)
    81  	}
    82  
    83  	if output := string(p.SanitizeBytes([]byte(input))); output != input {
    84  		t.Errorf(`SanitizeBytes() input = %s, output = %s`, input, output)
    85  	}
    86  
    87  	if output := p.SanitizeReader(
    88  		strings.NewReader(input),
    89  	).String(); output != input {
    90  
    91  		t.Errorf(`SanitizeReader() input = %s, output = %s`, input, output)
    92  	}
    93  }
    94  
    95  func TestAllowDocType(t *testing.T) {
    96  	p := NewPolicy()
    97  	p.AllowElements("b")
    98  
    99  	in := "<!DOCTYPE html>Hello, <b>World</b>!"
   100  	expected := "Hello, <b>World</b>!"
   101  
   102  	out := p.Sanitize(in)
   103  	if out != expected {
   104  		t.Errorf(
   105  			"test 1 failed;\ninput   : %s\noutput  : %s\nexpected: %s",
   106  			in,
   107  			out,
   108  			expected,
   109  		)
   110  	}
   111  
   112  	// Allow the doctype and run the test again
   113  	p.AllowDocType(true)
   114  
   115  	expected = "<!DOCTYPE html>Hello, <b>World</b>!"
   116  
   117  	out = p.Sanitize(in)
   118  	if out != expected {
   119  		t.Errorf(
   120  			"test 1 failed;\ninput   : %s\noutput  : %s\nexpected: %s",
   121  			in,
   122  			out,
   123  			expected,
   124  		)
   125  	}
   126  }
   127  
   128  func TestLinks(t *testing.T) {
   129  
   130  	tests := []test{
   131  		test{
   132  			in:       `<a href="http://www.google.com">`,
   133  			expected: `<a href="http://www.google.com" rel="nofollow">`,
   134  		},
   135  		test{
   136  			in:       `<a href="//www.google.com">`,
   137  			expected: `<a href="//www.google.com" rel="nofollow">`,
   138  		},
   139  		test{
   140  			in:       `<a href="/www.google.com">`,
   141  			expected: `<a href="/www.google.com" rel="nofollow">`,
   142  		},
   143  		test{
   144  			in:       `<a href="www.google.com">`,
   145  			expected: `<a href="www.google.com" rel="nofollow">`,
   146  		},
   147  		test{
   148  			in:       `<a href="javascript:alert(1)">`,
   149  			expected: ``,
   150  		},
   151  		test{
   152  			in:       `<a href="#">`,
   153  			expected: ``,
   154  		},
   155  		test{
   156  			in:       `<a href="#top">`,
   157  			expected: `<a href="#top" rel="nofollow">`,
   158  		},
   159  		test{
   160  			in:       `<a href="?">`,
   161  			expected: ``,
   162  		},
   163  		test{
   164  			in:       `<a href="?q=1">`,
   165  			expected: `<a href="?q=1" rel="nofollow">`,
   166  		},
   167  		test{
   168  			in:       `<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==" alt="Red dot" />`,
   169  			expected: `<img alt="Red dot"/>`,
   170  		},
   171  		test{
   172  			in:       `<img src="giraffe.gif" />`,
   173  			expected: `<img src="giraffe.gif"/>`,
   174  		},
   175  	}
   176  
   177  	p := UGCPolicy()
   178  	p.RequireParseableURLs(true)
   179  
   180  	// These tests are run concurrently to enable the race detector to pick up
   181  	// potential issues
   182  	wg := sync.WaitGroup{}
   183  	wg.Add(len(tests))
   184  	for ii, tt := range tests {
   185  		go func(ii int, tt test) {
   186  			out := p.Sanitize(tt.in)
   187  			if out != tt.expected {
   188  				t.Errorf(
   189  					"test %d failed;\ninput   : %s\noutput  : %s\nexpected: %s",
   190  					ii,
   191  					tt.in,
   192  					out,
   193  					tt.expected,
   194  				)
   195  			}
   196  			wg.Done()
   197  		}(ii, tt)
   198  	}
   199  	wg.Wait()
   200  }
   201  
   202  func TestLinkTargets(t *testing.T) {
   203  
   204  	tests := []test{
   205  		test{
   206  			in:       `<a href="http://www.google.com">`,
   207  			expected: `<a href="http://www.google.com" rel="nofollow" target="_blank">`,
   208  		},
   209  		test{
   210  			in:       `<a href="//www.google.com">`,
   211  			expected: `<a href="//www.google.com" rel="nofollow" target="_blank">`,
   212  		},
   213  		test{
   214  			in:       `<a href="/www.google.com">`,
   215  			expected: `<a href="/www.google.com">`,
   216  		},
   217  		test{
   218  			in:       `<a href="www.google.com">`,
   219  			expected: `<a href="www.google.com">`,
   220  		},
   221  		test{
   222  			in:       `<a href="javascript:alert(1)">`,
   223  			expected: ``,
   224  		},
   225  		test{
   226  			in:       `<a href="#">`,
   227  			expected: ``,
   228  		},
   229  		test{
   230  			in:       `<a href="#top">`,
   231  			expected: `<a href="#top">`,
   232  		},
   233  		test{
   234  			in:       `<a href="?">`,
   235  			expected: ``,
   236  		},
   237  		test{
   238  			in:       `<a href="?q=1">`,
   239  			expected: `<a href="?q=1">`,
   240  		},
   241  		test{
   242  			in:       `<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==" alt="Red dot" />`,
   243  			expected: `<img alt="Red dot"/>`,
   244  		},
   245  		test{
   246  			in:       `<img src="giraffe.gif" />`,
   247  			expected: `<img src="giraffe.gif"/>`,
   248  		},
   249  	}
   250  
   251  	p := UGCPolicy()
   252  	p.RequireParseableURLs(true)
   253  	p.RequireNoFollowOnLinks(false)
   254  	p.RequireNoFollowOnFullyQualifiedLinks(true)
   255  	p.AddTargetBlankToFullyQualifiedLinks(true)
   256  
   257  	// These tests are run concurrently to enable the race detector to pick up
   258  	// potential issues
   259  	wg := sync.WaitGroup{}
   260  	wg.Add(len(tests))
   261  	for ii, tt := range tests {
   262  		go func(ii int, tt test) {
   263  			out := p.Sanitize(tt.in)
   264  			if out != tt.expected {
   265  				t.Errorf(
   266  					"test %d failed;\ninput   : %s\noutput  : %s\nexpected: %s",
   267  					ii,
   268  					tt.in,
   269  					out,
   270  					tt.expected,
   271  				)
   272  			}
   273  			wg.Done()
   274  		}(ii, tt)
   275  	}
   276  	wg.Wait()
   277  }
   278  
   279  func TestStyling(t *testing.T) {
   280  
   281  	tests := []test{
   282  		test{
   283  			in:       `<span class="foo">Hello World</span>`,
   284  			expected: `<span class="foo">Hello World</span>`,
   285  		},
   286  		test{
   287  			in:       `<span class="foo bar654">Hello World</span>`,
   288  			expected: `<span class="foo bar654">Hello World</span>`,
   289  		},
   290  	}
   291  
   292  	p := UGCPolicy()
   293  	p.AllowStyling()
   294  
   295  	// These tests are run concurrently to enable the race detector to pick up
   296  	// potential issues
   297  	wg := sync.WaitGroup{}
   298  	wg.Add(len(tests))
   299  	for ii, tt := range tests {
   300  		go func(ii int, tt test) {
   301  			out := p.Sanitize(tt.in)
   302  			if out != tt.expected {
   303  				t.Errorf(
   304  					"test %d failed;\ninput   : %s\noutput  : %s\nexpected: %s",
   305  					ii,
   306  					tt.in,
   307  					out,
   308  					tt.expected,
   309  				)
   310  			}
   311  			wg.Done()
   312  		}(ii, tt)
   313  	}
   314  	wg.Wait()
   315  }
   316  
   317  func TestEmptyAttributes(t *testing.T) {
   318  
   319  	p := UGCPolicy()
   320  	// Do not do this, especially without a Matching() clause, this is a test
   321  	p.AllowAttrs("disabled").OnElements("textarea")
   322  
   323  	tests := []test{
   324  		// Empty elements
   325  		test{
   326  			in: `<textarea>text</textarea><textarea disabled></textarea>` +
   327  				`<div onclick='redirect()'><span>Styled by span</span></div>`,
   328  			expected: `<textarea>text</textarea><textarea disabled=""></textarea>` +
   329  				`<div><span>Styled by span</span></div>`,
   330  		},
   331  		test{
   332  			in:       `foo<br />bar`,
   333  			expected: `foo<br/>bar`,
   334  		},
   335  		test{
   336  			in:       `foo<br/>bar`,
   337  			expected: `foo<br/>bar`,
   338  		},
   339  		test{
   340  			in:       `foo<br>bar`,
   341  			expected: `foo<br>bar`,
   342  		},
   343  		test{
   344  			in:       `foo<hr noshade>bar`,
   345  			expected: `foo<hr>bar`,
   346  		},
   347  	}
   348  
   349  	for ii, test := range tests {
   350  		out := p.Sanitize(test.in)
   351  		if out != test.expected {
   352  			t.Errorf(
   353  				"test %d failed;\ninput   : %s\noutput  : %s\nexpected: %s",
   354  				ii,
   355  				test.in,
   356  				out,
   357  				test.expected,
   358  			)
   359  		}
   360  	}
   361  }
   362  
   363  func TestDataUri(t *testing.T) {
   364  
   365  	p := UGCPolicy()
   366  	p.AllowURLSchemeWithCustomPolicy(
   367  		"data",
   368  		func(url *url.URL) (allowUrl bool) {
   369  			// Allows PNG images only
   370  			const prefix = "image/png;base64,"
   371  			if !strings.HasPrefix(url.Opaque, prefix) {
   372  				return false
   373  			}
   374  			if _, err := base64.StdEncoding.DecodeString(url.Opaque[len(prefix):]); err != nil {
   375  				return false
   376  			}
   377  			if url.RawQuery != "" || url.Fragment != "" {
   378  				return false
   379  			}
   380  			return true
   381  		},
   382  	)
   383  
   384  	tests := []test{
   385  		test{
   386  			in:       `<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==">`,
   387  			expected: `<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==">`,
   388  		},
   389  		test{
   390  			in:       `<img src="data:text/javascript;charset=utf-8,alert('hi');">`,
   391  			expected: ``,
   392  		},
   393  		test{
   394  			in:       `<img src="data:image/png;base64,charset=utf-8,alert('hi');">`,
   395  			expected: ``,
   396  		},
   397  		test{
   398  			in:       `<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4-_8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==">`,
   399  			expected: ``,
   400  		},
   401  	}
   402  
   403  	for ii, test := range tests {
   404  		out := p.Sanitize(test.in)
   405  		if out != test.expected {
   406  			t.Errorf(
   407  				"test %d failed;\ninput   : %s\noutput  : %s\nexpected: %s",
   408  				ii,
   409  				test.in,
   410  				out,
   411  				test.expected,
   412  			)
   413  		}
   414  	}
   415  }
   416  
   417  func TestAntiSamy(t *testing.T) {
   418  
   419  	standardUrls := regexp.MustCompile(`(?i)^https?|mailto`)
   420  
   421  	p := NewPolicy()
   422  
   423  	p.AllowElements(
   424  		"a", "b", "br", "div", "font", "i", "img", "input", "li", "ol", "p",
   425  		"span", "td", "ul",
   426  	)
   427  	p.AllowAttrs("checked", "type").OnElements("input")
   428  	p.AllowAttrs("color").OnElements("font")
   429  	p.AllowAttrs("href").Matching(standardUrls).OnElements("a")
   430  	p.AllowAttrs("src").Matching(standardUrls).OnElements("img")
   431  	p.AllowAttrs("class", "id", "title").Globally()
   432  	p.AllowAttrs("char").Matching(
   433  		regexp.MustCompile(`p{L}`), // Single character or HTML entity only
   434  	).OnElements("td")
   435  
   436  	tests := []test{
   437  		// Base64 strings
   438  		//
   439  		// first string is
   440  		// <a - href="http://www.owasp.org">click here</a>
   441  		test{
   442  			in:       `PGEgLSBocmVmPSJodHRwOi8vd3d3Lm93YXNwLm9yZyI+Y2xpY2sgaGVyZTwvYT4=`,
   443  			expected: `PGEgLSBocmVmPSJodHRwOi8vd3d3Lm93YXNwLm9yZyI+Y2xpY2sgaGVyZTwvYT4=`,
   444  		},
   445  		// the rest are randomly generated 300 byte sequences which generate
   446  		// parser errors, turned into Strings
   447  		test{
   448  			in:       `uz0sEy5aDiok6oufQRaYPyYOxbtlACRnfrOnUVIbOstiaoB95iw+dJYuO5sI9nudhRtSYLANlcdgO0pRb+65qKDwZ5o6GJRMWv4YajZk+7Q3W/GN295XmyWUpxuyPGVi7d5fhmtYaYNW6vxyKK1Wjn9IEhIrfvNNjtEF90vlERnz3wde4WMaKMeciqgDXuZHEApYmUcu6Wbx4Q6WcNDqohAN/qCli74tvC+Umy0ZsQGU7E+BvJJ1tLfMcSzYiz7Q15ByZOYrA2aa0wDu0no3gSatjGt6aB4h30D9xUP31LuPGZ2GdWwMfZbFcfRgDSh42JPwa1bODmt5cw0Y8ACeyrIbfk9IkX1bPpYfIgtO7TwuXjBbhh2EEixOZ2YkcsvmcOSVTvraChbxv6kP`,
   449  			expected: `uz0sEy5aDiok6oufQRaYPyYOxbtlACRnfrOnUVIbOstiaoB95iw+dJYuO5sI9nudhRtSYLANlcdgO0pRb+65qKDwZ5o6GJRMWv4YajZk+7Q3W/GN295XmyWUpxuyPGVi7d5fhmtYaYNW6vxyKK1Wjn9IEhIrfvNNjtEF90vlERnz3wde4WMaKMeciqgDXuZHEApYmUcu6Wbx4Q6WcNDqohAN/qCli74tvC+Umy0ZsQGU7E+BvJJ1tLfMcSzYiz7Q15ByZOYrA2aa0wDu0no3gSatjGt6aB4h30D9xUP31LuPGZ2GdWwMfZbFcfRgDSh42JPwa1bODmt5cw0Y8ACeyrIbfk9IkX1bPpYfIgtO7TwuXjBbhh2EEixOZ2YkcsvmcOSVTvraChbxv6kP`,
   450  		},
   451  		test{
   452  			in:       `PIWjMV4y+MpuNLtcY3vBRG4ZcNaCkB9wXJr3pghmFA6rVXAik+d5lei48TtnHvfvb5rQZVceWKv9cR/9IIsLokMyN0omkd8j3TV0DOh3JyBjPHFCu1Gp4Weo96h5C6RBoB0xsE4QdS2Y1sq/yiha9IebyHThAfnGU8AMC4AvZ7DDBccD2leZy2Q617ekz5grvxEG6tEcZ3fCbJn4leQVVo9MNoerim8KFHGloT+LxdgQR6YN5y1ii3bVGreM51S4TeANujdqJXp8B7B1Gk3PKCRS2T1SNFZedut45y+/w7wp5AUQCBUpIPUj6RLp+y3byWhcbZbJ70KOzTSZuYYIKLLo8047Fej43bIaghJm0F9yIKk3C5gtBcw8T5pciJoVXrTdBAK/8fMVo29P`,
   453  			expected: `PIWjMV4y+MpuNLtcY3vBRG4ZcNaCkB9wXJr3pghmFA6rVXAik+d5lei48TtnHvfvb5rQZVceWKv9cR/9IIsLokMyN0omkd8j3TV0DOh3JyBjPHFCu1Gp4Weo96h5C6RBoB0xsE4QdS2Y1sq/yiha9IebyHThAfnGU8AMC4AvZ7DDBccD2leZy2Q617ekz5grvxEG6tEcZ3fCbJn4leQVVo9MNoerim8KFHGloT+LxdgQR6YN5y1ii3bVGreM51S4TeANujdqJXp8B7B1Gk3PKCRS2T1SNFZedut45y+/w7wp5AUQCBUpIPUj6RLp+y3byWhcbZbJ70KOzTSZuYYIKLLo8047Fej43bIaghJm0F9yIKk3C5gtBcw8T5pciJoVXrTdBAK/8fMVo29P`,
   454  		},
   455  		test{
   456  			in:       `uCk7HocubT6KzJw2eXpSUItZFGkr7U+D89mJw70rxdqXP2JaG04SNjx3dd84G4bz+UVPPhPO2gBAx2vHI0xhgJG9T4vffAYh2D1kenmr+8gIHt6WDNeD+HwJeAbJYhfVFMJsTuIGlYIw8+I+TARK0vqjACyRwMDAndhXnDrk4E5U3hyjqS14XX0kIDZYM6FGFPXe/s+ba2886Q8o1a7WosgqqAmt4u6R3IHOvVf5/PIeZrBJKrVptxjdjelP8Xwjq2ujWNtR3/HM1kjRlJi4xedvMRe4Rlxek0NDLC9hNd18RYi0EjzQ0bGSDDl0813yv6s6tcT6xHMzKvDcUcFRkX6BbxmoIcMsVeHM/ur6yRv834o/TT5IdiM9/wpkuICFOWIfM+Y8OWhiU6BK`,
   457  			expected: `uCk7HocubT6KzJw2eXpSUItZFGkr7U+D89mJw70rxdqXP2JaG04SNjx3dd84G4bz+UVPPhPO2gBAx2vHI0xhgJG9T4vffAYh2D1kenmr+8gIHt6WDNeD+HwJeAbJYhfVFMJsTuIGlYIw8+I+TARK0vqjACyRwMDAndhXnDrk4E5U3hyjqS14XX0kIDZYM6FGFPXe/s+ba2886Q8o1a7WosgqqAmt4u6R3IHOvVf5/PIeZrBJKrVptxjdjelP8Xwjq2ujWNtR3/HM1kjRlJi4xedvMRe4Rlxek0NDLC9hNd18RYi0EjzQ0bGSDDl0813yv6s6tcT6xHMzKvDcUcFRkX6BbxmoIcMsVeHM/ur6yRv834o/TT5IdiM9/wpkuICFOWIfM+Y8OWhiU6BK`,
   458  		},
   459  		test{
   460  			in:       `Bb6Cqy6stJ0YhtPirRAQ8OXrPFKAeYHeuZXuC1qdHJRlweEzl4F2z/ZFG7hzr5NLZtzrRG3wm5TXl6Aua5G6v0WKcjJiS2V43WB8uY1BFK1d2y68c1gTRSF0u+VTThGjz+q/R6zE8HG8uchO+KPw64RehXDbPQ4uadiL+UwfZ4BzY1OHhvM5+2lVlibG+awtH6qzzx6zOWemTih932Lt9mMnm3FzEw7uGzPEYZ3aBV5xnbQ2a2N4UXIdm7RtIUiYFzHcLe5PZM/utJF8NdHKy0SPaKYkdXHli7g3tarzAabLZqLT4k7oemKYCn/eKRreZjqTB2E8Kc9Swf3jHDkmSvzOYE8wi1vQ3X7JtPcQ2O4muvpSa70NIE+XK1CgnnsL79Qzci1/1xgkBlNq`,
   461  			expected: `Bb6Cqy6stJ0YhtPirRAQ8OXrPFKAeYHeuZXuC1qdHJRlweEzl4F2z/ZFG7hzr5NLZtzrRG3wm5TXl6Aua5G6v0WKcjJiS2V43WB8uY1BFK1d2y68c1gTRSF0u+VTThGjz+q/R6zE8HG8uchO+KPw64RehXDbPQ4uadiL+UwfZ4BzY1OHhvM5+2lVlibG+awtH6qzzx6zOWemTih932Lt9mMnm3FzEw7uGzPEYZ3aBV5xnbQ2a2N4UXIdm7RtIUiYFzHcLe5PZM/utJF8NdHKy0SPaKYkdXHli7g3tarzAabLZqLT4k7oemKYCn/eKRreZjqTB2E8Kc9Swf3jHDkmSvzOYE8wi1vQ3X7JtPcQ2O4muvpSa70NIE+XK1CgnnsL79Qzci1/1xgkBlNq`,
   462  		},
   463  		test{
   464  			in:       `FZNVr4nOICD1cNfAvQwZvZWi+P4I2Gubzrt+wK+7gLEY144BosgKeK7snwlA/vJjPAnkFW72APTBjY6kk4EOyoUef0MxRnZEU11vby5Ru19eixZBFB/SVXDJleLK0z3zXXE8U5Zl5RzLActHakG8Psvdt8TDscQc4MPZ1K7mXDhi7FQdpjRTwVxFyCFoybQ9WNJNGPsAkkm84NtFb4KjGpwVC70oq87tM2gYCrNgMhBfdBl0bnQHoNBCp76RKdpq1UAY01t1ipfgt7BoaAr0eTw1S32DezjfkAz04WyPTzkdBKd3b44rX9dXEbm6szAz0SjgztRPDJKSMELjq16W2Ua8d1AHq2Dz8JlsvGzi2jICUjpFsIfRmQ/STSvOT8VsaCFhwL1zDLbn5jCr`,
   465  			expected: `FZNVr4nOICD1cNfAvQwZvZWi+P4I2Gubzrt+wK+7gLEY144BosgKeK7snwlA/vJjPAnkFW72APTBjY6kk4EOyoUef0MxRnZEU11vby5Ru19eixZBFB/SVXDJleLK0z3zXXE8U5Zl5RzLActHakG8Psvdt8TDscQc4MPZ1K7mXDhi7FQdpjRTwVxFyCFoybQ9WNJNGPsAkkm84NtFb4KjGpwVC70oq87tM2gYCrNgMhBfdBl0bnQHoNBCp76RKdpq1UAY01t1ipfgt7BoaAr0eTw1S32DezjfkAz04WyPTzkdBKd3b44rX9dXEbm6szAz0SjgztRPDJKSMELjq16W2Ua8d1AHq2Dz8JlsvGzi2jICUjpFsIfRmQ/STSvOT8VsaCFhwL1zDLbn5jCr`,
   466  		},
   467  		test{
   468  			in:       `RuiRkvYjH2FcCjNzFPT2PJWh7Q6vUbfMadMIEnw49GvzTmhk4OUFyjY13GL52JVyqdyFrnpgEOtXiTu88Cm+TiBI7JRh0jRs3VJRP3N+5GpyjKX7cJA46w8PrH3ovJo3PES7o8CSYKRa3eUs7BnFt7kUCvMqBBqIhTIKlnQd2JkMNnhhCcYdPygLx7E1Vg+H3KybcETsYWBeUVrhRl/RAyYJkn6LddjPuWkDdgIcnKhNvpQu4MMqF3YbzHgyTh7bdWjy1liZle7xR/uRbOrRIRKTxkUinQGEWyW3bbXOvPO71E7xyKywBanwg2FtvzOoRFRVF7V9mLzPSqdvbM7VMQoLFob2UgeNLbVHkWeQtEqQWIV5RMu3+knhoqGYxP/3Srszp0ELRQy/xyyD`,
   469  			expected: `RuiRkvYjH2FcCjNzFPT2PJWh7Q6vUbfMadMIEnw49GvzTmhk4OUFyjY13GL52JVyqdyFrnpgEOtXiTu88Cm+TiBI7JRh0jRs3VJRP3N+5GpyjKX7cJA46w8PrH3ovJo3PES7o8CSYKRa3eUs7BnFt7kUCvMqBBqIhTIKlnQd2JkMNnhhCcYdPygLx7E1Vg+H3KybcETsYWBeUVrhRl/RAyYJkn6LddjPuWkDdgIcnKhNvpQu4MMqF3YbzHgyTh7bdWjy1liZle7xR/uRbOrRIRKTxkUinQGEWyW3bbXOvPO71E7xyKywBanwg2FtvzOoRFRVF7V9mLzPSqdvbM7VMQoLFob2UgeNLbVHkWeQtEqQWIV5RMu3+knhoqGYxP/3Srszp0ELRQy/xyyD`,
   470  		},
   471  		test{
   472  			in:       `mqBEVbNnL929CUA3sjkOmPB5dL0/a0spq8LgbIsJa22SfP580XduzUIKnCtdeC9TjPB/GEPp/LvEUFaLTUgPDQQGu3H5UCZyjVTAMHl45me/0qISEf903zFFqW5Lk3TS6iPrithqMMvhdK29Eg5OhhcoHS+ALpn0EjzUe86NywuFNb6ID4o8aF/ztZlKJegnpDAm3JuhCBauJ+0gcOB8GNdWd5a06qkokmwk1tgwWat7cQGFIH1NOvBwRMKhD51MJ7V28806a3zkOVwwhOiyyTXR+EcDA/aq5acX0yailLWB82g/2GR/DiaqNtusV+gpcMTNYemEv3c/xLkClJc29DSfTsJGKsmIDMqeBMM7RRBNinNAriY9iNX1UuHZLr/tUrRNrfuNT5CvvK1K`,
   473  			expected: `mqBEVbNnL929CUA3sjkOmPB5dL0/a0spq8LgbIsJa22SfP580XduzUIKnCtdeC9TjPB/GEPp/LvEUFaLTUgPDQQGu3H5UCZyjVTAMHl45me/0qISEf903zFFqW5Lk3TS6iPrithqMMvhdK29Eg5OhhcoHS+ALpn0EjzUe86NywuFNb6ID4o8aF/ztZlKJegnpDAm3JuhCBauJ+0gcOB8GNdWd5a06qkokmwk1tgwWat7cQGFIH1NOvBwRMKhD51MJ7V28806a3zkOVwwhOiyyTXR+EcDA/aq5acX0yailLWB82g/2GR/DiaqNtusV+gpcMTNYemEv3c/xLkClJc29DSfTsJGKsmIDMqeBMM7RRBNinNAriY9iNX1UuHZLr/tUrRNrfuNT5CvvK1K`,
   474  		},
   475  		test{
   476  			in:       `IMcfbWZ/iCa/LDcvMlk6LEJ0gDe4ohy2Vi0pVBd9aqR5PnRj8zGit8G2rLuNUkDmQ95bMURasmaPw2Xjf6SQjRk8coIHDLtbg/YNQVMabE8pKd6EaFdsGWJkcFoonxhPR29aH0xvjC4Mp3cJX3mjqyVsOp9xdk6d0Y2hzV3W/oPCq0DV03pm7P3+jH2OzoVVIDYgG1FD12S03otJrCXuzDmE2LOQ0xwgBQ9sREBLXwQzUKfXH8ogZzjdR19pX9qe0rRKMNz8k5lqcF9R2z+XIS1QAfeV9xopXA0CeyrhtoOkXV2i8kBxyodDp7tIeOvbEfvaqZGJgaJyV8UMTDi7zjwNeVdyKa8USH7zrXSoCl+Ud5eflI9vxKS+u9Bt1ufBHJtULOCHGA2vimkU`,
   477  			expected: `IMcfbWZ/iCa/LDcvMlk6LEJ0gDe4ohy2Vi0pVBd9aqR5PnRj8zGit8G2rLuNUkDmQ95bMURasmaPw2Xjf6SQjRk8coIHDLtbg/YNQVMabE8pKd6EaFdsGWJkcFoonxhPR29aH0xvjC4Mp3cJX3mjqyVsOp9xdk6d0Y2hzV3W/oPCq0DV03pm7P3+jH2OzoVVIDYgG1FD12S03otJrCXuzDmE2LOQ0xwgBQ9sREBLXwQzUKfXH8ogZzjdR19pX9qe0rRKMNz8k5lqcF9R2z+XIS1QAfeV9xopXA0CeyrhtoOkXV2i8kBxyodDp7tIeOvbEfvaqZGJgaJyV8UMTDi7zjwNeVdyKa8USH7zrXSoCl+Ud5eflI9vxKS+u9Bt1ufBHJtULOCHGA2vimkU`,
   478  		},
   479  		test{
   480  			in:       `AqC2sr44HVueGzgW13zHvJkqOEBWA8XA66ZEb3EoL1ehypSnJ07cFoWZlO8kf3k57L1fuHFWJ6quEdLXQaT9SJKHlUaYQvanvjbBlqWwaH3hODNsBGoK0DatpoQ+FxcSkdVE/ki3rbEUuJiZzU0BnDxH+Q6FiNsBaJuwau29w24MlD28ELJsjCcUVwtTQkaNtUxIlFKHLj0++T+IVrQH8KZlmVLvDefJ6llWbrFNVuh674HfKr/GEUatG6KI4gWNtGKKRYh76mMl5xH5qDfBZqxyRaKylJaDIYbx5xP5I4DDm4gOnxH+h/Pu6dq6FJ/U3eDio/KQ9xwFqTuyjH0BIRBsvWWgbTNURVBheq+am92YBhkj1QmdKTxQ9fQM55O8DpyWzRhky0NevM9j`,
   481  			expected: `AqC2sr44HVueGzgW13zHvJkqOEBWA8XA66ZEb3EoL1ehypSnJ07cFoWZlO8kf3k57L1fuHFWJ6quEdLXQaT9SJKHlUaYQvanvjbBlqWwaH3hODNsBGoK0DatpoQ+FxcSkdVE/ki3rbEUuJiZzU0BnDxH+Q6FiNsBaJuwau29w24MlD28ELJsjCcUVwtTQkaNtUxIlFKHLj0++T+IVrQH8KZlmVLvDefJ6llWbrFNVuh674HfKr/GEUatG6KI4gWNtGKKRYh76mMl5xH5qDfBZqxyRaKylJaDIYbx5xP5I4DDm4gOnxH+h/Pu6dq6FJ/U3eDio/KQ9xwFqTuyjH0BIRBsvWWgbTNURVBheq+am92YBhkj1QmdKTxQ9fQM55O8DpyWzRhky0NevM9j`,
   482  		},
   483  		test{
   484  			in:       `qkFfS3WfLyj3QTQT9i/s57uOPQCTN1jrab8bwxaxyeYUlz2tEtYyKGGUufua8WzdBT2VvWTvH0JkK0LfUJ+vChvcnMFna+tEaCKCFMIOWMLYVZSJDcYMIqaIr8d0Bi2bpbVf5z4WNma0pbCKaXpkYgeg1Sb8HpKG0p0fAez7Q/QRASlvyM5vuIOH8/CM4fF5Ga6aWkTRG0lfxiyeZ2vi3q7uNmsZF490J79r/6tnPPXIIC4XGnijwho5NmhZG0XcQeyW5KnT7VmGACFdTHOb9oS5WxZZU29/oZ5Y23rBBoSDX/xZ1LNFiZk6Xfl4ih207jzogv+3nOro93JHQydNeKEwxOtbKqEe7WWJLDw/EzVdJTODrhBYKbjUce10XsavuiTvv+H1Qh4lo2Vx`,
   485  			expected: `qkFfS3WfLyj3QTQT9i/s57uOPQCTN1jrab8bwxaxyeYUlz2tEtYyKGGUufua8WzdBT2VvWTvH0JkK0LfUJ+vChvcnMFna+tEaCKCFMIOWMLYVZSJDcYMIqaIr8d0Bi2bpbVf5z4WNma0pbCKaXpkYgeg1Sb8HpKG0p0fAez7Q/QRASlvyM5vuIOH8/CM4fF5Ga6aWkTRG0lfxiyeZ2vi3q7uNmsZF490J79r/6tnPPXIIC4XGnijwho5NmhZG0XcQeyW5KnT7VmGACFdTHOb9oS5WxZZU29/oZ5Y23rBBoSDX/xZ1LNFiZk6Xfl4ih207jzogv+3nOro93JHQydNeKEwxOtbKqEe7WWJLDw/EzVdJTODrhBYKbjUce10XsavuiTvv+H1Qh4lo2Vx`,
   486  		},
   487  		test{
   488  			in:       `O900/Gn82AjyLYqiWZ4ILXBBv/ZaXpTpQL0p9nv7gwF2MWsS2OWEImcVDa+1ElrjUumG6CVEv/rvax53krqJJDg+4Z/XcHxv58w6hNrXiWqFNjxlu5RZHvj1oQQXnS2n8qw8e/c+8ea2TiDIVr4OmgZz1G9uSPBeOZJvySqdgNPMpgfjZwkL2ez9/x31sLuQxi/FW3DFXU6kGSUjaq8g/iGXlaaAcQ0t9Gy+y005Z9wpr2JWWzishL+1JZp9D4SY/r3NHDphN4MNdLHMNBRPSIgfsaSqfLraIt+zWIycsd+nksVxtPv9wcyXy51E1qlHr6Uygz2VZYD9q9zyxEX4wRP2VEewHYUomL9d1F6gGG5fN3z82bQ4hI9uDirWhneWazUOQBRud5otPOm9`,
   489  			expected: `O900/Gn82AjyLYqiWZ4ILXBBv/ZaXpTpQL0p9nv7gwF2MWsS2OWEImcVDa+1ElrjUumG6CVEv/rvax53krqJJDg+4Z/XcHxv58w6hNrXiWqFNjxlu5RZHvj1oQQXnS2n8qw8e/c+8ea2TiDIVr4OmgZz1G9uSPBeOZJvySqdgNPMpgfjZwkL2ez9/x31sLuQxi/FW3DFXU6kGSUjaq8g/iGXlaaAcQ0t9Gy+y005Z9wpr2JWWzishL+1JZp9D4SY/r3NHDphN4MNdLHMNBRPSIgfsaSqfLraIt+zWIycsd+nksVxtPv9wcyXy51E1qlHr6Uygz2VZYD9q9zyxEX4wRP2VEewHYUomL9d1F6gGG5fN3z82bQ4hI9uDirWhneWazUOQBRud5otPOm9`,
   490  		},
   491  		test{
   492  			in:       `C3c+d5Q9lyTafPLdelG1TKaLFinw1TOjyI6KkrQyHKkttfnO58WFvScl1TiRcB/iHxKahskoE2+VRLUIhctuDU4sUvQh/g9Arw0LAA4QTxuLFt01XYdigurz4FT15ox2oDGGGrRb3VGjDTXK1OWVJoLMW95EVqyMc9F+Fdej85LHE+8WesIfacjUQtTG1tzYVQTfubZq0+qxXws8QrxMLFtVE38tbeXo+Ok1/U5TUa6FjWflEfvKY3XVcl8RKkXua7fVz/Blj8Gh+dWe2cOxa0lpM75ZHyz9adQrB2Pb4571E4u2xI5un0R0MFJZBQuPDc1G5rPhyk+Hb4LRG3dS0m8IASQUOskv93z978L1+Abu9CLP6d6s5p+BzWxhMUqwQXC/CCpTywrkJ0RG`,
   493  			expected: `C3c+d5Q9lyTafPLdelG1TKaLFinw1TOjyI6KkrQyHKkttfnO58WFvScl1TiRcB/iHxKahskoE2+VRLUIhctuDU4sUvQh/g9Arw0LAA4QTxuLFt01XYdigurz4FT15ox2oDGGGrRb3VGjDTXK1OWVJoLMW95EVqyMc9F+Fdej85LHE+8WesIfacjUQtTG1tzYVQTfubZq0+qxXws8QrxMLFtVE38tbeXo+Ok1/U5TUa6FjWflEfvKY3XVcl8RKkXua7fVz/Blj8Gh+dWe2cOxa0lpM75ZHyz9adQrB2Pb4571E4u2xI5un0R0MFJZBQuPDc1G5rPhyk+Hb4LRG3dS0m8IASQUOskv93z978L1+Abu9CLP6d6s5p+BzWxhMUqwQXC/CCpTywrkJ0RG`,
   494  		},
   495  		// Basic XSS
   496  		test{
   497  			in:       `test<script>alert(document.cookie)</script>`,
   498  			expected: `test`,
   499  		},
   500  		test{
   501  			in:       `<<<><<script src=http://fake-evil.ru/test.js>`,
   502  			expected: `&lt;&lt;&lt;&gt;&lt;`,
   503  		},
   504  		test{
   505  			in:       `<script<script src=http://fake-evil.ru/test.js>>`,
   506  			expected: `&gt;`,
   507  		},
   508  		test{
   509  			in:       `<SCRIPT/XSS SRC="http://ha.ckers.org/xss.js"></SCRIPT>`,
   510  			expected: ``,
   511  		},
   512  		test{
   513  			in:       "<BODY onload!#$%&()*~+-_.,:;?@[/|\\]^`=alert(\"XSS\")>",
   514  			expected: ``,
   515  		},
   516  		test{
   517  			in:       `<BODY ONLOAD=alert('XSS')>`,
   518  			expected: ``,
   519  		},
   520  		test{
   521  			in:       `<iframe src=http://ha.ckers.org/scriptlet.html <`,
   522  			expected: ``,
   523  		},
   524  		test{
   525  			in:       `<INPUT TYPE="IMAGE" SRC="javascript:alert('XSS');"">`,
   526  			expected: `<input type="IMAGE">`,
   527  		},
   528  		test{
   529  			in:       `<a onblur="alert(secret)" href="http://www.google.com">Google</a>`,
   530  			expected: `<a href="http://www.google.com">Google</a>`,
   531  		},
   532  		// IMG attacks
   533  		test{
   534  			in:       `<img src="http://www.myspace.com/img.gif"/>`,
   535  			expected: `<img src="http://www.myspace.com/img.gif"/>`,
   536  		},
   537  		test{
   538  			in:       `<img src=javascript:alert(document.cookie)>`,
   539  			expected: ``,
   540  		},
   541  		test{
   542  			in:       `<IMG SRC=&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;&#97;&#108;&#101;&#114;&#116;&#40;&#39;&#88;&#83;&#83;&#39;&#41;>`,
   543  			expected: ``,
   544  		},
   545  		test{
   546  			in:       `<IMG SRC='&#0000106&#0000097&#0000118&#0000097&#0000115&#0000099&#0000114&#0000105&#0000112&#0000116&#0000058&#0000097&#0000108&#0000101&#0000114&#0000116&#0000040&#0000039&#0000088&#0000083&#0000083&#0000039&#0000041'>`,
   547  			expected: ``,
   548  		},
   549  		test{
   550  			in:       `<IMG SRC="jav&#x0D;ascript:alert('XSS');">`,
   551  			expected: ``,
   552  		},
   553  		test{
   554  			in:       `<IMG SRC=&#0000106&#0000097&#0000118&#0000097&#0000115&#0000099&#0000114&#0000105&#0000112&#0000116&#0000058&#0000097&#0000108&#0000101&#0000114&#0000116&#0000040&#0000039&#0000088&#0000083&#0000083&#0000039&#0000041>`,
   555  			expected: ``,
   556  		},
   557  		test{
   558  			in:       `<IMG SRC=&#x6A&#x61&#x76&#x61&#x73&#x63&#x72&#x69&#x70&#x74&#x3A&#x61&#x6C&#x65&#x72&#x74&#x28&#x27&#x58&#x53&#x53&#x27&#x29>`,
   559  			expected: ``,
   560  		},
   561  		test{
   562  			in:       `<IMG SRC="javascript:alert('XSS')"`,
   563  			expected: ``,
   564  		},
   565  		test{
   566  			in:       `<IMG LOWSRC="javascript:alert('XSS')">`,
   567  			expected: ``,
   568  		},
   569  		test{
   570  			in:       `<BGSOUND SRC="javascript:alert('XSS');">`,
   571  			expected: ``,
   572  		},
   573  		// HREF attacks
   574  		test{
   575  			in:       `<LINK REL="stylesheet" HREF="javascript:alert('XSS');">`,
   576  			expected: ``,
   577  		},
   578  		test{
   579  			in:       `<LINK REL="stylesheet" HREF="http://ha.ckers.org/xss.css">`,
   580  			expected: ``,
   581  		},
   582  		test{
   583  			in:       `<STYLE>@import'http://ha.ckers.org/xss.css';</STYLE>`,
   584  			expected: ``,
   585  		},
   586  		test{
   587  			in:       `<STYLE>BODY{-moz-binding:url("http://ha.ckers.org/xssmoz.xml#xss")}</STYLE>`,
   588  			expected: ``,
   589  		},
   590  		test{
   591  			in:       `<STYLE>li {list-style-image: url("javascript:alert('XSS')");}</STYLE><UL><LI>XSS`,
   592  			expected: `<ul><li>XSS`,
   593  		},
   594  		test{
   595  			in:       `<IMG SRC='vbscript:msgbox("XSS")'>`,
   596  			expected: ``,
   597  		},
   598  		test{
   599  			in:       `<META HTTP-EQUIV="refresh" CONTENT="0; URL=http://;URL=javascript:alert('XSS');">`,
   600  			expected: ``,
   601  		},
   602  		test{
   603  			in:       `<META HTTP-EQUIV="refresh" CONTENT="0;url=javascript:alert('XSS');">`,
   604  			expected: ``,
   605  		},
   606  		test{
   607  			in:       `<META HTTP-EQUIV="refresh" CONTENT="0;url=data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4K">`,
   608  			expected: ``,
   609  		},
   610  		test{
   611  			in:       `<IFRAME SRC="javascript:alert('XSS');"></IFRAME>`,
   612  			expected: ``,
   613  		},
   614  		test{
   615  			in:       `<FRAMESET><FRAME SRC="javascript:alert('XSS');"></FRAMESET>`,
   616  			expected: ``,
   617  		},
   618  		test{
   619  			in:       `<TABLE BACKGROUND="javascript:alert('XSS')">`,
   620  			expected: ``,
   621  		},
   622  		test{
   623  			in:       `<TABLE><TD BACKGROUND="javascript:alert('XSS')">`,
   624  			expected: `<td>`,
   625  		},
   626  		test{
   627  			in:       `<DIV STYLE="background-image: url(javascript:alert('XSS'))">`,
   628  			expected: `<div>`,
   629  		},
   630  		test{
   631  			in:       `<DIV STYLE="width: expression(alert('XSS'));">`,
   632  			expected: `<div>`,
   633  		},
   634  		test{
   635  			in:       `<IMG STYLE="xss:expr/*XSS*/ession(alert('XSS'))">`,
   636  			expected: ``,
   637  		},
   638  		test{
   639  			in:       `<STYLE>@im\\port'\\ja\\vasc\\ript:alert("XSS")';</STYLE>`,
   640  			expected: ``,
   641  		},
   642  		test{
   643  			in:       `<BASE HREF="javascript:alert('XSS');//">`,
   644  			expected: ``,
   645  		},
   646  		test{
   647  			in:       `<BaSe hReF="http://arbitrary.com/">`,
   648  			expected: ``,
   649  		},
   650  		test{
   651  			in:       `<OBJECT TYPE="text/x-scriptlet" DATA="http://ha.ckers.org/scriptlet.html"></OBJECT>`,
   652  			expected: ``,
   653  		},
   654  		test{
   655  			in:       `<OBJECT classid=clsid:ae24fdae-03c6-11d1-8b76-0080c744f389><param name=url value=javascript:alert('XSS')></OBJECT>`,
   656  			expected: ``,
   657  		},
   658  		test{
   659  			in:       `<EMBED SRC="http://ha.ckers.org/xss.swf" AllowScriptAccess="always"></EMBED>`,
   660  			expected: ``,
   661  		},
   662  		test{
   663  			in:       `<EMBED SRC="data:image/svg+xml;base64,PHN2ZyB4bWxuczpzdmc9Imh0dH A6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcv MjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hs aW5rIiB2ZXJzaW9uPSIxLjAiIHg9IjAiIHk9IjAiIHdpZHRoPSIxOTQiIGhlaWdodD0iMjAw IiBpZD0ieHNzIj48c2NyaXB0IHR5cGU9InRleHQvZWNtYXNjcmlwdCI+YWxlcnQoIlh TUyIpOzwvc2NyaXB0Pjwvc3ZnPg==" type="image/svg+xml" AllowScriptAccess="always"></EMBED>`,
   664  			expected: ``,
   665  		},
   666  		test{
   667  			in:       `<SCRIPT a=">" SRC="http://ha.ckers.org/xss.js"></SCRIPT>`,
   668  			expected: ``,
   669  		},
   670  		test{
   671  			in:       `<SCRIPT a=">" '' SRC="http://ha.ckers.org/xss.js"></SCRIPT>`,
   672  			expected: ``,
   673  		},
   674  		test{
   675  			in:       "<SCRIPT a=`>` SRC=\"http://ha.ckers.org/xss.js\"></SCRIPT>",
   676  			expected: ``,
   677  		},
   678  		test{
   679  			in:       `<SCRIPT a=">'>" SRC="http://ha.ckers.org/xss.js"></SCRIPT>`,
   680  			expected: ``,
   681  		},
   682  		test{
   683  			in:       `<SCRIPT>document.write("<SCRI");</SCRIPT>PT SRC="http://ha.ckers.org/xss.js"></SCRIPT>`,
   684  			expected: `PT SRC=&#34;http://ha.ckers.org/xss.js&#34;&gt;`,
   685  		},
   686  		test{
   687  			in:       `<SCRIPT SRC=http://ha.ckers.org/xss.js`,
   688  			expected: ``,
   689  		},
   690  		test{
   691  			in:       `<div/style=&#92&#45&#92&#109&#111&#92&#122&#92&#45&#98&#92&#105&#92&#110&#100&#92&#105&#110&#92&#103:&#92&#117&#114&#108&#40&#47&#47&#98&#117&#115&#105&#110&#101&#115&#115&#92&#105&#92&#110&#102&#111&#46&#99&#111&#46&#117&#107&#92&#47&#108&#97&#98&#115&#92&#47&#120&#98&#108&#92&#47&#120&#98&#108&#92&#46&#120&#109&#108&#92&#35&#120&#115&#115&#41&>`,
   692  			expected: `<div>`,
   693  		},
   694  		test{
   695  			in:       `<a href='aim: &c:\\windows\\system32\\calc.exe' ini='C:\\Documents and Settings\\All Users\\Start Menu\\Programs\\Startup\\pwnd.bat'>`,
   696  			expected: ``,
   697  		},
   698  		test{
   699  			in:       `<!--\n<A href=\n- --><a href=javascript:alert:document.domain>test-->`,
   700  			expected: `test--&gt;`,
   701  		},
   702  		test{
   703  			in:       `<a></a style="xx:expr/**/ession(document.appendChild(document.createElement('script')).src='http://h4k.in/i.js')">`,
   704  			expected: ``,
   705  		},
   706  		// CSS attacks
   707  		test{
   708  			in:       `<div style="position:absolute">`,
   709  			expected: `<div>`,
   710  		},
   711  		test{
   712  			in:       `<style>b { position:absolute }</style>`,
   713  			expected: ``,
   714  		},
   715  		test{
   716  			in:       `<div style="z-index:25">test</div>`,
   717  			expected: `<div>test</div>`,
   718  		},
   719  		test{
   720  			in:       `<style>z-index:25</style>`,
   721  			expected: ``,
   722  		},
   723  		// Strings that cause issues for tokenizers
   724  		test{
   725  			in:       `<a - href="http://www.test.com">`,
   726  			expected: `<a href="http://www.test.com">`,
   727  		},
   728  		// Comments
   729  		test{
   730  			in:       `text <!-- comment -->`,
   731  			expected: `text `,
   732  		},
   733  		test{
   734  			in:       `<div>text <!-- comment --></div>`,
   735  			expected: `<div>text </div>`,
   736  		},
   737  		test{
   738  			in:       `<div>text <!--[if IE]> comment <[endif]--></div>`,
   739  			expected: `<div>text </div>`,
   740  		},
   741  		test{
   742  			in:       `<div>text <!--[if IE]> <!--[if gte 6]> comment <[endif]--><[endif]--></div>`,
   743  			expected: `<div>text &lt;[endif]--&gt;</div>`,
   744  		},
   745  		test{
   746  			in:       `<div>text <!--[if IE]> <!-- IE specific --> comment <[endif]--></div>`,
   747  			expected: `<div>text  comment &lt;[endif]--&gt;</div>`,
   748  		},
   749  		test{
   750  			in:       `<div>text <!-- [ if lte 6 ]>\ncomment <[ endif\n]--></div>`,
   751  			expected: `<div>text </div>`,
   752  		},
   753  		test{
   754  			in:       `<div>text <![if !IE]> comment <![endif]></div>`,
   755  			expected: `<div>text  comment </div>`,
   756  		},
   757  		test{
   758  			in:       `<div>text <![ if !IE]> comment <![endif]></div>`,
   759  			expected: `<div>text  comment </div>`,
   760  		},
   761  	}
   762  
   763  	// These tests are run concurrently to enable the race detector to pick up
   764  	// potential issues
   765  	wg := sync.WaitGroup{}
   766  	wg.Add(len(tests))
   767  	for ii, tt := range tests {
   768  		go func(ii int, tt test) {
   769  			out := p.Sanitize(tt.in)
   770  			if out != tt.expected {
   771  				t.Errorf(
   772  					"test %d failed;\ninput   : %s\noutput  : %s\nexpected: %s",
   773  					ii,
   774  					tt.in,
   775  					out,
   776  					tt.expected,
   777  				)
   778  			}
   779  			wg.Done()
   780  		}(ii, tt)
   781  	}
   782  	wg.Wait()
   783  }
   784  
   785  func TestXSS(t *testing.T) {
   786  
   787  	p := UGCPolicy()
   788  
   789  	tests := []test{
   790  		test{
   791  			in:       `<A HREF="javascript:document.location='http://www.google.com/'">XSS</A>`,
   792  			expected: `XSS`,
   793  		},
   794  		test{
   795  			in: `<A HREF="h
   796  tt	p://6	6.000146.0x7.147/">XSS</A>`,
   797  			expected: `XSS`,
   798  		},
   799  		test{
   800  			in:       `<SCRIPT>document.write("<SCRI");</SCRIPT>PT SRC="http://ha.ckers.org/xss.js"></SCRIPT>`,
   801  			expected: `PT SRC=&#34;http://ha.ckers.org/xss.js&#34;&gt;`,
   802  		},
   803  		test{
   804  			in:       `<SCRIPT a=">'>" SRC="http://ha.ckers.org/xss.js"></SCRIPT>`,
   805  			expected: ``,
   806  		},
   807  		test{
   808  			in:       "<SCRIPT a=`>` SRC=\"http://ha.ckers.org/xss.js\"></SCRIPT>",
   809  			expected: ``,
   810  		},
   811  		test{
   812  			in:       `<SCRIPT "a='>'" SRC="http://ha.ckers.org/xss.js"></SCRIPT>`,
   813  			expected: ``,
   814  		},
   815  		test{
   816  			in:       `<SCRIPT a=">" '' SRC="http://ha.ckers.org/xss.js"></SCRIPT>`,
   817  			expected: ``,
   818  		},
   819  		test{
   820  			in:       `<SCRIPT =">" SRC="http://ha.ckers.org/xss.js"></SCRIPT>`,
   821  			expected: ``,
   822  		},
   823  		test{
   824  			in:       `<SCRIPT a=">" SRC="http://ha.ckers.org/xss.js"></SCRIPT>`,
   825  			expected: ``,
   826  		},
   827  		test{
   828  			in:       `<HEAD><META HTTP-EQUIV="CONTENT-TYPE" CONTENT="text/html; charset=UTF-7"> </HEAD>+ADw-SCRIPT+AD4-alert('XSS')`,
   829  			expected: ` +ADw-SCRIPT+AD4-alert(&#39;XSS&#39;)`,
   830  		},
   831  		test{
   832  			in:       `<META HTTP-EQUIV="Set-Cookie" Content="USERID=<SCRIPT>alert('XSS')</SCRIPT>">`,
   833  			expected: ``,
   834  		},
   835  		test{
   836  			in: `<? echo('<SCR)';
   837  echo('IPT>alert("XSS")</SCRIPT>'); ?>`,
   838  			expected: `alert(&#34;XSS&#34;)&#39;); ?&gt;`,
   839  		},
   840  		test{
   841  			in:       `<!--#exec cmd="/bin/echo '<SCR'"--><!--#exec cmd="/bin/echo 'IPT SRC=http://ha.ckers.org/xss.js></SCRIPT>'"-->`,
   842  			expected: ``,
   843  		},
   844  		test{
   845  			in: `<HTML><BODY>
   846  <?xml:namespace prefix="t" ns="urn:schemas-microsoft-com:time">
   847  <?import namespace="t" implementation="#default#time2">
   848  <t:set attributeName="innerHTML" to="XSS<SCRIPT DEFER>alert("XSS")</SCRIPT>">
   849  </BODY></HTML>`,
   850  			expected: "\n\n\n&#34;&gt;\n",
   851  		},
   852  		test{
   853  			in: `<XML SRC="xsstest.xml" ID=I></XML>
   854  <SPAN DATASRC=#I DATAFLD=C DATAFORMATAS=HTML></SPAN>`,
   855  			expected: `
   856  <span></span>`,
   857  		},
   858  		test{
   859  			in: `<XML ID="xss"><I><B><IMG SRC="javas<!-- -->cript:alert('XSS')"></B></I></XML>
   860  <SPAN DATASRC="#xss" DATAFLD="B" DATAFORMATAS="HTML"></SPAN>`,
   861  			expected: `<i><b></b></i>
   862  <span></span>`,
   863  		},
   864  		test{
   865  			in:       `<EMBED SRC="data:image/svg+xml;base64,PHN2ZyB4bWxuczpzdmc9Imh0dH A6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcv MjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hs aW5rIiB2ZXJzaW9uPSIxLjAiIHg9IjAiIHk9IjAiIHdpZHRoPSIxOTQiIGhlaWdodD0iMjAw IiBpZD0ieHNzIj48c2NyaXB0IHR5cGU9InRleHQvZWNtYXNjcmlwdCI+YWxlcnQoIlh TUyIpOzwvc2NyaXB0Pjwvc3ZnPg==" type="image/svg+xml" AllowScriptAccess="always"></EMBED>`,
   866  			expected: ``,
   867  		},
   868  		test{
   869  			in:       `<OBJECT TYPE="text/x-scriptlet" DATA="http://ha.ckers.org/scriptlet.html"></OBJECT>`,
   870  			expected: ``,
   871  		},
   872  		test{
   873  			in:       `<BASE HREF="javascript:alert('XSS');//">`,
   874  			expected: ``,
   875  		},
   876  		test{
   877  			in:       `<!--[if gte IE 4]><SCRIPT>alert('XSS');</SCRIPT><![endif]-->`,
   878  			expected: ``,
   879  		},
   880  		test{
   881  			in:       `<DIV STYLE="width: expression(alert('XSS'));">`,
   882  			expected: `<div>`,
   883  		},
   884  		test{
   885  			in:       `<DIV STYLE="background-image: url(&#1;javascript:alert('XSS'))">`,
   886  			expected: `<div>`,
   887  		},
   888  		test{
   889  			in:       `<DIV STYLE="background-image:\0075\0072\006C\0028'\006a\0061\0076\0061\0073\0063\0072\0069\0070\0074\003a\0061\006c\0065\0072\0074\0028.1027\0058.1053\0053\0027\0029'\0029">`,
   890  			expected: `<div>`,
   891  		},
   892  		test{
   893  			in:       `<DIV STYLE="background-image: url(javascript:alert('XSS'))">`,
   894  			expected: `<div>`,
   895  		},
   896  		test{
   897  			in:       `<TABLE><TD BACKGROUND="javascript:alert('XSS')">`,
   898  			expected: `<table><td>`,
   899  		},
   900  		test{
   901  			in:       `<TABLE BACKGROUND="javascript:alert('XSS')">`,
   902  			expected: `<table>`,
   903  		},
   904  		test{
   905  			in:       `<FRAMESET><FRAME SRC="javascript:alert('XSS');"></FRAMESET>`,
   906  			expected: ``,
   907  		},
   908  		test{
   909  			in:       `<IFRAME SRC=# onmouseover="alert(document.cookie)"></IFRAME>`,
   910  			expected: ``,
   911  		},
   912  		test{
   913  			in:       `<IFRAME SRC="javascript:alert('XSS');"></IFRAME>`,
   914  			expected: ``,
   915  		},
   916  		test{
   917  			in:       `<META HTTP-EQUIV="refresh" CONTENT="0; URL=http://;URL=javascript:alert('XSS');">`,
   918  			expected: ``,
   919  		},
   920  		test{
   921  			in:       `<META HTTP-EQUIV="refresh" CONTENT="0;url=data:text/html base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4K">`,
   922  			expected: ``,
   923  		},
   924  		test{
   925  			in:       `<META HTTP-EQUIV="refresh" CONTENT="0;url=javascript:alert('XSS');">`,
   926  			expected: ``,
   927  		},
   928  		test{
   929  			in:       `<XSS STYLE="behavior: url(xss.htc);">`,
   930  			expected: ``,
   931  		},
   932  		test{
   933  			in:       `<XSS STYLE="xss:expression(alert('XSS'))">`,
   934  			expected: ``,
   935  		},
   936  		test{
   937  			in:       `<STYLE type="text/css">BODY{background:url("javascript:alert('XSS')")}</STYLE>`,
   938  			expected: ``,
   939  		},
   940  		test{
   941  			in:       `<STYLE>.XSS{background-image:url("javascript:alert('XSS')");}</STYLE><A CLASS=XSS></A>`,
   942  			expected: ``,
   943  		},
   944  		test{
   945  			in:       `<STYLE TYPE="text/javascript">alert('XSS');</STYLE>`,
   946  			expected: ``,
   947  		},
   948  		test{
   949  			in:       `<IMG STYLE="xss:expr/*XSS*/ession(alert('XSS'))">`,
   950  			expected: ``,
   951  		},
   952  		test{
   953  			in:       `<STYLE>@im\port'\ja\vasc\ript:alert("XSS")';</STYLE>`,
   954  			expected: ``,
   955  		},
   956  		test{
   957  			in:       `<STYLE>BODY{-moz-binding:url("http://ha.ckers.org/xssmoz.xml#xss")}</STYLE>`,
   958  			expected: ``,
   959  		},
   960  		test{
   961  			in:       `<META HTTP-EQUIV="Link" Content="<http://ha.ckers.org/xss.css>; REL=stylesheet">`,
   962  			expected: ``,
   963  		},
   964  		test{
   965  			in:       `<STYLE>@import'http://ha.ckers.org/xss.css';</STYLE>`,
   966  			expected: ``,
   967  		},
   968  		test{
   969  			in:       `<LINK REL="stylesheet" HREF="http://ha.ckers.org/xss.css">`,
   970  			expected: ``,
   971  		},
   972  		test{
   973  			in:       `<LINK REL="stylesheet" HREF="javascript:alert('XSS');">`,
   974  			expected: ``,
   975  		},
   976  		test{
   977  			in:       `<BR SIZE="&{alert('XSS')}">`,
   978  			expected: `<br>`,
   979  		},
   980  		test{
   981  			in:       `<BGSOUND SRC="javascript:alert('XSS');">`,
   982  			expected: ``,
   983  		},
   984  		test{
   985  			in:       `<BODY ONLOAD=alert('XSS')>`,
   986  			expected: ``,
   987  		},
   988  		test{
   989  			in:       `<STYLE>li {list-style-image: url("javascript:alert('XSS')");}</STYLE><UL><LI>XSS</br>`,
   990  			expected: `<ul><li>XSS</br>`,
   991  		},
   992  		test{
   993  			in:       `<IMG LOWSRC="javascript:alert('XSS')">`,
   994  			expected: ``,
   995  		},
   996  		test{
   997  			in:       `<IMG DYNSRC="javascript:alert('XSS')">`,
   998  			expected: ``,
   999  		},
  1000  		test{
  1001  			in:       `<BODY BACKGROUND="javascript:alert('XSS')">`,
  1002  			expected: ``,
  1003  		},
  1004  		test{
  1005  			in:       `<INPUT TYPE="IMAGE" SRC="javascript:alert('XSS');">`,
  1006  			expected: ``,
  1007  		},
  1008  		test{
  1009  			in:       `</TITLE><SCRIPT>alert("XSS");</SCRIPT>`,
  1010  			expected: ``,
  1011  		},
  1012  		test{
  1013  			in:       `\";alert('XSS');//`,
  1014  			expected: `\&#34;;alert(&#39;XSS&#39;);//`,
  1015  		},
  1016  		test{
  1017  			in:       `<iframe src=http://ha.ckers.org/scriptlet.html <`,
  1018  			expected: ``,
  1019  		},
  1020  		test{
  1021  			in:       `<SCRIPT SRC=http://ha.ckers.org/xss.js?< B >`,
  1022  			expected: ``,
  1023  		},
  1024  		test{
  1025  			in:       `<<SCRIPT>alert("XSS");//<</SCRIPT>`,
  1026  			expected: `&lt;`,
  1027  		},
  1028  		test{
  1029  			in:       "<BODY onload!#$%&()*~+-_.,:;?@[/|\\]^`=alert(\"XSS\")>",
  1030  			expected: ``,
  1031  		},
  1032  		test{
  1033  			in:       `<SCRIPT/SRC="http://ha.ckers.org/xss.js"></SCRIPT>`,
  1034  			expected: ``,
  1035  		},
  1036  		test{
  1037  			in:       `<SCRIPT/XSS SRC="http://ha.ckers.org/xss.js"></SCRIPT>`,
  1038  			expected: ``,
  1039  		},
  1040  		test{
  1041  			in:       `<IMG SRC=" &#14;  javascript:alert('XSS');">`,
  1042  			expected: ``,
  1043  		},
  1044  		test{
  1045  			in:       `<IMG SRC="jav&#x0D;ascript:alert('XSS');">`,
  1046  			expected: `<img src="jav%0Dascript:alert%28%27XSS%27%29;">`,
  1047  		},
  1048  		test{
  1049  			in:       `<IMG SRC="jav&#x0A;ascript:alert('XSS');">`,
  1050  			expected: ``,
  1051  		},
  1052  		test{
  1053  			in:       `<IMG SRC="jav&#x09;ascript:alert('XSS');">`,
  1054  			expected: ``,
  1055  		},
  1056  		test{
  1057  			in: `<IMG SRC="jav	ascript:alert('XSS');">`,
  1058  			expected: ``,
  1059  		},
  1060  		test{
  1061  			in:       `<IMG SRC=&#x6A&#x61&#x76&#x61&#x73&#x63&#x72&#x69&#x70&#x74&#x3A&#x61&#x6C&#x65&#x72&#x74&#x28&#x27&#x58&#x53&#x53&#x27&#x29>`,
  1062  			expected: ``,
  1063  		},
  1064  		test{
  1065  			in: `<IMG SRC=&#0000106&#0000097&#0000118&#0000097&#0000115&#0000099&#0000114&#0000105&#0000112&#0000116&#0000058&#0000097&
  1066  #0000108&#0000101&#0000114&#0000116&#0000040&#0000039&#0000088&#0000083&#0000083&#0000039&#0000041>`,
  1067  			expected: ``,
  1068  		},
  1069  		test{
  1070  			in: `<IMG SRC=&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;&#97;&#108;&#101;&#114;&#116;&#40;
  1071  &#39;&#88;&#83;&#83;&#39;&#41;>`,
  1072  			expected: ``,
  1073  		},
  1074  		test{
  1075  			in:       `<IMG SRC=/ onerror="alert(String.fromCharCode(88,83,83))"></img>`,
  1076  			expected: `<img src="/"></img>`,
  1077  		},
  1078  		test{
  1079  			in:       `<IMG onmouseover="alert('xxs')">`,
  1080  			expected: ``,
  1081  		},
  1082  		test{
  1083  			in:       `<IMG SRC= onmouseover="alert('xxs')">`,
  1084  			expected: `<img src="onmouseover=%22alert%28%27xxs%27%29%22">`,
  1085  		},
  1086  		test{
  1087  			in:       `<IMG SRC=# onmouseover="alert('xxs')">`,
  1088  			expected: ``,
  1089  		},
  1090  		test{
  1091  			in:       `<IMG SRC=javascript:alert(String.fromCharCode(88,83,83))>`,
  1092  			expected: ``,
  1093  		},
  1094  		test{
  1095  			in:       `<IMG """><SCRIPT>alert("XSS")</SCRIPT>">`,
  1096  			expected: `&#34;&gt;`,
  1097  		},
  1098  		test{
  1099  			in:       "<IMG SRC=`javascript:alert(\"RSnake says, 'XSS'\")`>",
  1100  			expected: `<img src="%60javascript:alert%28%22RSnake">`,
  1101  		},
  1102  		test{
  1103  			in:       `<IMG SRC=javascript:alert("XSS")>`,
  1104  			expected: ``,
  1105  		},
  1106  		test{
  1107  			in:       `<IMG SRC=JaVaScRiPt:alert('XSS')>`,
  1108  			expected: ``,
  1109  		},
  1110  		test{
  1111  			in:       `<IMG SRC=javascript:alert('XSS')>`,
  1112  			expected: ``,
  1113  		},
  1114  		test{
  1115  			in:       `<IMG SRC="javascript:alert('XSS');">`,
  1116  			expected: ``,
  1117  		},
  1118  		test{
  1119  			in:       `<SCRIPT SRC=http://ha.ckers.org/xss.js></SCRIPT>`,
  1120  			expected: ``,
  1121  		},
  1122  		test{
  1123  			in:       `'';!--"<XSS>=&{()}`,
  1124  			expected: `&#39;&#39;;!--&#34;=&amp;{()}`,
  1125  		},
  1126  		test{
  1127  			in:       `';alert(String.fromCharCode(88,83,83))//';alert(String.fromCharCode(88,83,83))//";alert(String.fromCharCode(88,83,83))//";alert(String.fromCharCode(88,83,83))//--></SCRIPT>">'><SCRIPT>alert(String.fromCharCode(88,83,83))</SCRIPT>`,
  1128  			expected: `&#39;;alert(String.fromCharCode(88,83,83))//&#39;;alert(String.fromCharCode(88,83,83))//&#34;;alert(String.fromCharCode(88,83,83))//&#34;;alert(String.fromCharCode(88,83,83))//--&gt;&#34;&gt;&#39;&gt;`,
  1129  		},
  1130  	}
  1131  
  1132  	// These tests are run concurrently to enable the race detector to pick up
  1133  	// potential issues
  1134  	wg := sync.WaitGroup{}
  1135  	wg.Add(len(tests))
  1136  	for ii, tt := range tests {
  1137  		go func(ii int, tt test) {
  1138  			out := p.Sanitize(tt.in)
  1139  			if out != tt.expected {
  1140  				t.Errorf(
  1141  					"test %d failed;\ninput   : %s\noutput  : %s\nexpected: %s",
  1142  					ii,
  1143  					tt.in,
  1144  					out,
  1145  					tt.expected,
  1146  				)
  1147  			}
  1148  			wg.Done()
  1149  		}(ii, tt)
  1150  	}
  1151  	wg.Wait()
  1152  }
  1153  
  1154  func TestIssue3(t *testing.T) {
  1155  	// https://yougam/libraries/microcosm-cc/bluemonday/issues/3
  1156  
  1157  	p := UGCPolicy()
  1158  	p.AllowStyling()
  1159  
  1160  	tests := []test{
  1161  		test{
  1162  			in:       `Hello <span class="foo bar bash">there</span> world.`,
  1163  			expected: `Hello <span class="foo bar bash">there</span> world.`,
  1164  		},
  1165  		test{
  1166  			in:       `Hello <span class="javascript:alert(123)">there</span> world.`,
  1167  			expected: `Hello <span>there</span> world.`,
  1168  		},
  1169  		test{
  1170  			in:       `Hello <span class="><script src="http://hackers.org/XSS.js"></script>">there</span> world.`,
  1171  			expected: `Hello <span>&#34;&gt;there</span> world.`,
  1172  		},
  1173  		test{
  1174  			in:       `Hello <span class="><script src='http://hackers.org/XSS.js'></script>">there</span> world.`,
  1175  			expected: `Hello <span>there</span> world.`,
  1176  		},
  1177  	}
  1178  
  1179  	wg := sync.WaitGroup{}
  1180  	wg.Add(len(tests))
  1181  	for ii, tt := range tests {
  1182  		go func(ii int, tt test) {
  1183  			out := p.Sanitize(tt.in)
  1184  			if out != tt.expected {
  1185  				t.Errorf(
  1186  					"test %d failed;\ninput   : %s\noutput  : %s\nexpected: %s",
  1187  					ii,
  1188  					tt.in,
  1189  					out,
  1190  					tt.expected,
  1191  				)
  1192  			}
  1193  			wg.Done()
  1194  		}(ii, tt)
  1195  	}
  1196  	wg.Wait()
  1197  }
  1198  
  1199  func TestIssue9(t *testing.T) {
  1200  
  1201  	p := UGCPolicy()
  1202  	p.AllowAttrs("class").Matching(SpaceSeparatedTokens).OnElements("div", "span")
  1203  	p.AllowAttrs("class", "name").Matching(SpaceSeparatedTokens).OnElements("a")
  1204  	p.AllowAttrs("rel").Matching(regexp.MustCompile(`^nofollow$`)).OnElements("a")
  1205  	p.AllowAttrs("aria-hidden").Matching(regexp.MustCompile(`^true$`)).OnElements("a")
  1206  	p.AllowDataURIImages()
  1207  
  1208  	tt := test{
  1209  		in:       `<h2><a name="git-diff" class="anchor" href="#git-diff" rel="nofollow" aria-hidden="true"><span class="octicon octicon-link"></span></a>git diff</h2>`,
  1210  		expected: `<h2><a name="git-diff" class="anchor" href="#git-diff" rel="nofollow" aria-hidden="true"><span class="octicon octicon-link"></span></a>git diff</h2>`,
  1211  	}
  1212  	out := p.Sanitize(tt.in)
  1213  	if out != tt.expected {
  1214  		t.Errorf(
  1215  			"test failed;\ninput   : %s\noutput  : %s\nexpected: %s",
  1216  			tt.in,
  1217  			out,
  1218  			tt.expected,
  1219  		)
  1220  	}
  1221  
  1222  	tt = test{
  1223  		in:       `<h2><a name="git-diff" class="anchor" href="#git-diff" aria-hidden="true"><span class="octicon octicon-link"></span></a>git diff</h2>`,
  1224  		expected: `<h2><a name="git-diff" class="anchor" href="#git-diff" aria-hidden="true" rel="nofollow"><span class="octicon octicon-link"></span></a>git diff</h2>`,
  1225  	}
  1226  	out = p.Sanitize(tt.in)
  1227  	if out != tt.expected {
  1228  		t.Errorf(
  1229  			"test failed;\ninput   : %s\noutput  : %s\nexpected: %s",
  1230  			tt.in,
  1231  			out,
  1232  			tt.expected,
  1233  		)
  1234  	}
  1235  
  1236  	p.AddTargetBlankToFullyQualifiedLinks(true)
  1237  
  1238  	tt = test{
  1239  		in:       `<h2><a name="git-diff" class="anchor" href="#git-diff" aria-hidden="true"><span class="octicon octicon-link"></span></a>git diff</h2>`,
  1240  		expected: `<h2><a name="git-diff" class="anchor" href="#git-diff" aria-hidden="true" rel="nofollow"><span class="octicon octicon-link"></span></a>git diff</h2>`,
  1241  	}
  1242  	out = p.Sanitize(tt.in)
  1243  	if out != tt.expected {
  1244  		t.Errorf(
  1245  			"test failed;\ninput   : %s\noutput  : %s\nexpected: %s",
  1246  			tt.in,
  1247  			out,
  1248  			tt.expected,
  1249  		)
  1250  	}
  1251  
  1252  	tt = test{
  1253  		in:       `<h2><a name="git-diff" class="anchor" href="https://yougam/libraries/shurcooL/github_flavored_markdown/blob/master/sanitize_test.go" aria-hidden="true"><span class="octicon octicon-link"></span></a>git diff</h2>`,
  1254  		expected: `<h2><a name="git-diff" class="anchor" href="https://yougam/libraries/shurcooL/github_flavored_markdown/blob/master/sanitize_test.go" aria-hidden="true" rel="nofollow" target="_blank"><span class="octicon octicon-link"></span></a>git diff</h2>`,
  1255  	}
  1256  	out = p.Sanitize(tt.in)
  1257  	if out != tt.expected {
  1258  		t.Errorf(
  1259  			"test failed;\ninput   : %s\noutput  : %s\nexpected: %s",
  1260  			tt.in,
  1261  			out,
  1262  			tt.expected,
  1263  		)
  1264  	}
  1265  
  1266  	tt = test{
  1267  		in:       `<h2><a name="git-diff" class="anchor" href="https://yougam/libraries/shurcooL/github_flavored_markdown/blob/master/sanitize_test.go" aria-hidden="true" target="namedwindow"><span class="octicon octicon-link"></span></a>git diff</h2>`,
  1268  		expected: `<h2><a name="git-diff" class="anchor" href="https://yougam/libraries/shurcooL/github_flavored_markdown/blob/master/sanitize_test.go" aria-hidden="true" rel="nofollow" target="_blank"><span class="octicon octicon-link"></span></a>git diff</h2>`,
  1269  	}
  1270  	out = p.Sanitize(tt.in)
  1271  	if out != tt.expected {
  1272  		t.Errorf(
  1273  			"test failed;\ninput   : %s\noutput  : %s\nexpected: %s",
  1274  			tt.in,
  1275  			out,
  1276  			tt.expected,
  1277  		)
  1278  	}
  1279  }
  1280  
  1281  func TestIssue18(t *testing.T) {
  1282  	p := UGCPolicy()
  1283  
  1284  	p.AllowAttrs("color").OnElements("font")
  1285  	p.AllowElements("font")
  1286  
  1287  	tt := test{
  1288  		in:       `<font face="Arial">No link here. <a href="http://link.com">link here</a>.</font> Should not be linked here.`,
  1289  		expected: `No link here. <a href="http://link.com" rel="nofollow">link here</a>. Should not be linked here.`,
  1290  	}
  1291  	out := p.Sanitize(tt.in)
  1292  	if out != tt.expected {
  1293  		t.Errorf(
  1294  			"test failed;\ninput   : %s\noutput  : %s\nexpected: %s",
  1295  			tt.in,
  1296  			out,
  1297  			tt.expected)
  1298  	}
  1299  }
  1300  
  1301  func TestIssue23(t *testing.T) {
  1302  	p := NewPolicy()
  1303  	p.SkipElementsContent("tag1", "tag2")
  1304  	input := `<tag1>cut<tag2></tag2>harm</tag1><tag1>123</tag1><tag2>234</tag2>`
  1305  	out := p.Sanitize(input)
  1306  	expected := ""
  1307  	if out != expected {
  1308  		t.Errorf(
  1309  			"test failed;\ninput   : %s\noutput  : %s\nexpected: %s",
  1310  			input,
  1311  			out,
  1312  			expected)
  1313  	}
  1314  
  1315  	p = NewPolicy()
  1316  	p.SkipElementsContent("tag")
  1317  	p.AllowElements("p")
  1318  	input = `<tag>234<p>asd</p></tag>`
  1319  	out = p.Sanitize(input)
  1320  	expected = ""
  1321  	if out != expected {
  1322  		t.Errorf(
  1323  			"test failed;\ninput   : %s\noutput  : %s\nexpected: %s",
  1324  			input,
  1325  			out,
  1326  			expected)
  1327  	}
  1328  
  1329  	p = NewPolicy()
  1330  	p.SkipElementsContent("tag")
  1331  	p.AllowElements("p", "br")
  1332  	input = `<tag>234<p>as<br/>d</p></tag>`
  1333  	out = p.Sanitize(input)
  1334  	expected = ""
  1335  	if out != expected {
  1336  		t.Errorf(
  1337  			"test failed;\ninput   : %s\noutput  : %s\nexpected: %s",
  1338  			input,
  1339  			out,
  1340  			expected)
  1341  	}
  1342  }
  1343  
  1344  func TestAllowNoAttrs(t *testing.T) {
  1345  	input := "<tag>test</tag>"
  1346  	outputFail := "test"
  1347  	outputOk := input
  1348  
  1349  	p := NewPolicy()
  1350  	p.AllowElements("tag")
  1351  
  1352  	if output := p.Sanitize(input); output != outputFail {
  1353  		t.Errorf(
  1354  			"test failed;\ninput   : %s\noutput  : %s\nexpected: %s",
  1355  			input,
  1356  			output,
  1357  			outputFail,
  1358  		)
  1359  	}
  1360  
  1361  	p.AllowNoAttrs().OnElements("tag")
  1362  
  1363  	if output := p.Sanitize(input); output != outputOk {
  1364  		t.Errorf(
  1365  			"test failed;\ninput   : %s\noutput  : %s\nexpected: %s",
  1366  			input,
  1367  			output,
  1368  			outputOk,
  1369  		)
  1370  	}
  1371  }
  1372  
  1373  func TestSkipElementsContent(t *testing.T) {
  1374  	input := "<tag>test</tag>"
  1375  	outputFail := "test"
  1376  	outputOk := ""
  1377  
  1378  	p := NewPolicy()
  1379  
  1380  	if output := p.Sanitize(input); output != outputFail {
  1381  		t.Errorf(
  1382  			"test failed;\ninput   : %s\noutput  : %s\nexpected: %s",
  1383  			input,
  1384  			output,
  1385  			outputFail,
  1386  		)
  1387  	}
  1388  
  1389  	p.SkipElementsContent("tag")
  1390  
  1391  	if output := p.Sanitize(input); output != outputOk {
  1392  		t.Errorf(
  1393  			"test failed;\ninput   : %s\noutput  : %s\nexpected: %s",
  1394  			input,
  1395  			output,
  1396  			outputOk,
  1397  		)
  1398  	}
  1399  }
  1400  
  1401  func TestTagSkipClosingTagNested(t *testing.T) {
  1402  	input := "<tag1><tag2><tag3>text</tag3></tag2></tag1>"
  1403  	outputOk := "<tag2>text</tag2>"
  1404  
  1405  	p := NewPolicy()
  1406  	p.AllowElements("tag1", "tag3")
  1407  	p.AllowNoAttrs().OnElements("tag2")
  1408  
  1409  	if output := p.Sanitize(input); output != outputOk {
  1410  		t.Errorf(
  1411  			"test failed;\ninput   : %s\noutput  : %s\nexpected: %s",
  1412  			input,
  1413  			output,
  1414  			outputOk,
  1415  		)
  1416  	}
  1417  }