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 30package bluemonday 31 32import ( 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. 43type test struct { 44 in string 45 expected string 46} 47 48func TestEmpty(t *testing.T) { 49 p := StrictPolicy() 50 51 if "" != p.Sanitize(``) { 52 t.Error("Empty string is not empty") 53 } 54} 55 56func TestSignatureBehaviour(t *testing.T) { 57 // https://github.com/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 95func TestLinks(t *testing.T) { 96 97 tests := []test{ 98 { 99 in: `<a href="http://www.google.com">`, 100 expected: `<a href="http://www.google.com" rel="nofollow">`, 101 }, 102 { 103 in: `<a href="//www.google.com">`, 104 expected: `<a href="//www.google.com" rel="nofollow">`, 105 }, 106 { 107 in: `<a href="/www.google.com">`, 108 expected: `<a href="/www.google.com" rel="nofollow">`, 109 }, 110 { 111 in: `<a href="www.google.com">`, 112 expected: `<a href="www.google.com" rel="nofollow">`, 113 }, 114 { 115 in: `<a href="javascript:alert(1)">`, 116 expected: ``, 117 }, 118 { 119 in: `<a href="#">`, 120 expected: ``, 121 }, 122 { 123 in: `<a href="#top">`, 124 expected: `<a href="#top" rel="nofollow">`, 125 }, 126 { 127 in: `<a href="?q=1">`, 128 expected: `<a href="?q=1" rel="nofollow">`, 129 }, 130 { 131 in: `<a href="?q=1&r=2">`, 132 expected: `<a href="?q=1&r=2" rel="nofollow">`, 133 }, 134 { 135 in: `<a href="?q=1&q=2">`, 136 expected: `<a href="?q=1&q=2" rel="nofollow">`, 137 }, 138 { 139 in: `<a href="?q=%7B%22value%22%3A%22a%22%7D">`, 140 expected: `<a href="?q=%7B%22value%22%3A%22a%22%7D" rel="nofollow">`, 141 }, 142 { 143 in: `<a href="?q=1&r=2&s=:foo@">`, 144 expected: `<a href="?q=1&r=2&s=:foo@" rel="nofollow">`, 145 }, 146 { 147 in: `<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==" alt="Red dot" />`, 148 expected: `<img alt="Red dot"/>`, 149 }, 150 { 151 in: `<img src="giraffe.gif" />`, 152 expected: `<img src="giraffe.gif"/>`, 153 }, 154 { 155 in: `<img src="giraffe.gif?height=500&width=500&flag" />`, 156 expected: `<img src="giraffe.gif?height=500&width=500&flag"/>`, 157 }, 158 } 159 160 p := UGCPolicy() 161 p.RequireParseableURLs(true) 162 163 // These tests are run concurrently to enable the race detector to pick up 164 // potential issues 165 wg := sync.WaitGroup{} 166 wg.Add(len(tests)) 167 for ii, tt := range tests { 168 go func(ii int, tt test) { 169 out := p.Sanitize(tt.in) 170 if out != tt.expected { 171 t.Errorf( 172 "test %d failed;\ninput : %s\noutput : %s\nexpected: %s", 173 ii, 174 tt.in, 175 out, 176 tt.expected, 177 ) 178 } 179 wg.Done() 180 }(ii, tt) 181 } 182 wg.Wait() 183} 184 185func TestLinkTargets(t *testing.T) { 186 187 tests := []test{ 188 { 189 in: `<a href="http://www.google.com">`, 190 expected: `<a href="http://www.google.com" rel="nofollow noopener" target="_blank">`, 191 }, 192 { 193 in: `<a href="//www.google.com">`, 194 expected: `<a href="//www.google.com" rel="nofollow noopener" target="_blank">`, 195 }, 196 { 197 in: `<a href="/www.google.com">`, 198 expected: `<a href="/www.google.com">`, 199 }, 200 { 201 in: `<a href="www.google.com">`, 202 expected: `<a href="www.google.com">`, 203 }, 204 { 205 in: `<a href="javascript:alert(1)">`, 206 expected: ``, 207 }, 208 { 209 in: `<a href="#">`, 210 expected: ``, 211 }, 212 { 213 in: `<a href="#top">`, 214 expected: `<a href="#top">`, 215 }, 216 { 217 in: `<a href="?q=1">`, 218 expected: `<a href="?q=1">`, 219 }, 220 { 221 in: `<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==" alt="Red dot" />`, 222 expected: `<img alt="Red dot"/>`, 223 }, 224 { 225 in: `<img src="giraffe.gif" />`, 226 expected: `<img src="giraffe.gif"/>`, 227 }, 228 } 229 230 p := UGCPolicy() 231 p.RequireParseableURLs(true) 232 p.RequireNoFollowOnLinks(false) 233 p.RequireNoFollowOnFullyQualifiedLinks(true) 234 p.AddTargetBlankToFullyQualifiedLinks(true) 235 236 // These tests are run concurrently to enable the race detector to pick up 237 // potential issues 238 wg := sync.WaitGroup{} 239 wg.Add(len(tests)) 240 for ii, tt := range tests { 241 go func(ii int, tt test) { 242 out := p.Sanitize(tt.in) 243 if out != tt.expected { 244 t.Errorf( 245 "test %d failed;\ninput : %s\noutput : %s\nexpected: %s", 246 ii, 247 tt.in, 248 out, 249 tt.expected, 250 ) 251 } 252 wg.Done() 253 }(ii, tt) 254 } 255 wg.Wait() 256} 257 258func TestStyling(t *testing.T) { 259 260 tests := []test{ 261 { 262 in: `<span class="foo">Hello World</span>`, 263 expected: `<span class="foo">Hello World</span>`, 264 }, 265 { 266 in: `<span class="foo bar654">Hello World</span>`, 267 expected: `<span class="foo bar654">Hello World</span>`, 268 }, 269 } 270 271 p := UGCPolicy() 272 p.AllowStyling() 273 274 // These tests are run concurrently to enable the race detector to pick up 275 // potential issues 276 wg := sync.WaitGroup{} 277 wg.Add(len(tests)) 278 for ii, tt := range tests { 279 go func(ii int, tt test) { 280 out := p.Sanitize(tt.in) 281 if out != tt.expected { 282 t.Errorf( 283 "test %d failed;\ninput : %s\noutput : %s\nexpected: %s", 284 ii, 285 tt.in, 286 out, 287 tt.expected, 288 ) 289 } 290 wg.Done() 291 }(ii, tt) 292 } 293 wg.Wait() 294} 295 296func TestEmptyAttributes(t *testing.T) { 297 298 p := UGCPolicy() 299 // Do not do this, especially without a Matching() clause, this is a test 300 p.AllowAttrs("disabled").OnElements("textarea") 301 302 tests := []test{ 303 // Empty elements 304 { 305 in: `<textarea>text</textarea><textarea disabled></textarea>` + 306 `<div onclick='redirect()'><span>Styled by span</span></div>`, 307 expected: `<textarea>text</textarea><textarea disabled=""></textarea>` + 308 `<div><span>Styled by span</span></div>`, 309 }, 310 { 311 in: `foo<br />bar`, 312 expected: `foo<br/>bar`, 313 }, 314 { 315 in: `foo<br/>bar`, 316 expected: `foo<br/>bar`, 317 }, 318 { 319 in: `foo<br>bar`, 320 expected: `foo<br>bar`, 321 }, 322 { 323 in: `foo<hr noshade>bar`, 324 expected: `foo<hr>bar`, 325 }, 326 } 327 328 for ii, test := range tests { 329 out := p.Sanitize(test.in) 330 if out != test.expected { 331 t.Errorf( 332 "test %d failed;\ninput : %s\noutput : %s\nexpected: %s", 333 ii, 334 test.in, 335 out, 336 test.expected, 337 ) 338 } 339 } 340} 341 342func TestDataAttributes(t *testing.T) { 343 344 p := UGCPolicy() 345 p.AllowDataAttributes() 346 347 tests := []test{ 348 { 349 in: `<p data-cfg="dave">text</p>`, 350 expected: `<p data-cfg="dave">text</p>`, 351 }, 352 { 353 in: `<p data-component="dave">text</p>`, 354 expected: `<p data-component="dave">text</p>`, 355 }, 356 { 357 in: `<p data-semicolon;="dave">text</p>`, 358 expected: `<p>text</p>`, 359 }, 360 { 361 in: `<p data-xml-prefix="dave">text</p>`, 362 expected: `<p>text</p>`, 363 }, 364 } 365 366 for ii, test := range tests { 367 out := p.Sanitize(test.in) 368 if out != test.expected { 369 t.Errorf( 370 "test %d failed;\ninput : %s\noutput : %s\nexpected: %s", 371 ii, 372 test.in, 373 out, 374 test.expected, 375 ) 376 } 377 } 378} 379 380func TestDataUri(t *testing.T) { 381 382 p := UGCPolicy() 383 p.AllowURLSchemeWithCustomPolicy( 384 "data", 385 func(url *url.URL) (allowUrl bool) { 386 // Allows PNG images only 387 const prefix = "image/png;base64," 388 if !strings.HasPrefix(url.Opaque, prefix) { 389 return false 390 } 391 if _, err := base64.StdEncoding.DecodeString(url.Opaque[len(prefix):]); err != nil { 392 return false 393 } 394 if url.RawQuery != "" || url.Fragment != "" { 395 return false 396 } 397 return true 398 }, 399 ) 400 401 tests := []test{ 402 { 403 in: `<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==">`, 404 expected: `<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==">`, 405 }, 406 { 407 in: `<img src="data:text/javascript;charset=utf-8,alert('hi');">`, 408 expected: ``, 409 }, 410 { 411 in: `<img src="data:image/png;base64,charset=utf-8,alert('hi');">`, 412 expected: ``, 413 }, 414 { 415 in: `<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4-_8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==">`, 416 expected: ``, 417 }, 418 } 419 420 for ii, test := range tests { 421 out := p.Sanitize(test.in) 422 if out != test.expected { 423 t.Errorf( 424 "test %d failed;\ninput : %s\noutput : %s\nexpected: %s", 425 ii, 426 test.in, 427 out, 428 test.expected, 429 ) 430 } 431 } 432} 433 434func TestGlobalURLPatternsViaCustomPolicy(t *testing.T) { 435 436 p := UGCPolicy() 437 // youtube embeds 438 p.AllowElements("iframe") 439 p.AllowAttrs("width", "height", "frameborder").Matching(Integer).OnElements("iframe") 440 p.AllowAttrs("allow").Matching(regexp.MustCompile(`^(([\p{L}\p{N}_-]+)(; )?)+$`)).OnElements("iframe") 441 p.AllowAttrs("allowfullscreen").OnElements("iframe") 442 p.AllowAttrs("src").OnElements("iframe") 443 // These clobber... so you only get one and it applies to URLs everywhere 444 p.AllowURLSchemeWithCustomPolicy("mailto", func(url *url.URL) (allowUrl bool) { return false }) 445 p.AllowURLSchemeWithCustomPolicy("http", func(url *url.URL) (allowUrl bool) { return false }) 446 p.AllowURLSchemeWithCustomPolicy( 447 "https", 448 func(url *url.URL) bool { 449 // Allow YouTube 450 if url.Host == `www.youtube.com` { 451 return true 452 } 453 return false 454 }, 455 ) 456 457 tests := []test{ 458 { 459 in: `<iframe width="560" height="315" src="https://www.youtube.com/embed/lJIrF4YjHfQ" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>`, 460 expected: `<iframe width="560" height="315" src="https://www.youtube.com/embed/lJIrF4YjHfQ" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>`, 461 }, 462 { 463 in: `<iframe width="560" height="315" src="htt://www.vimeo.com/embed/lJIrF4YjHfQ" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>`, 464 expected: `<iframe width="560" height="315" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>`, 465 }, 466 } 467 468 for ii, test := range tests { 469 out := p.Sanitize(test.in) 470 if out != test.expected { 471 t.Errorf( 472 "test %d failed;\ninput : %s\noutput : %s\nexpected: %s", 473 ii, 474 test.in, 475 out, 476 test.expected, 477 ) 478 } 479 } 480} 481 482func TestELementURLPatternsMatching(t *testing.T) { 483 484 p := UGCPolicy() 485 // youtube embeds 486 p.AllowElements("iframe") 487 p.AllowAttrs("width", "height", "frameborder").Matching(Integer).OnElements("iframe") 488 p.AllowAttrs("allow").Matching(regexp.MustCompile(`^(([\p{L}\p{N}_-]+)(; )?)+$`)).OnElements("iframe") 489 p.AllowAttrs("allowfullscreen").OnElements("iframe") 490 p.AllowAttrs("src").Matching(regexp.MustCompile(`^https://www.youtube.com/.*$`)).OnElements("iframe") 491 492 tests := []test{ 493 { 494 in: `<iframe width="560" height="315" src="https://www.youtube.com/embed/lJIrF4YjHfQ" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>`, 495 expected: `<iframe width="560" height="315" src="https://www.youtube.com/embed/lJIrF4YjHfQ" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>`, 496 }, 497 { 498 in: `<iframe width="560" height="315" src="htt://www.vimeo.com/embed/lJIrF4YjHfQ" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>`, 499 expected: `<iframe width="560" height="315" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>`, 500 }, 501 } 502 503 for ii, test := range tests { 504 out := p.Sanitize(test.in) 505 if out != test.expected { 506 t.Errorf( 507 "test %d failed;\ninput : %s\noutput : %s\nexpected: %s", 508 ii, 509 test.in, 510 out, 511 test.expected, 512 ) 513 } 514 } 515} 516 517func TestAntiSamy(t *testing.T) { 518 519 standardUrls := regexp.MustCompile(`(?i)^https?|mailto`) 520 521 p := NewPolicy() 522 523 p.AllowElements( 524 "a", "b", "br", "div", "font", "i", "img", "input", "li", "ol", "p", 525 "span", "td", "ul", 526 ) 527 p.AllowAttrs("checked", "type").OnElements("input") 528 p.AllowAttrs("color").OnElements("font") 529 p.AllowAttrs("href").Matching(standardUrls).OnElements("a") 530 p.AllowAttrs("src").Matching(standardUrls).OnElements("img") 531 p.AllowAttrs("class", "id", "title").Globally() 532 p.AllowAttrs("char").Matching( 533 regexp.MustCompile(`p{L}`), // Single character or HTML entity only 534 ).OnElements("td") 535 536 tests := []test{ 537 // Base64 strings 538 // 539 // first string is 540 // <a - href="http://www.owasp.org">click here</a> 541 { 542 in: `PGEgLSBocmVmPSJodHRwOi8vd3d3Lm93YXNwLm9yZyI+Y2xpY2sgaGVyZTwvYT4=`, 543 expected: `PGEgLSBocmVmPSJodHRwOi8vd3d3Lm93YXNwLm9yZyI+Y2xpY2sgaGVyZTwvYT4=`, 544 }, 545 // the rest are randomly generated 300 byte sequences which generate 546 // parser errors, turned into Strings 547 { 548 in: `uz0sEy5aDiok6oufQRaYPyYOxbtlACRnfrOnUVIbOstiaoB95iw+dJYuO5sI9nudhRtSYLANlcdgO0pRb+65qKDwZ5o6GJRMWv4YajZk+7Q3W/GN295XmyWUpxuyPGVi7d5fhmtYaYNW6vxyKK1Wjn9IEhIrfvNNjtEF90vlERnz3wde4WMaKMeciqgDXuZHEApYmUcu6Wbx4Q6WcNDqohAN/qCli74tvC+Umy0ZsQGU7E+BvJJ1tLfMcSzYiz7Q15ByZOYrA2aa0wDu0no3gSatjGt6aB4h30D9xUP31LuPGZ2GdWwMfZbFcfRgDSh42JPwa1bODmt5cw0Y8ACeyrIbfk9IkX1bPpYfIgtO7TwuXjBbhh2EEixOZ2YkcsvmcOSVTvraChbxv6kP`, 549 expected: `uz0sEy5aDiok6oufQRaYPyYOxbtlACRnfrOnUVIbOstiaoB95iw+dJYuO5sI9nudhRtSYLANlcdgO0pRb+65qKDwZ5o6GJRMWv4YajZk+7Q3W/GN295XmyWUpxuyPGVi7d5fhmtYaYNW6vxyKK1Wjn9IEhIrfvNNjtEF90vlERnz3wde4WMaKMeciqgDXuZHEApYmUcu6Wbx4Q6WcNDqohAN/qCli74tvC+Umy0ZsQGU7E+BvJJ1tLfMcSzYiz7Q15ByZOYrA2aa0wDu0no3gSatjGt6aB4h30D9xUP31LuPGZ2GdWwMfZbFcfRgDSh42JPwa1bODmt5cw0Y8ACeyrIbfk9IkX1bPpYfIgtO7TwuXjBbhh2EEixOZ2YkcsvmcOSVTvraChbxv6kP`, 550 }, 551 { 552 in: `PIWjMV4y+MpuNLtcY3vBRG4ZcNaCkB9wXJr3pghmFA6rVXAik+d5lei48TtnHvfvb5rQZVceWKv9cR/9IIsLokMyN0omkd8j3TV0DOh3JyBjPHFCu1Gp4Weo96h5C6RBoB0xsE4QdS2Y1sq/yiha9IebyHThAfnGU8AMC4AvZ7DDBccD2leZy2Q617ekz5grvxEG6tEcZ3fCbJn4leQVVo9MNoerim8KFHGloT+LxdgQR6YN5y1ii3bVGreM51S4TeANujdqJXp8B7B1Gk3PKCRS2T1SNFZedut45y+/w7wp5AUQCBUpIPUj6RLp+y3byWhcbZbJ70KOzTSZuYYIKLLo8047Fej43bIaghJm0F9yIKk3C5gtBcw8T5pciJoVXrTdBAK/8fMVo29P`, 553 expected: `PIWjMV4y+MpuNLtcY3vBRG4ZcNaCkB9wXJr3pghmFA6rVXAik+d5lei48TtnHvfvb5rQZVceWKv9cR/9IIsLokMyN0omkd8j3TV0DOh3JyBjPHFCu1Gp4Weo96h5C6RBoB0xsE4QdS2Y1sq/yiha9IebyHThAfnGU8AMC4AvZ7DDBccD2leZy2Q617ekz5grvxEG6tEcZ3fCbJn4leQVVo9MNoerim8KFHGloT+LxdgQR6YN5y1ii3bVGreM51S4TeANujdqJXp8B7B1Gk3PKCRS2T1SNFZedut45y+/w7wp5AUQCBUpIPUj6RLp+y3byWhcbZbJ70KOzTSZuYYIKLLo8047Fej43bIaghJm0F9yIKk3C5gtBcw8T5pciJoVXrTdBAK/8fMVo29P`, 554 }, 555 { 556 in: `uCk7HocubT6KzJw2eXpSUItZFGkr7U+D89mJw70rxdqXP2JaG04SNjx3dd84G4bz+UVPPhPO2gBAx2vHI0xhgJG9T4vffAYh2D1kenmr+8gIHt6WDNeD+HwJeAbJYhfVFMJsTuIGlYIw8+I+TARK0vqjACyRwMDAndhXnDrk4E5U3hyjqS14XX0kIDZYM6FGFPXe/s+ba2886Q8o1a7WosgqqAmt4u6R3IHOvVf5/PIeZrBJKrVptxjdjelP8Xwjq2ujWNtR3/HM1kjRlJi4xedvMRe4Rlxek0NDLC9hNd18RYi0EjzQ0bGSDDl0813yv6s6tcT6xHMzKvDcUcFRkX6BbxmoIcMsVeHM/ur6yRv834o/TT5IdiM9/wpkuICFOWIfM+Y8OWhiU6BK`, 557 expected: `uCk7HocubT6KzJw2eXpSUItZFGkr7U+D89mJw70rxdqXP2JaG04SNjx3dd84G4bz+UVPPhPO2gBAx2vHI0xhgJG9T4vffAYh2D1kenmr+8gIHt6WDNeD+HwJeAbJYhfVFMJsTuIGlYIw8+I+TARK0vqjACyRwMDAndhXnDrk4E5U3hyjqS14XX0kIDZYM6FGFPXe/s+ba2886Q8o1a7WosgqqAmt4u6R3IHOvVf5/PIeZrBJKrVptxjdjelP8Xwjq2ujWNtR3/HM1kjRlJi4xedvMRe4Rlxek0NDLC9hNd18RYi0EjzQ0bGSDDl0813yv6s6tcT6xHMzKvDcUcFRkX6BbxmoIcMsVeHM/ur6yRv834o/TT5IdiM9/wpkuICFOWIfM+Y8OWhiU6BK`, 558 }, 559 { 560 in: `Bb6Cqy6stJ0YhtPirRAQ8OXrPFKAeYHeuZXuC1qdHJRlweEzl4F2z/ZFG7hzr5NLZtzrRG3wm5TXl6Aua5G6v0WKcjJiS2V43WB8uY1BFK1d2y68c1gTRSF0u+VTThGjz+q/R6zE8HG8uchO+KPw64RehXDbPQ4uadiL+UwfZ4BzY1OHhvM5+2lVlibG+awtH6qzzx6zOWemTih932Lt9mMnm3FzEw7uGzPEYZ3aBV5xnbQ2a2N4UXIdm7RtIUiYFzHcLe5PZM/utJF8NdHKy0SPaKYkdXHli7g3tarzAabLZqLT4k7oemKYCn/eKRreZjqTB2E8Kc9Swf3jHDkmSvzOYE8wi1vQ3X7JtPcQ2O4muvpSa70NIE+XK1CgnnsL79Qzci1/1xgkBlNq`, 561 expected: `Bb6Cqy6stJ0YhtPirRAQ8OXrPFKAeYHeuZXuC1qdHJRlweEzl4F2z/ZFG7hzr5NLZtzrRG3wm5TXl6Aua5G6v0WKcjJiS2V43WB8uY1BFK1d2y68c1gTRSF0u+VTThGjz+q/R6zE8HG8uchO+KPw64RehXDbPQ4uadiL+UwfZ4BzY1OHhvM5+2lVlibG+awtH6qzzx6zOWemTih932Lt9mMnm3FzEw7uGzPEYZ3aBV5xnbQ2a2N4UXIdm7RtIUiYFzHcLe5PZM/utJF8NdHKy0SPaKYkdXHli7g3tarzAabLZqLT4k7oemKYCn/eKRreZjqTB2E8Kc9Swf3jHDkmSvzOYE8wi1vQ3X7JtPcQ2O4muvpSa70NIE+XK1CgnnsL79Qzci1/1xgkBlNq`, 562 }, 563 { 564 in: `FZNVr4nOICD1cNfAvQwZvZWi+P4I2Gubzrt+wK+7gLEY144BosgKeK7snwlA/vJjPAnkFW72APTBjY6kk4EOyoUef0MxRnZEU11vby5Ru19eixZBFB/SVXDJleLK0z3zXXE8U5Zl5RzLActHakG8Psvdt8TDscQc4MPZ1K7mXDhi7FQdpjRTwVxFyCFoybQ9WNJNGPsAkkm84NtFb4KjGpwVC70oq87tM2gYCrNgMhBfdBl0bnQHoNBCp76RKdpq1UAY01t1ipfgt7BoaAr0eTw1S32DezjfkAz04WyPTzkdBKd3b44rX9dXEbm6szAz0SjgztRPDJKSMELjq16W2Ua8d1AHq2Dz8JlsvGzi2jICUjpFsIfRmQ/STSvOT8VsaCFhwL1zDLbn5jCr`, 565 expected: `FZNVr4nOICD1cNfAvQwZvZWi+P4I2Gubzrt+wK+7gLEY144BosgKeK7snwlA/vJjPAnkFW72APTBjY6kk4EOyoUef0MxRnZEU11vby5Ru19eixZBFB/SVXDJleLK0z3zXXE8U5Zl5RzLActHakG8Psvdt8TDscQc4MPZ1K7mXDhi7FQdpjRTwVxFyCFoybQ9WNJNGPsAkkm84NtFb4KjGpwVC70oq87tM2gYCrNgMhBfdBl0bnQHoNBCp76RKdpq1UAY01t1ipfgt7BoaAr0eTw1S32DezjfkAz04WyPTzkdBKd3b44rX9dXEbm6szAz0SjgztRPDJKSMELjq16W2Ua8d1AHq2Dz8JlsvGzi2jICUjpFsIfRmQ/STSvOT8VsaCFhwL1zDLbn5jCr`, 566 }, 567 { 568 in: `RuiRkvYjH2FcCjNzFPT2PJWh7Q6vUbfMadMIEnw49GvzTmhk4OUFyjY13GL52JVyqdyFrnpgEOtXiTu88Cm+TiBI7JRh0jRs3VJRP3N+5GpyjKX7cJA46w8PrH3ovJo3PES7o8CSYKRa3eUs7BnFt7kUCvMqBBqIhTIKlnQd2JkMNnhhCcYdPygLx7E1Vg+H3KybcETsYWBeUVrhRl/RAyYJkn6LddjPuWkDdgIcnKhNvpQu4MMqF3YbzHgyTh7bdWjy1liZle7xR/uRbOrRIRKTxkUinQGEWyW3bbXOvPO71E7xyKywBanwg2FtvzOoRFRVF7V9mLzPSqdvbM7VMQoLFob2UgeNLbVHkWeQtEqQWIV5RMu3+knhoqGYxP/3Srszp0ELRQy/xyyD`, 569 expected: `RuiRkvYjH2FcCjNzFPT2PJWh7Q6vUbfMadMIEnw49GvzTmhk4OUFyjY13GL52JVyqdyFrnpgEOtXiTu88Cm+TiBI7JRh0jRs3VJRP3N+5GpyjKX7cJA46w8PrH3ovJo3PES7o8CSYKRa3eUs7BnFt7kUCvMqBBqIhTIKlnQd2JkMNnhhCcYdPygLx7E1Vg+H3KybcETsYWBeUVrhRl/RAyYJkn6LddjPuWkDdgIcnKhNvpQu4MMqF3YbzHgyTh7bdWjy1liZle7xR/uRbOrRIRKTxkUinQGEWyW3bbXOvPO71E7xyKywBanwg2FtvzOoRFRVF7V9mLzPSqdvbM7VMQoLFob2UgeNLbVHkWeQtEqQWIV5RMu3+knhoqGYxP/3Srszp0ELRQy/xyyD`, 570 }, 571 { 572 in: `mqBEVbNnL929CUA3sjkOmPB5dL0/a0spq8LgbIsJa22SfP580XduzUIKnCtdeC9TjPB/GEPp/LvEUFaLTUgPDQQGu3H5UCZyjVTAMHl45me/0qISEf903zFFqW5Lk3TS6iPrithqMMvhdK29Eg5OhhcoHS+ALpn0EjzUe86NywuFNb6ID4o8aF/ztZlKJegnpDAm3JuhCBauJ+0gcOB8GNdWd5a06qkokmwk1tgwWat7cQGFIH1NOvBwRMKhD51MJ7V28806a3zkOVwwhOiyyTXR+EcDA/aq5acX0yailLWB82g/2GR/DiaqNtusV+gpcMTNYemEv3c/xLkClJc29DSfTsJGKsmIDMqeBMM7RRBNinNAriY9iNX1UuHZLr/tUrRNrfuNT5CvvK1K`, 573 expected: `mqBEVbNnL929CUA3sjkOmPB5dL0/a0spq8LgbIsJa22SfP580XduzUIKnCtdeC9TjPB/GEPp/LvEUFaLTUgPDQQGu3H5UCZyjVTAMHl45me/0qISEf903zFFqW5Lk3TS6iPrithqMMvhdK29Eg5OhhcoHS+ALpn0EjzUe86NywuFNb6ID4o8aF/ztZlKJegnpDAm3JuhCBauJ+0gcOB8GNdWd5a06qkokmwk1tgwWat7cQGFIH1NOvBwRMKhD51MJ7V28806a3zkOVwwhOiyyTXR+EcDA/aq5acX0yailLWB82g/2GR/DiaqNtusV+gpcMTNYemEv3c/xLkClJc29DSfTsJGKsmIDMqeBMM7RRBNinNAriY9iNX1UuHZLr/tUrRNrfuNT5CvvK1K`, 574 }, 575 { 576 in: `IMcfbWZ/iCa/LDcvMlk6LEJ0gDe4ohy2Vi0pVBd9aqR5PnRj8zGit8G2rLuNUkDmQ95bMURasmaPw2Xjf6SQjRk8coIHDLtbg/YNQVMabE8pKd6EaFdsGWJkcFoonxhPR29aH0xvjC4Mp3cJX3mjqyVsOp9xdk6d0Y2hzV3W/oPCq0DV03pm7P3+jH2OzoVVIDYgG1FD12S03otJrCXuzDmE2LOQ0xwgBQ9sREBLXwQzUKfXH8ogZzjdR19pX9qe0rRKMNz8k5lqcF9R2z+XIS1QAfeV9xopXA0CeyrhtoOkXV2i8kBxyodDp7tIeOvbEfvaqZGJgaJyV8UMTDi7zjwNeVdyKa8USH7zrXSoCl+Ud5eflI9vxKS+u9Bt1ufBHJtULOCHGA2vimkU`, 577 expected: `IMcfbWZ/iCa/LDcvMlk6LEJ0gDe4ohy2Vi0pVBd9aqR5PnRj8zGit8G2rLuNUkDmQ95bMURasmaPw2Xjf6SQjRk8coIHDLtbg/YNQVMabE8pKd6EaFdsGWJkcFoonxhPR29aH0xvjC4Mp3cJX3mjqyVsOp9xdk6d0Y2hzV3W/oPCq0DV03pm7P3+jH2OzoVVIDYgG1FD12S03otJrCXuzDmE2LOQ0xwgBQ9sREBLXwQzUKfXH8ogZzjdR19pX9qe0rRKMNz8k5lqcF9R2z+XIS1QAfeV9xopXA0CeyrhtoOkXV2i8kBxyodDp7tIeOvbEfvaqZGJgaJyV8UMTDi7zjwNeVdyKa8USH7zrXSoCl+Ud5eflI9vxKS+u9Bt1ufBHJtULOCHGA2vimkU`, 578 }, 579 { 580 in: `AqC2sr44HVueGzgW13zHvJkqOEBWA8XA66ZEb3EoL1ehypSnJ07cFoWZlO8kf3k57L1fuHFWJ6quEdLXQaT9SJKHlUaYQvanvjbBlqWwaH3hODNsBGoK0DatpoQ+FxcSkdVE/ki3rbEUuJiZzU0BnDxH+Q6FiNsBaJuwau29w24MlD28ELJsjCcUVwtTQkaNtUxIlFKHLj0++T+IVrQH8KZlmVLvDefJ6llWbrFNVuh674HfKr/GEUatG6KI4gWNtGKKRYh76mMl5xH5qDfBZqxyRaKylJaDIYbx5xP5I4DDm4gOnxH+h/Pu6dq6FJ/U3eDio/KQ9xwFqTuyjH0BIRBsvWWgbTNURVBheq+am92YBhkj1QmdKTxQ9fQM55O8DpyWzRhky0NevM9j`, 581 expected: `AqC2sr44HVueGzgW13zHvJkqOEBWA8XA66ZEb3EoL1ehypSnJ07cFoWZlO8kf3k57L1fuHFWJ6quEdLXQaT9SJKHlUaYQvanvjbBlqWwaH3hODNsBGoK0DatpoQ+FxcSkdVE/ki3rbEUuJiZzU0BnDxH+Q6FiNsBaJuwau29w24MlD28ELJsjCcUVwtTQkaNtUxIlFKHLj0++T+IVrQH8KZlmVLvDefJ6llWbrFNVuh674HfKr/GEUatG6KI4gWNtGKKRYh76mMl5xH5qDfBZqxyRaKylJaDIYbx5xP5I4DDm4gOnxH+h/Pu6dq6FJ/U3eDio/KQ9xwFqTuyjH0BIRBsvWWgbTNURVBheq+am92YBhkj1QmdKTxQ9fQM55O8DpyWzRhky0NevM9j`, 582 }, 583 { 584 in: `qkFfS3WfLyj3QTQT9i/s57uOPQCTN1jrab8bwxaxyeYUlz2tEtYyKGGUufua8WzdBT2VvWTvH0JkK0LfUJ+vChvcnMFna+tEaCKCFMIOWMLYVZSJDcYMIqaIr8d0Bi2bpbVf5z4WNma0pbCKaXpkYgeg1Sb8HpKG0p0fAez7Q/QRASlvyM5vuIOH8/CM4fF5Ga6aWkTRG0lfxiyeZ2vi3q7uNmsZF490J79r/6tnPPXIIC4XGnijwho5NmhZG0XcQeyW5KnT7VmGACFdTHOb9oS5WxZZU29/oZ5Y23rBBoSDX/xZ1LNFiZk6Xfl4ih207jzogv+3nOro93JHQydNeKEwxOtbKqEe7WWJLDw/EzVdJTODrhBYKbjUce10XsavuiTvv+H1Qh4lo2Vx`, 585 expected: `qkFfS3WfLyj3QTQT9i/s57uOPQCTN1jrab8bwxaxyeYUlz2tEtYyKGGUufua8WzdBT2VvWTvH0JkK0LfUJ+vChvcnMFna+tEaCKCFMIOWMLYVZSJDcYMIqaIr8d0Bi2bpbVf5z4WNma0pbCKaXpkYgeg1Sb8HpKG0p0fAez7Q/QRASlvyM5vuIOH8/CM4fF5Ga6aWkTRG0lfxiyeZ2vi3q7uNmsZF490J79r/6tnPPXIIC4XGnijwho5NmhZG0XcQeyW5KnT7VmGACFdTHOb9oS5WxZZU29/oZ5Y23rBBoSDX/xZ1LNFiZk6Xfl4ih207jzogv+3nOro93JHQydNeKEwxOtbKqEe7WWJLDw/EzVdJTODrhBYKbjUce10XsavuiTvv+H1Qh4lo2Vx`, 586 }, 587 { 588 in: `O900/Gn82AjyLYqiWZ4ILXBBv/ZaXpTpQL0p9nv7gwF2MWsS2OWEImcVDa+1ElrjUumG6CVEv/rvax53krqJJDg+4Z/XcHxv58w6hNrXiWqFNjxlu5RZHvj1oQQXnS2n8qw8e/c+8ea2TiDIVr4OmgZz1G9uSPBeOZJvySqdgNPMpgfjZwkL2ez9/x31sLuQxi/FW3DFXU6kGSUjaq8g/iGXlaaAcQ0t9Gy+y005Z9wpr2JWWzishL+1JZp9D4SY/r3NHDphN4MNdLHMNBRPSIgfsaSqfLraIt+zWIycsd+nksVxtPv9wcyXy51E1qlHr6Uygz2VZYD9q9zyxEX4wRP2VEewHYUomL9d1F6gGG5fN3z82bQ4hI9uDirWhneWazUOQBRud5otPOm9`, 589 expected: `O900/Gn82AjyLYqiWZ4ILXBBv/ZaXpTpQL0p9nv7gwF2MWsS2OWEImcVDa+1ElrjUumG6CVEv/rvax53krqJJDg+4Z/XcHxv58w6hNrXiWqFNjxlu5RZHvj1oQQXnS2n8qw8e/c+8ea2TiDIVr4OmgZz1G9uSPBeOZJvySqdgNPMpgfjZwkL2ez9/x31sLuQxi/FW3DFXU6kGSUjaq8g/iGXlaaAcQ0t9Gy+y005Z9wpr2JWWzishL+1JZp9D4SY/r3NHDphN4MNdLHMNBRPSIgfsaSqfLraIt+zWIycsd+nksVxtPv9wcyXy51E1qlHr6Uygz2VZYD9q9zyxEX4wRP2VEewHYUomL9d1F6gGG5fN3z82bQ4hI9uDirWhneWazUOQBRud5otPOm9`, 590 }, 591 { 592 in: `C3c+d5Q9lyTafPLdelG1TKaLFinw1TOjyI6KkrQyHKkttfnO58WFvScl1TiRcB/iHxKahskoE2+VRLUIhctuDU4sUvQh/g9Arw0LAA4QTxuLFt01XYdigurz4FT15ox2oDGGGrRb3VGjDTXK1OWVJoLMW95EVqyMc9F+Fdej85LHE+8WesIfacjUQtTG1tzYVQTfubZq0+qxXws8QrxMLFtVE38tbeXo+Ok1/U5TUa6FjWflEfvKY3XVcl8RKkXua7fVz/Blj8Gh+dWe2cOxa0lpM75ZHyz9adQrB2Pb4571E4u2xI5un0R0MFJZBQuPDc1G5rPhyk+Hb4LRG3dS0m8IASQUOskv93z978L1+Abu9CLP6d6s5p+BzWxhMUqwQXC/CCpTywrkJ0RG`, 593 expected: `C3c+d5Q9lyTafPLdelG1TKaLFinw1TOjyI6KkrQyHKkttfnO58WFvScl1TiRcB/iHxKahskoE2+VRLUIhctuDU4sUvQh/g9Arw0LAA4QTxuLFt01XYdigurz4FT15ox2oDGGGrRb3VGjDTXK1OWVJoLMW95EVqyMc9F+Fdej85LHE+8WesIfacjUQtTG1tzYVQTfubZq0+qxXws8QrxMLFtVE38tbeXo+Ok1/U5TUa6FjWflEfvKY3XVcl8RKkXua7fVz/Blj8Gh+dWe2cOxa0lpM75ZHyz9adQrB2Pb4571E4u2xI5un0R0MFJZBQuPDc1G5rPhyk+Hb4LRG3dS0m8IASQUOskv93z978L1+Abu9CLP6d6s5p+BzWxhMUqwQXC/CCpTywrkJ0RG`, 594 }, 595 // Basic XSS 596 { 597 in: `test<script>alert(document.cookie)</script>`, 598 expected: `test`, 599 }, 600 { 601 in: `<<<><<script src=http://fake-evil.ru/test.js>`, 602 expected: `<<<><`, 603 }, 604 { 605 in: `<script<script src=http://fake-evil.ru/test.js>>`, 606 expected: `>`, 607 }, 608 { 609 in: `<SCRIPT/XSS SRC="http://ha.ckers.org/xss.js"></SCRIPT>`, 610 expected: ``, 611 }, 612 { 613 in: "<BODY onload!#$%&()*~+-_.,:;?@[/|\\]^`=alert(\"XSS\")>", 614 expected: ``, 615 }, 616 { 617 in: `<BODY ONLOAD=alert('XSS')>`, 618 expected: ``, 619 }, 620 { 621 in: `<iframe src=http://ha.ckers.org/scriptlet.html <`, 622 expected: ``, 623 }, 624 { 625 in: `<INPUT TYPE="IMAGE" SRC="javascript:alert('XSS');"">`, 626 expected: `<input type="IMAGE">`, 627 }, 628 { 629 in: `<a onblur="alert(secret)" href="http://www.google.com">Google</a>`, 630 expected: `<a href="http://www.google.com">Google</a>`, 631 }, 632 // IMG attacks 633 { 634 in: `<img src="http://www.myspace.com/img.gif"/>`, 635 expected: `<img src="http://www.myspace.com/img.gif"/>`, 636 }, 637 { 638 in: `<img src=javascript:alert(document.cookie)>`, 639 expected: ``, 640 }, 641 { 642 in: `<IMG SRC=javascript:alert('XSS')>`, 643 expected: ``, 644 }, 645 { 646 in: `<IMG SRC='javascript:alert('XSS')'>`, 647 expected: ``, 648 }, 649 { 650 in: `<IMG SRC="jav
ascript:alert('XSS');">`, 651 expected: ``, 652 }, 653 { 654 in: `<IMG SRC=javascript:alert('XSS')>`, 655 expected: ``, 656 }, 657 { 658 in: `<IMG SRC=javascript:alert('XSS')>`, 659 expected: ``, 660 }, 661 { 662 in: `<IMG SRC="javascript:alert('XSS')"`, 663 expected: ``, 664 }, 665 { 666 in: `<IMG LOWSRC="javascript:alert('XSS')">`, 667 expected: ``, 668 }, 669 { 670 in: `<BGSOUND SRC="javascript:alert('XSS');">`, 671 expected: ``, 672 }, 673 // HREF attacks 674 { 675 in: `<LINK REL="stylesheet" HREF="javascript:alert('XSS');">`, 676 expected: ``, 677 }, 678 { 679 in: `<LINK REL="stylesheet" HREF="http://ha.ckers.org/xss.css">`, 680 expected: ``, 681 }, 682 { 683 in: `<STYLE>@import'http://ha.ckers.org/xss.css';</STYLE>`, 684 expected: ``, 685 }, 686 { 687 in: `<STYLE>BODY{-moz-binding:url("http://ha.ckers.org/xssmoz.xml#xss")}</STYLE>`, 688 expected: ``, 689 }, 690 { 691 in: `<STYLE>li {list-style-image: url("javascript:alert('XSS')");}</STYLE><UL><LI>XSS`, 692 expected: `<ul><li>XSS`, 693 }, 694 { 695 in: `<IMG SRC='vbscript:msgbox("XSS")'>`, 696 expected: ``, 697 }, 698 { 699 in: `<META HTTP-EQUIV="refresh" CONTENT="0; URL=http://;URL=javascript:alert('XSS');">`, 700 expected: ``, 701 }, 702 { 703 in: `<META HTTP-EQUIV="refresh" CONTENT="0;url=javascript:alert('XSS');">`, 704 expected: ``, 705 }, 706 { 707 in: `<META HTTP-EQUIV="refresh" CONTENT="0;url=data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4K">`, 708 expected: ``, 709 }, 710 { 711 in: `<IFRAME SRC="javascript:alert('XSS');"></IFRAME>`, 712 expected: ``, 713 }, 714 { 715 in: `<FRAMESET><FRAME SRC="javascript:alert('XSS');"></FRAMESET>`, 716 expected: ``, 717 }, 718 { 719 in: `<TABLE BACKGROUND="javascript:alert('XSS')">`, 720 expected: ``, 721 }, 722 { 723 in: `<TABLE><TD BACKGROUND="javascript:alert('XSS')">`, 724 expected: `<td>`, 725 }, 726 { 727 in: `<DIV STYLE="background-image: url(javascript:alert('XSS'))">`, 728 expected: `<div>`, 729 }, 730 { 731 in: `<DIV STYLE="width: expression(alert('XSS'));">`, 732 expected: `<div>`, 733 }, 734 { 735 in: `<IMG STYLE="xss:expr/*XSS*/ession(alert('XSS'))">`, 736 expected: ``, 737 }, 738 { 739 in: `<STYLE>@im\\port'\\ja\\vasc\\ript:alert("XSS")';</STYLE>`, 740 expected: ``, 741 }, 742 { 743 in: `<BASE HREF="javascript:alert('XSS');//">`, 744 expected: ``, 745 }, 746 { 747 in: `<BaSe hReF="http://arbitrary.com/">`, 748 expected: ``, 749 }, 750 { 751 in: `<OBJECT TYPE="text/x-scriptlet" DATA="http://ha.ckers.org/scriptlet.html"></OBJECT>`, 752 expected: ``, 753 }, 754 { 755 in: `<OBJECT classid=clsid:ae24fdae-03c6-11d1-8b76-0080c744f389><param name=url value=javascript:alert('XSS')></OBJECT>`, 756 expected: ``, 757 }, 758 { 759 in: `<EMBED SRC="http://ha.ckers.org/xss.swf" AllowScriptAccess="always"></EMBED>`, 760 expected: ``, 761 }, 762 { 763 in: `<EMBED SRC="data:image/svg+xml;base64,PHN2ZyB4bWxuczpzdmc9Imh0dH A6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcv MjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hs aW5rIiB2ZXJzaW9uPSIxLjAiIHg9IjAiIHk9IjAiIHdpZHRoPSIxOTQiIGhlaWdodD0iMjAw IiBpZD0ieHNzIj48c2NyaXB0IHR5cGU9InRleHQvZWNtYXNjcmlwdCI+YWxlcnQoIlh TUyIpOzwvc2NyaXB0Pjwvc3ZnPg==" type="image/svg+xml" AllowScriptAccess="always"></EMBED>`, 764 expected: ``, 765 }, 766 { 767 in: `<SCRIPT a=">" SRC="http://ha.ckers.org/xss.js"></SCRIPT>`, 768 expected: ``, 769 }, 770 { 771 in: `<SCRIPT a=">" '' SRC="http://ha.ckers.org/xss.js"></SCRIPT>`, 772 expected: ``, 773 }, 774 { 775 in: "<SCRIPT a=`>` SRC=\"http://ha.ckers.org/xss.js\"></SCRIPT>", 776 expected: ``, 777 }, 778 { 779 in: `<SCRIPT a=">'>" SRC="http://ha.ckers.org/xss.js"></SCRIPT>`, 780 expected: ``, 781 }, 782 { 783 in: `<SCRIPT>document.write("<SCRI");</SCRIPT>PT SRC="http://ha.ckers.org/xss.js"></SCRIPT>`, 784 expected: `PT SRC="http://ha.ckers.org/xss.js">`, 785 }, 786 { 787 in: `<SCRIPT SRC=http://ha.ckers.org/xss.js`, 788 expected: ``, 789 }, 790 { 791 in: `<div/style=\-\mo\z\-b\i\nd\in\g:\url(//business\i\nfo.co.uk\/labs\/xbl\/xbl\.xml\#xss)&>`, 792 expected: `<div>`, 793 }, 794 { 795 in: `<a href='aim: &c:\\windows\\system32\\calc.exe' ini='C:\\Documents and Settings\\All Users\\Start Menu\\Programs\\Startup\\pwnd.bat'>`, 796 expected: ``, 797 }, 798 { 799 in: `<!--\n<A href=\n- --><a href=javascript:alert:document.domain>test-->`, 800 expected: `test-->`, 801 }, 802 { 803 in: `<a></a style="xx:expr/**/ession(document.appendChild(document.createElement('script')).src='http://h4k.in/i.js')">`, 804 expected: ``, 805 }, 806 // CSS attacks 807 { 808 in: `<div style="position:absolute">`, 809 expected: `<div>`, 810 }, 811 { 812 in: `<style>b { position:absolute }</style>`, 813 expected: ``, 814 }, 815 { 816 in: `<div style="z-index:25">test</div>`, 817 expected: `<div>test</div>`, 818 }, 819 { 820 in: `<style>z-index:25</style>`, 821 expected: ``, 822 }, 823 // Strings that cause issues for tokenizers 824 { 825 in: `<a - href="http://www.test.com">`, 826 expected: `<a href="http://www.test.com">`, 827 }, 828 // Comments 829 { 830 in: `text <!-- comment -->`, 831 expected: `text `, 832 }, 833 { 834 in: `<div>text <!-- comment --></div>`, 835 expected: `<div>text </div>`, 836 }, 837 { 838 in: `<div>text <!--[if IE]> comment <[endif]--></div>`, 839 expected: `<div>text </div>`, 840 }, 841 { 842 in: `<div>text <!--[if IE]> <!--[if gte 6]> comment <[endif]--><[endif]--></div>`, 843 expected: `<div>text <[endif]--></div>`, 844 }, 845 { 846 in: `<div>text <!--[if IE]> <!-- IE specific --> comment <[endif]--></div>`, 847 expected: `<div>text comment <[endif]--></div>`, 848 }, 849 { 850 in: `<div>text <!-- [ if lte 6 ]>\ncomment <[ endif\n]--></div>`, 851 expected: `<div>text </div>`, 852 }, 853 { 854 in: `<div>text <![if !IE]> comment <![endif]></div>`, 855 expected: `<div>text comment </div>`, 856 }, 857 { 858 in: `<div>text <![ if !IE]> comment <![endif]></div>`, 859 expected: `<div>text comment </div>`, 860 }, 861 } 862 863 // These tests are run concurrently to enable the race detector to pick up 864 // potential issues 865 wg := sync.WaitGroup{} 866 wg.Add(len(tests)) 867 for ii, tt := range tests { 868 go func(ii int, tt test) { 869 out := p.Sanitize(tt.in) 870 if out != tt.expected { 871 t.Errorf( 872 "test %d failed;\ninput : %s\noutput : %s\nexpected: %s", 873 ii, 874 tt.in, 875 out, 876 tt.expected, 877 ) 878 } 879 wg.Done() 880 }(ii, tt) 881 } 882 wg.Wait() 883} 884 885func TestXSS(t *testing.T) { 886 887 p := UGCPolicy() 888 889 tests := []test{ 890 { 891 in: `<A HREF="javascript:document.location='http://www.google.com/'">XSS</A>`, 892 expected: `XSS`, 893 }, 894 { 895 in: `<A HREF="h 896tt p://6 6.000146.0x7.147/">XSS</A>`, 897 expected: `XSS`, 898 }, 899 { 900 in: `<SCRIPT>document.write("<SCRI");</SCRIPT>PT SRC="http://ha.ckers.org/xss.js"></SCRIPT>`, 901 expected: `PT SRC="http://ha.ckers.org/xss.js">`, 902 }, 903 { 904 in: `<SCRIPT a=">'>" SRC="http://ha.ckers.org/xss.js"></SCRIPT>`, 905 expected: ``, 906 }, 907 { 908 in: "<SCRIPT a=`>` SRC=\"http://ha.ckers.org/xss.js\"></SCRIPT>", 909 expected: ``, 910 }, 911 { 912 in: `<SCRIPT "a='>'" SRC="http://ha.ckers.org/xss.js"></SCRIPT>`, 913 expected: ``, 914 }, 915 { 916 in: `<SCRIPT a=">" '' SRC="http://ha.ckers.org/xss.js"></SCRIPT>`, 917 expected: ``, 918 }, 919 { 920 in: `<SCRIPT =">" SRC="http://ha.ckers.org/xss.js"></SCRIPT>`, 921 expected: ``, 922 }, 923 { 924 in: `<SCRIPT a=">" SRC="http://ha.ckers.org/xss.js"></SCRIPT>`, 925 expected: ``, 926 }, 927 { 928 in: `<HEAD><META HTTP-EQUIV="CONTENT-TYPE" CONTENT="text/html; charset=UTF-7"> </HEAD>+ADw-SCRIPT+AD4-alert('XSS')`, 929 expected: ` +ADw-SCRIPT+AD4-alert('XSS')`, 930 }, 931 { 932 in: `<META HTTP-EQUIV="Set-Cookie" Content="USERID=<SCRIPT>alert('XSS')</SCRIPT>">`, 933 expected: ``, 934 }, 935 { 936 in: `<? echo('<SCR)'; 937echo('IPT>alert("XSS")</SCRIPT>'); ?>`, 938 expected: `alert("XSS")'); ?>`, 939 }, 940 { 941 in: `<!--#exec cmd="/bin/echo '<SCR'"--><!--#exec cmd="/bin/echo 'IPT SRC=http://ha.ckers.org/xss.js></SCRIPT>'"-->`, 942 expected: ``, 943 }, 944 { 945 in: `<HTML><BODY> 946<?xml:namespace prefix="t" ns="urn:schemas-microsoft-com:time"> 947<?import namespace="t" implementation="#default#time2"> 948<t:set attributeName="innerHTML" to="XSS<SCRIPT DEFER>alert("XSS")</SCRIPT>"> 949</BODY></HTML>`, 950 expected: "\n\n\n">\n", 951 }, 952 { 953 in: `<XML SRC="xsstest.xml" ID=I></XML> 954<SPAN DATASRC=#I DATAFLD=C DATAFORMATAS=HTML></SPAN>`, 955 expected: ` 956<span></span>`, 957 }, 958 { 959 in: `<XML ID="xss"><I><B><IMG SRC="javas<!-- -->cript:alert('XSS')"></B></I></XML> 960<SPAN DATASRC="#xss" DATAFLD="B" DATAFORMATAS="HTML"></SPAN>`, 961 expected: `<i><b></b></i> 962<span></span>`, 963 }, 964 { 965 in: `<EMBED SRC="data:image/svg+xml;base64,PHN2ZyB4bWxuczpzdmc9Imh0dH A6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcv MjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hs aW5rIiB2ZXJzaW9uPSIxLjAiIHg9IjAiIHk9IjAiIHdpZHRoPSIxOTQiIGhlaWdodD0iMjAw IiBpZD0ieHNzIj48c2NyaXB0IHR5cGU9InRleHQvZWNtYXNjcmlwdCI+YWxlcnQoIlh TUyIpOzwvc2NyaXB0Pjwvc3ZnPg==" type="image/svg+xml" AllowScriptAccess="always"></EMBED>`, 966 expected: ``, 967 }, 968 { 969 in: `<OBJECT TYPE="text/x-scriptlet" DATA="http://ha.ckers.org/scriptlet.html"></OBJECT>`, 970 expected: ``, 971 }, 972 { 973 in: `<BASE HREF="javascript:alert('XSS');//">`, 974 expected: ``, 975 }, 976 { 977 in: `<!--[if gte IE 4]><SCRIPT>alert('XSS');</SCRIPT><![endif]-->`, 978 expected: ``, 979 }, 980 { 981 in: `<DIV STYLE="width: expression(alert('XSS'));">`, 982 expected: `<div>`, 983 }, 984 { 985 in: `<DIV STYLE="background-image: url(javascript:alert('XSS'))">`, 986 expected: `<div>`, 987 }, 988 { 989 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">`, 990 expected: `<div>`, 991 }, 992 { 993 in: `<DIV STYLE="background-image: url(javascript:alert('XSS'))">`, 994 expected: `<div>`, 995 }, 996 { 997 in: `<TABLE><TD BACKGROUND="javascript:alert('XSS')">`, 998 expected: `<table><td>`, 999 }, 1000 { 1001 in: `<TABLE BACKGROUND="javascript:alert('XSS')">`, 1002 expected: `<table>`, 1003 }, 1004 { 1005 in: `<FRAMESET><FRAME SRC="javascript:alert('XSS');"></FRAMESET>`, 1006 expected: ``, 1007 }, 1008 { 1009 in: `<IFRAME SRC=# onmouseover="alert(document.cookie)"></IFRAME>`, 1010 expected: ``, 1011 }, 1012 { 1013 in: `<IFRAME SRC="javascript:alert('XSS');"></IFRAME>`, 1014 expected: ``, 1015 }, 1016 { 1017 in: `<META HTTP-EQUIV="refresh" CONTENT="0; URL=http://;URL=javascript:alert('XSS');">`, 1018 expected: ``, 1019 }, 1020 { 1021 in: `<META HTTP-EQUIV="refresh" CONTENT="0;url=data:text/html base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4K">`, 1022 expected: ``, 1023 }, 1024 { 1025 in: `<META HTTP-EQUIV="refresh" CONTENT="0;url=javascript:alert('XSS');">`, 1026 expected: ``, 1027 }, 1028 { 1029 in: `<XSS STYLE="behavior: url(xss.htc);">`, 1030 expected: ``, 1031 }, 1032 { 1033 in: `<XSS STYLE="xss:expression(alert('XSS'))">`, 1034 expected: ``, 1035 }, 1036 { 1037 in: `<STYLE type="text/css">BODY{background:url("javascript:alert('XSS')")}</STYLE>`, 1038 expected: ``, 1039 }, 1040 { 1041 in: `<STYLE>.XSS{background-image:url("javascript:alert('XSS')");}</STYLE><A CLASS=XSS></A>`, 1042 expected: ``, 1043 }, 1044 { 1045 in: `<STYLE TYPE="text/javascript">alert('XSS');</STYLE>`, 1046 expected: ``, 1047 }, 1048 { 1049 in: `<IMG STYLE="xss:expr/*XSS*/ession(alert('XSS'))">`, 1050 expected: ``, 1051 }, 1052 { 1053 in: `<STYLE>@im\port'\ja\vasc\ript:alert("XSS")';</STYLE>`, 1054 expected: ``, 1055 }, 1056 { 1057 in: `<STYLE>BODY{-moz-binding:url("http://ha.ckers.org/xssmoz.xml#xss")}</STYLE>`, 1058 expected: ``, 1059 }, 1060 { 1061 in: `<META HTTP-EQUIV="Link" Content="<http://ha.ckers.org/xss.css>; REL=stylesheet">`, 1062 expected: ``, 1063 }, 1064 { 1065 in: `<STYLE>@import'http://ha.ckers.org/xss.css';</STYLE>`, 1066 expected: ``, 1067 }, 1068 { 1069 in: `<LINK REL="stylesheet" HREF="http://ha.ckers.org/xss.css">`, 1070 expected: ``, 1071 }, 1072 { 1073 in: `<LINK REL="stylesheet" HREF="javascript:alert('XSS');">`, 1074 expected: ``, 1075 }, 1076 { 1077 in: `<BR SIZE="&{alert('XSS')}">`, 1078 expected: `<br>`, 1079 }, 1080 { 1081 in: `<BGSOUND SRC="javascript:alert('XSS');">`, 1082 expected: ``, 1083 }, 1084 { 1085 in: `<BODY ONLOAD=alert('XSS')>`, 1086 expected: ``, 1087 }, 1088 { 1089 in: `<STYLE>li {list-style-image: url("javascript:alert('XSS')");}</STYLE><UL><LI>XSS</br>`, 1090 expected: `<ul><li>XSS</br>`, 1091 }, 1092 { 1093 in: `<IMG LOWSRC="javascript:alert('XSS')">`, 1094 expected: ``, 1095 }, 1096 { 1097 in: `<IMG DYNSRC="javascript:alert('XSS')">`, 1098 expected: ``, 1099 }, 1100 { 1101 in: `<BODY BACKGROUND="javascript:alert('XSS')">`, 1102 expected: ``, 1103 }, 1104 { 1105 in: `<INPUT TYPE="IMAGE" SRC="javascript:alert('XSS');">`, 1106 expected: ``, 1107 }, 1108 { 1109 in: `</TITLE><SCRIPT>alert("XSS");</SCRIPT>`, 1110 expected: ``, 1111 }, 1112 { 1113 in: `\";alert('XSS');//`, 1114 expected: `\";alert('XSS');//`, 1115 }, 1116 { 1117 in: `<iframe src=http://ha.ckers.org/scriptlet.html <`, 1118 expected: ``, 1119 }, 1120 { 1121 in: `<SCRIPT SRC=http://ha.ckers.org/xss.js?< B >`, 1122 expected: ``, 1123 }, 1124 { 1125 in: `<<SCRIPT>alert("XSS");//<</SCRIPT>`, 1126 expected: `<`, 1127 }, 1128 { 1129 in: "<BODY onload!#$%&()*~+-_.,:;?@[/|\\]^`=alert(\"XSS\")>", 1130 expected: ``, 1131 }, 1132 { 1133 in: `<SCRIPT/SRC="http://ha.ckers.org/xss.js"></SCRIPT>`, 1134 expected: ``, 1135 }, 1136 { 1137 in: `<SCRIPT/XSS SRC="http://ha.ckers.org/xss.js"></SCRIPT>`, 1138 expected: ``, 1139 }, 1140 { 1141 in: `<IMG SRC="  javascript:alert('XSS');">`, 1142 expected: ``, 1143 }, 1144 { 1145 in: `<IMG SRC="jav
ascript:alert('XSS');">`, 1146 expected: ``, 1147 }, 1148 { 1149 in: `<IMG SRC="jav	ascript:alert('XSS');">`, 1150 expected: ``, 1151 }, 1152 { 1153 in: `<IMG SRC="jav ascript:alert('XSS');">`, 1154 expected: ``, 1155 }, 1156 { 1157 in: `<IMG SRC=javascript:alert('XSS')>`, 1158 expected: ``, 1159 }, 1160 { 1161 in: `<IMG SRC=javascript:a& 1162#0000108ert('XSS')>`, 1163 expected: ``, 1164 }, 1165 { 1166 in: `<IMG SRC=javascript:alert( 1167'XSS')>`, 1168 expected: ``, 1169 }, 1170 { 1171 in: `<IMG SRC=/ onerror="alert(String.fromCharCode(88,83,83))"></img>`, 1172 expected: `<img src="/"></img>`, 1173 }, 1174 { 1175 in: `<IMG onmouseover="alert('xxs')">`, 1176 expected: ``, 1177 }, 1178 { 1179 in: `<IMG SRC= onmouseover="alert('xxs')">`, 1180 expected: `<img src="onmouseover=%22alert%28%27xxs%27%29%22">`, 1181 }, 1182 { 1183 in: `<IMG SRC=# onmouseover="alert('xxs')">`, 1184 expected: ``, 1185 }, 1186 { 1187 in: `<IMG SRC=javascript:alert(String.fromCharCode(88,83,83))>`, 1188 expected: ``, 1189 }, 1190 { 1191 in: `<IMG """><SCRIPT>alert("XSS")</SCRIPT>">`, 1192 expected: `">`, 1193 }, 1194 { 1195 in: `<IMG SRC=javascript:alert("XSS")>`, 1196 expected: ``, 1197 }, 1198 { 1199 in: `<IMG SRC=JaVaScRiPt:alert('XSS')>`, 1200 expected: ``, 1201 }, 1202 { 1203 in: `<IMG SRC=javascript:alert('XSS')>`, 1204 expected: ``, 1205 }, 1206 { 1207 in: `<IMG SRC="javascript:alert('XSS');">`, 1208 expected: ``, 1209 }, 1210 { 1211 in: `<SCRIPT SRC=http://ha.ckers.org/xss.js></SCRIPT>`, 1212 expected: ``, 1213 }, 1214 { 1215 in: `'';!--"<XSS>=&{()}`, 1216 expected: `'';!--"=&{()}`, 1217 }, 1218 { 1219 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>`, 1220 expected: `';alert(String.fromCharCode(88,83,83))//';alert(String.fromCharCode(88,83,83))//";alert(String.fromCharCode(88,83,83))//";alert(String.fromCharCode(88,83,83))//-->">'>`, 1221 }, 1222 } 1223 1224 // These tests are run concurrently to enable the race detector to pick up 1225 // potential issues 1226 wg := sync.WaitGroup{} 1227 wg.Add(len(tests)) 1228 for ii, tt := range tests { 1229 go func(ii int, tt test) { 1230 out := p.Sanitize(tt.in) 1231 if out != tt.expected { 1232 t.Errorf( 1233 "test %d failed;\ninput : %s\noutput : %s\nexpected: %s", 1234 ii, 1235 tt.in, 1236 out, 1237 tt.expected, 1238 ) 1239 } 1240 wg.Done() 1241 }(ii, tt) 1242 } 1243 wg.Wait() 1244} 1245 1246func TestIssue3(t *testing.T) { 1247 // https://github.com/microcosm-cc/bluemonday/issues/3 1248 1249 p := UGCPolicy() 1250 p.AllowStyling() 1251 1252 tests := []test{ 1253 { 1254 in: `Hello <span class="foo bar bash">there</span> world.`, 1255 expected: `Hello <span class="foo bar bash">there</span> world.`, 1256 }, 1257 { 1258 in: `Hello <span class="javascript:alert(123)">there</span> world.`, 1259 expected: `Hello <span>there</span> world.`, 1260 }, 1261 { 1262 in: `Hello <span class="><script src="http://hackers.org/XSS.js"></script>">there</span> world.`, 1263 expected: `Hello <span>">there</span> world.`, 1264 }, 1265 { 1266 in: `Hello <span class="><script src='http://hackers.org/XSS.js'></script>">there</span> world.`, 1267 expected: `Hello <span>there</span> world.`, 1268 }, 1269 } 1270 1271 wg := sync.WaitGroup{} 1272 wg.Add(len(tests)) 1273 for ii, tt := range tests { 1274 go func(ii int, tt test) { 1275 out := p.Sanitize(tt.in) 1276 if out != tt.expected { 1277 t.Errorf( 1278 "test %d failed;\ninput : %s\noutput : %s\nexpected: %s", 1279 ii, 1280 tt.in, 1281 out, 1282 tt.expected, 1283 ) 1284 } 1285 wg.Done() 1286 }(ii, tt) 1287 } 1288 wg.Wait() 1289} 1290 1291func TestIssue9(t *testing.T) { 1292 1293 p := UGCPolicy() 1294 p.AllowAttrs("class").Matching(SpaceSeparatedTokens).OnElements("div", "span") 1295 p.AllowAttrs("class", "name").Matching(SpaceSeparatedTokens).OnElements("a") 1296 p.AllowAttrs("rel").Matching(regexp.MustCompile(`^nofollow$`)).OnElements("a") 1297 p.AllowAttrs("aria-hidden").Matching(regexp.MustCompile(`^true$`)).OnElements("a") 1298 p.AllowDataURIImages() 1299 1300 tt := test{ 1301 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>`, 1302 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>`, 1303 } 1304 out := p.Sanitize(tt.in) 1305 if out != tt.expected { 1306 t.Errorf( 1307 "test failed;\ninput : %s\noutput : %s\nexpected: %s", 1308 tt.in, 1309 out, 1310 tt.expected, 1311 ) 1312 } 1313 1314 tt = test{ 1315 in: `<h2><a name="git-diff" class="anchor" href="#git-diff" aria-hidden="true"><span class="octicon octicon-link"></span></a>git diff</h2>`, 1316 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>`, 1317 } 1318 out = p.Sanitize(tt.in) 1319 if out != tt.expected { 1320 t.Errorf( 1321 "test failed;\ninput : %s\noutput : %s\nexpected: %s", 1322 tt.in, 1323 out, 1324 tt.expected, 1325 ) 1326 } 1327 1328 p.AddTargetBlankToFullyQualifiedLinks(true) 1329 1330 tt = test{ 1331 in: `<h2><a name="git-diff" class="anchor" href="#git-diff" aria-hidden="true"><span class="octicon octicon-link"></span></a>git diff</h2>`, 1332 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>`, 1333 } 1334 out = p.Sanitize(tt.in) 1335 if out != tt.expected { 1336 t.Errorf( 1337 "test failed;\ninput : %s\noutput : %s\nexpected: %s", 1338 tt.in, 1339 out, 1340 tt.expected, 1341 ) 1342 } 1343 1344 tt = test{ 1345 in: `<h2><a name="git-diff" class="anchor" href="https://github.com/shurcooL/github_flavored_markdown/blob/master/sanitize_test.go" aria-hidden="true"><span class="octicon octicon-link"></span></a>git diff</h2>`, 1346 expected: `<h2><a name="git-diff" class="anchor" href="https://github.com/shurcooL/github_flavored_markdown/blob/master/sanitize_test.go" aria-hidden="true" rel="nofollow noopener" target="_blank"><span class="octicon octicon-link"></span></a>git diff</h2>`, 1347 } 1348 out = p.Sanitize(tt.in) 1349 if out != tt.expected { 1350 t.Errorf( 1351 "test failed;\ninput : %s\noutput : %s\nexpected: %s", 1352 tt.in, 1353 out, 1354 tt.expected, 1355 ) 1356 } 1357 1358 tt = test{ 1359 in: `<h2><a name="git-diff" class="anchor" href="https://github.com/shurcooL/github_flavored_markdown/blob/master/sanitize_test.go" aria-hidden="true" target="namedwindow"><span class="octicon octicon-link"></span></a>git diff</h2>`, 1360 expected: `<h2><a name="git-diff" class="anchor" href="https://github.com/shurcooL/github_flavored_markdown/blob/master/sanitize_test.go" aria-hidden="true" rel="nofollow noopener" target="_blank"><span class="octicon octicon-link"></span></a>git diff</h2>`, 1361 } 1362 out = p.Sanitize(tt.in) 1363 if out != tt.expected { 1364 t.Errorf( 1365 "test failed;\ninput : %s\noutput : %s\nexpected: %s", 1366 tt.in, 1367 out, 1368 tt.expected, 1369 ) 1370 } 1371} 1372 1373func TestIssue18(t *testing.T) { 1374 p := UGCPolicy() 1375 1376 p.AllowAttrs("color").OnElements("font") 1377 p.AllowElements("font") 1378 1379 tt := test{ 1380 in: `<font face="Arial">No link here. <a href="http://link.com">link here</a>.</font> Should not be linked here.`, 1381 expected: `No link here. <a href="http://link.com" rel="nofollow">link here</a>. Should not be linked here.`, 1382 } 1383 out := p.Sanitize(tt.in) 1384 if out != tt.expected { 1385 t.Errorf( 1386 "test failed;\ninput : %s\noutput : %s\nexpected: %s", 1387 tt.in, 1388 out, 1389 tt.expected) 1390 } 1391} 1392 1393func TestIssue23(t *testing.T) { 1394 p := NewPolicy() 1395 p.SkipElementsContent("tag1", "tag2") 1396 input := `<tag1>cut<tag2></tag2>harm</tag1><tag1>123</tag1><tag2>234</tag2>` 1397 out := p.Sanitize(input) 1398 expected := "" 1399 if out != expected { 1400 t.Errorf( 1401 "test failed;\ninput : %s\noutput : %s\nexpected: %s", 1402 input, 1403 out, 1404 expected) 1405 } 1406 1407 p = NewPolicy() 1408 p.SkipElementsContent("tag") 1409 p.AllowElements("p") 1410 input = `<tag>234<p>asd</p></tag>` 1411 out = p.Sanitize(input) 1412 expected = "" 1413 if out != expected { 1414 t.Errorf( 1415 "test failed;\ninput : %s\noutput : %s\nexpected: %s", 1416 input, 1417 out, 1418 expected) 1419 } 1420 1421 p = NewPolicy() 1422 p.SkipElementsContent("tag") 1423 p.AllowElements("p", "br") 1424 input = `<tag>234<p>as<br/>d</p></tag>` 1425 out = p.Sanitize(input) 1426 expected = "" 1427 if out != expected { 1428 t.Errorf( 1429 "test failed;\ninput : %s\noutput : %s\nexpected: %s", 1430 input, 1431 out, 1432 expected) 1433 } 1434} 1435 1436func TestAllowNoAttrs(t *testing.T) { 1437 input := "<tag>test</tag>" 1438 outputFail := "test" 1439 outputOk := input 1440 1441 p := NewPolicy() 1442 p.AllowElements("tag") 1443 1444 if output := p.Sanitize(input); output != outputFail { 1445 t.Errorf( 1446 "test failed;\ninput : %s\noutput : %s\nexpected: %s", 1447 input, 1448 output, 1449 outputFail, 1450 ) 1451 } 1452 1453 p.AllowNoAttrs().OnElements("tag") 1454 1455 if output := p.Sanitize(input); output != outputOk { 1456 t.Errorf( 1457 "test failed;\ninput : %s\noutput : %s\nexpected: %s", 1458 input, 1459 output, 1460 outputOk, 1461 ) 1462 } 1463} 1464 1465func TestSkipElementsContent(t *testing.T) { 1466 input := "<tag>test</tag>" 1467 outputFail := "test" 1468 outputOk := "" 1469 1470 p := NewPolicy() 1471 1472 if output := p.Sanitize(input); output != outputFail { 1473 t.Errorf( 1474 "test failed;\ninput : %s\noutput : %s\nexpected: %s", 1475 input, 1476 output, 1477 outputFail, 1478 ) 1479 } 1480 1481 p.SkipElementsContent("tag") 1482 1483 if output := p.Sanitize(input); output != outputOk { 1484 t.Errorf( 1485 "test failed;\ninput : %s\noutput : %s\nexpected: %s", 1486 input, 1487 output, 1488 outputOk, 1489 ) 1490 } 1491} 1492 1493func TestTagSkipClosingTagNested(t *testing.T) { 1494 input := "<tag1><tag2><tag3>text</tag3></tag2></tag1>" 1495 outputOk := "<tag2>text</tag2>" 1496 1497 p := NewPolicy() 1498 p.AllowElements("tag1", "tag3") 1499 p.AllowNoAttrs().OnElements("tag2") 1500 1501 if output := p.Sanitize(input); output != outputOk { 1502 t.Errorf( 1503 "test failed;\ninput : %s\noutput : %s\nexpected: %s", 1504 input, 1505 output, 1506 outputOk, 1507 ) 1508 } 1509} 1510 1511func TestAddSpaces(t *testing.T) { 1512 p := UGCPolicy() 1513 p.AddSpaceWhenStrippingTag(true) 1514 1515 tests := []test{ 1516 { 1517 in: `<foo>Hello</foo><bar>World</bar>`, 1518 expected: ` Hello World `, 1519 }, 1520 { 1521 in: `<p>Hello</p><bar>World</bar>`, 1522 expected: `<p>Hello</p> World `, 1523 }, 1524 { 1525 in: `<p>Hello</p><foo /><p>World</p>`, 1526 expected: `<p>Hello</p> <p>World</p>`, 1527 }, 1528 } 1529 1530 // These tests are run concurrently to enable the race detector to pick up 1531 // potential issues 1532 wg := sync.WaitGroup{} 1533 wg.Add(len(tests)) 1534 for ii, tt := range tests { 1535 go func(ii int, tt test) { 1536 out := p.Sanitize(tt.in) 1537 if out != tt.expected { 1538 t.Errorf( 1539 "test %d failed;\ninput : %s\noutput : %s\nexpected: %s", 1540 ii, 1541 tt.in, 1542 out, 1543 tt.expected, 1544 ) 1545 } 1546 wg.Done() 1547 }(ii, tt) 1548 } 1549 wg.Wait() 1550} 1551 1552func TestTargetBlankNoOpener(t *testing.T) { 1553 p := UGCPolicy() 1554 p.AddTargetBlankToFullyQualifiedLinks(true) 1555 p.AllowAttrs("target").Matching(Paragraph).OnElements("a") 1556 1557 tests := []test{ 1558 { 1559 in: `<a href="/path" />`, 1560 expected: `<a href="/path" rel="nofollow"/>`, 1561 }, 1562 { 1563 in: `<a href="/path" target="_blank" />`, 1564 expected: `<a href="/path" target="_blank" rel="nofollow noopener"/>`, 1565 }, 1566 { 1567 in: `<a href="/path" target="foo" />`, 1568 expected: `<a href="/path" target="foo" rel="nofollow"/>`, 1569 }, 1570 { 1571 in: `<a href="https://www.google.com/" />`, 1572 expected: `<a href="https://www.google.com/" rel="nofollow noopener" target="_blank"/>`, 1573 }, 1574 { 1575 in: `<a href="https://www.google.com/" target="_blank"/>`, 1576 expected: `<a href="https://www.google.com/" target="_blank" rel="nofollow noopener"/>`, 1577 }, 1578 { 1579 in: `<a href="https://www.google.com/" rel="nofollow"/>`, 1580 expected: `<a href="https://www.google.com/" rel="nofollow noopener" target="_blank"/>`, 1581 }, 1582 { 1583 in: `<a href="https://www.google.com/" rel="noopener"/>`, 1584 expected: `<a href="https://www.google.com/" rel="nofollow noopener" target="_blank"/>`, 1585 }, 1586 { 1587 in: `<a href="https://www.google.com/" rel="noopener nofollow" />`, 1588 expected: `<a href="https://www.google.com/" rel="nofollow noopener" target="_blank"/>`, 1589 }, 1590 { 1591 in: `<a href="https://www.google.com/" target="foo" />`, 1592 expected: `<a href="https://www.google.com/" target="_blank" rel="nofollow noopener"/>`, 1593 }, 1594 } 1595 1596 // These tests are run concurrently to enable the race detector to pick up 1597 // potential issues 1598 wg := sync.WaitGroup{} 1599 wg.Add(len(tests)) 1600 for ii, tt := range tests { 1601 go func(ii int, tt test) { 1602 out := p.Sanitize(tt.in) 1603 if out != tt.expected { 1604 t.Errorf( 1605 "test %d failed;\ninput : %s\noutput : %s\nexpected: %s", 1606 ii, 1607 tt.in, 1608 out, 1609 tt.expected, 1610 ) 1611 } 1612 wg.Done() 1613 }(ii, tt) 1614 } 1615 wg.Wait() 1616} 1617 1618func TestIssue51(t *testing.T) { 1619 // Whitespace in URLs is permitted within HTML according to: 1620 // https://dev.w3.org/html5/spec-LC/urls.html#parsing-urls 1621 // 1622 // We were aggressively rejecting URLs that contained line feeds but these 1623 // are permitted. 1624 // 1625 // This test ensures that we do not regress that fix. 1626 p := NewPolicy() 1627 p.AllowImages() 1628 p.AllowDataURIImages() 1629 1630 input := `<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAIAAADajyQQAAAAhnpUWHRSYXcgcHJvZmlsZSB0eXBlIGV4aWYAAHjadY5LCsNADEP3c4oewb+R7eOUkEBv0OPXZpKmm76FLIQRGvv7dYxHwyTDpgcSoMLSUp5lghZKxELct3RxXuVycsdDZRlkONn9aGd+MRWBw80dExs2qXbZlTVKu6hbqWfkT8l30Z/8WvEBQsUsKBcOhtYAAAoCaVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/Pgo8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJYTVAgQ29yZSA0LjQuMC1FeGl2MiI+CiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICB4bWxuczpleGlmPSJodHRwOi8vbnMuYWRvYmUuY29tL2V4aWYvMS4wLyIKICAgIHhtbG5zOnRpZmY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vdGlmZi8xLjAvIgogICBleGlmOlBpeGVsWERpbWVuc2lvbj0iNzIiCiAgIGV4aWY6UGl4ZWxZRGltZW5zaW9uPSI3MiIKICAgdGlmZjpJbWFnZVdpZHRoPSI3MiIKICAgdGlmZjpJbWFnZUhlaWdodD0iNzIiCiAgIHRpZmY6T3JpZW50YXRpb249IjEiLz4KIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAKPD94cGFja2V0IGVuZD0idyI/Pq6cYi8AAAADc0JJVAgICNvhT+AAAAN7SURBVGje7dtRSBNhHADwfxJ3L96Le0kf1GD1sBDyO5ALbEkyMyY9bHswg+FDW5B7EKVhJSeElrQUcRIkFFHoi0toPriEVi8KbUQxKSYNk8HpYE5ot4e7e/l68NT08aTp6v9/25+P7+O3/3d3H3ffB7RooSSH7IQQYu0KS4qeeeEWyHbY+qLZvbbZiEcghBBHIJ43NhrQ4oYiRUU7sQ0lFJqPizbBEViUFCWfnOmyCp4ZaV/bfHLKIwiecLYUYJTSbLid2ALJX/E+q7VnUdGz0pSDOKakA39DQrQSd8RI0cqgCLEe8rZ55zb1X5oKwLAMywJoANpOI4ZhAEBdHnA6B5ZVPalqwHCckTGLAqvi69jPwZF36yrIK6GR4NrZjrbTbK2ziVsaeba0CaD+nAtOrtU6m6rY2qbazYWH08syqOtLwUcfoamjzpCsSPNPigy5bYQQIti7xuP6VaOshsV26052Uc/mE1M9DoEQQmxuMbyqGBvwBKUU/sUog380EIYwhCEMYQhD2DGMk4VCASuGMIQhDGEIQ9hxe0Af5eDyj7ejw5PRVAGgwnLNJ/qaK+HTnRZ/bF8rc9/s86umEoKpXyb8E+nWx7NP65nM+9HuB/5T5tc3zouzs/q7Ri0d6vdHLb5GU2lNxa0txuLq6aw3scDVNHZcrsjE0jKwnEmPQnQiVLg26KvnSmwqVjb3DjXvVC8djRVOtVbvGTbmh19utY55z7Cle/NQN94/8IcYl+iq2U19m55Mmb2d51ijnR45TP7yrPvmaME1NnZrrzjy1+mo1tBp6OI6DndF2Ji/f3s03Si+6r34p0FNRb5q50ULd4iuj7Bi8reR7uFUgzjYYYFcLpfL5WT9I0sm9l2rbjQfxnWEFcvFJsIZgEi/O3LgiaVmUluMubr8UN2fkGUZl1QIQxjCEIYwhCEMYYdbUuE+D4QhDGEIQxjC/luYvBK667zE8zx/oc0XXNK3B8vL0716tsX75IOe3fzwxNtyged5vuX6QGhFNThkUfakJ0Sb4H6RyFOqrIZ7rIInmqdUSQbsxDEez+5mI3lKpRm3YOuLSAql2fi4g9gDSUObZ4vy+o2tu/dmATiOBZA1UIEzcQDAMiaO+aPV9nbtKtfkwhWW4wBUWVOh3FTFsce2YnhSAk9K4EmJvxt4UgJPSuCSCmEIQxjCEAYAAL8BrebxGP8KiJcAAAAASUVORK5CYII=" alt="">` 1631 out := p.Sanitize(input) 1632 expected := `<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAIAAADajyQQAAAAhnpUWHRSYXcgcHJvZmlsZSB0eXBlIGV4aWYAAHjadY5LCsNADEP3c4oewb+R7eOUkEBv0OPXZpKmm76FLIQRGvv7dYxHwyTDpgcSoMLSUp5lghZKxELct3RxXuVycsdDZRlkONn9aGd+MRWBw80dExs2qXbZlTVKu6hbqWfkT8l30Z/8WvEBQsUsKBcOhtYAAAoCaVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/Pgo8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJYTVAgQ29yZSA0LjQuMC1FeGl2MiI+CiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICB4bWxuczpleGlmPSJodHRwOi8vbnMuYWRvYmUuY29tL2V4aWYvMS4wLyIKICAgIHhtbG5zOnRpZmY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vdGlmZi8xLjAvIgogICBleGlmOlBpeGVsWERpbWVuc2lvbj0iNzIiCiAgIGV4aWY6UGl4ZWxZRGltZW5zaW9uPSI3MiIKICAgdGlmZjpJbWFnZVdpZHRoPSI3MiIKICAgdGlmZjpJbWFnZUhlaWdodD0iNzIiCiAgIHRpZmY6T3JpZW50YXRpb249IjEiLz4KIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAKPD94cGFja2V0IGVuZD0idyI/Pq6cYi8AAAADc0JJVAgICNvhT+AAAAN7SURBVGje7dtRSBNhHADwfxJ3L96Le0kf1GD1sBDyO5ALbEkyMyY9bHswg+FDW5B7EKVhJSeElrQUcRIkFFHoi0toPriEVi8KbUQxKSYNk8HpYE5ot4e7e/l68NT08aTp6v9/25+P7+O3/3d3H3ffB7RooSSH7IQQYu0KS4qeeeEWyHbY+qLZvbbZiEcghBBHIJ43NhrQ4oYiRUU7sQ0lFJqPizbBEViUFCWfnOmyCp4ZaV/bfHLKIwiecLYUYJTSbLid2ALJX/E+q7VnUdGz0pSDOKakA39DQrQSd8RI0cqgCLEe8rZ55zb1X5oKwLAMywJoANpOI4ZhAEBdHnA6B5ZVPalqwHCckTGLAqvi69jPwZF36yrIK6GR4NrZjrbTbK2ziVsaeba0CaD+nAtOrtU6m6rY2qbazYWH08syqOtLwUcfoamjzpCsSPNPigy5bYQQIti7xuP6VaOshsV26052Uc/mE1M9DoEQQmxuMbyqGBvwBKUU/sUog380EIYwhCEMYQhD2DGMk4VCASuGMIQhDGEIQ9hxe0Af5eDyj7ejw5PRVAGgwnLNJ/qaK+HTnRZ/bF8rc9/s86umEoKpXyb8E+nWx7NP65nM+9HuB/5T5tc3zouzs/q7Ri0d6vdHLb5GU2lNxa0txuLq6aw3scDVNHZcrsjE0jKwnEmPQnQiVLg26KvnSmwqVjb3DjXvVC8djRVOtVbvGTbmh19utY55z7Cle/NQN94/8IcYl+iq2U19m55Mmb2d51ijnR45TP7yrPvmaME1NnZrrzjy1+mo1tBp6OI6DndF2Ji/f3s03Si+6r34p0FNRb5q50ULd4iuj7Bi8reR7uFUgzjYYYFcLpfL5WT9I0sm9l2rbjQfxnWEFcvFJsIZgEi/O3LgiaVmUluMubr8UN2fkGUZl1QIQxjCEIYwhCEMYYdbUuE+D4QhDGEIQxjC/luYvBK667zE8zx/oc0XXNK3B8vL0716tsX75IOe3fzwxNtyged5vuX6QGhFNThkUfakJ0Sb4H6RyFOqrIZ7rIInmqdUSQbsxDEez+5mI3lKpRm3YOuLSAql2fi4g9gDSUObZ4vy+o2tu/dmATiOBZA1UIEzcQDAMiaO+aPV9nbtKtfkwhWW4wBUWVOh3FTFsce2YnhSAk9K4EmJvxt4UgJPSuCSCmEIQxjCEAYAAL8BrebxGP8KiJcAAAAASUVORK5CYII=" alt="">` 1633 if out != expected { 1634 t.Errorf( 1635 "test failed;\ninput : %s\noutput : %s\nexpected: %s", 1636 input, 1637 out, 1638 expected) 1639 } 1640 1641 input = `<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAIAAADajyQQAAAAhnpUWHRSYXcgcHJvZmlsZSB0 1642eXBlIGV4aWYAAHjadY5LCsNADEP3c4oewb+R7eOUkEBv0OPXZpKmm76FLIQRGvv7dYxHwyTD 1643pgcSoMLSUp5lghZKxELct3RxXuVycsdDZRlkONn9aGd+MRWBw80dExs2qXbZlTVKu6hbqWfk 1644T8l30Z/8WvEBQsUsKBcOhtYAAAoCaVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8P3hwYWNr 1645ZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/Pgo8eDp4bXBt 1646ZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJYTVAgQ29yZSA0LjQuMC1F 1647eGl2MiI+CiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIv 1648MjItcmRmLXN5bnRheC1ucyMiPgogIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAg 1649ICB4bWxuczpleGlmPSJodHRwOi8vbnMuYWRvYmUuY29tL2V4aWYvMS4wLyIKICAgIHhtbG5z 1650OnRpZmY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vdGlmZi8xLjAvIgogICBleGlmOlBpeGVsWERp 1651bWVuc2lvbj0iNzIiCiAgIGV4aWY6UGl4ZWxZRGltZW5zaW9uPSI3MiIKICAgdGlmZjpJbWFn 1652ZVdpZHRoPSI3MiIKICAgdGlmZjpJbWFnZUhlaWdodD0iNzIiCiAgIHRpZmY6T3JpZW50YXRp 1653b249IjEiLz4KIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+CiAgICAgICAgICAgICAgICAgICAg 1654ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg 1655ICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAg 1656ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg 1657ICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg 1658ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg 1659ICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg 1660ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg 1661ICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg 1662ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAog 1663ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg 1664ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAg 1665ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg 1666ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAg 1667ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg 1668ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAg 1669ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg 1670ICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAg 1671ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg 1672ICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg 1673ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg 1674ICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg 1675ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg 1676ICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg 1677ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAg 1678ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg 1679ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAg 1680ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg 1681ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAg 1682ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg 1683ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAg 1684ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg 1685ICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg 1686ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg 1687ICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg 1688ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg 1689ICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg 1690ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg 1691IAogICAgICAgICAgICAgICAgICAgICAgICAgICAKPD94cGFja2V0IGVuZD0idyI/Pq6cYi8A 1692AAADc0JJVAgICNvhT+AAAAN7SURBVGje7dtRSBNhHADwfxJ3L96Le0kf1GD1sBDyO5ALbEky 1693MyY9bHswg+FDW5B7EKVhJSeElrQUcRIkFFHoi0toPriEVi8KbUQxKSYNk8HpYE5ot4e7e/l6 16948NT08aTp6v9/25+P7+O3/3d3H3ffB7RooSSH7IQQYu0KS4qeeeEWyHbY+qLZvbbZiEcghBBH 1695IJ43NhrQ4oYiRUU7sQ0lFJqPizbBEViUFCWfnOmyCp4ZaV/bfHLKIwiecLYUYJTSbLid2ALJ 1696X/E+q7VnUdGz0pSDOKakA39DQrQSd8RI0cqgCLEe8rZ55zb1X5oKwLAMywJoANpOI4ZhAEBd 1697HnA6B5ZVPalqwHCckTGLAqvi69jPwZF36yrIK6GR4NrZjrbTbK2ziVsaeba0CaD+nAtOrtU6 1698m6rY2qbazYWH08syqOtLwUcfoamjzpCsSPNPigy5bYQQIti7xuP6VaOshsV26052Uc/mE1M9 1699DoEQQmxuMbyqGBvwBKUU/sUog380EIYwhCEMYQhD2DGMk4VCASuGMIQhDGEIQ9hxe0Af5eDy 1700j7ejw5PRVAGgwnLNJ/qaK+HTnRZ/bF8rc9/s86umEoKpXyb8E+nWx7NP65nM+9HuB/5T5tc3 1701zouzs/q7Ri0d6vdHLb5GU2lNxa0txuLq6aw3scDVNHZcrsjE0jKwnEmPQnQiVLg26KvnSmwq 1702Vjb3DjXvVC8djRVOtVbvGTbmh19utY55z7Cle/NQN94/8IcYl+iq2U19m55Mmb2d51ijnR45 1703TP7yrPvmaME1NnZrrzjy1+mo1tBp6OI6DndF2Ji/f3s03Si+6r34p0FNRb5q50ULd4iuj7Bi 17048reR7uFUgzjYYYFcLpfL5WT9I0sm9l2rbjQfxnWEFcvFJsIZgEi/O3LgiaVmUluMubr8UN2f 1705kGUZl1QIQxjCEIYwhCEMYYdbUuE+D4QhDGEIQxjC/luYvBK667zE8zx/oc0XXNK3B8vL0716 1706tsX75IOe3fzwxNtyged5vuX6QGhFNThkUfakJ0Sb4H6RyFOqrIZ7rIInmqdUSQbsxDEez+5m 1707I3lKpRm3YOuLSAql2fi4g9gDSUObZ4vy+o2tu/dmATiOBZA1UIEzcQDAMiaO+aPV9nbtKtfk 1708whWW4wBUWVOh3FTFsce2YnhSAk9K4EmJvxt4UgJPSuCSCmEIQxjCEAYAAL8BrebxGP8KiJcA 1709AAAASUVORK5CYII=" alt="">` 1710 out = p.Sanitize(input) 1711 expected = `<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAIAAADajyQQAAAAhnpUWHRSYXcgcHJvZmlsZSB0eXBlIGV4aWYAAHjadY5LCsNADEP3c4oewb+R7eOUkEBv0OPXZpKmm76FLIQRGvv7dYxHwyTDpgcSoMLSUp5lghZKxELct3RxXuVycsdDZRlkONn9aGd+MRWBw80dExs2qXbZlTVKu6hbqWfkT8l30Z/8WvEBQsUsKBcOhtYAAAoCaVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/Pgo8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJYTVAgQ29yZSA0LjQuMC1FeGl2MiI+CiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICB4bWxuczpleGlmPSJodHRwOi8vbnMuYWRvYmUuY29tL2V4aWYvMS4wLyIKICAgIHhtbG5zOnRpZmY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vdGlmZi8xLjAvIgogICBleGlmOlBpeGVsWERpbWVuc2lvbj0iNzIiCiAgIGV4aWY6UGl4ZWxZRGltZW5zaW9uPSI3MiIKICAgdGlmZjpJbWFnZVdpZHRoPSI3MiIKICAgdGlmZjpJbWFnZUhlaWdodD0iNzIiCiAgIHRpZmY6T3JpZW50YXRpb249IjEiLz4KIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAKPD94cGFja2V0IGVuZD0idyI/Pq6cYi8AAAADc0JJVAgICNvhT+AAAAN7SURBVGje7dtRSBNhHADwfxJ3L96Le0kf1GD1sBDyO5ALbEkyMyY9bHswg+FDW5B7EKVhJSeElrQUcRIkFFHoi0toPriEVi8KbUQxKSYNk8HpYE5ot4e7e/l68NT08aTp6v9/25+P7+O3/3d3H3ffB7RooSSH7IQQYu0KS4qeeeEWyHbY+qLZvbbZiEcghBBHIJ43NhrQ4oYiRUU7sQ0lFJqPizbBEViUFCWfnOmyCp4ZaV/bfHLKIwiecLYUYJTSbLid2ALJX/E+q7VnUdGz0pSDOKakA39DQrQSd8RI0cqgCLEe8rZ55zb1X5oKwLAMywJoANpOI4ZhAEBdHnA6B5ZVPalqwHCckTGLAqvi69jPwZF36yrIK6GR4NrZjrbTbK2ziVsaeba0CaD+nAtOrtU6m6rY2qbazYWH08syqOtLwUcfoamjzpCsSPNPigy5bYQQIti7xuP6VaOshsV26052Uc/mE1M9DoEQQmxuMbyqGBvwBKUU/sUog380EIYwhCEMYQhD2DGMk4VCASuGMIQhDGEIQ9hxe0Af5eDyj7ejw5PRVAGgwnLNJ/qaK+HTnRZ/bF8rc9/s86umEoKpXyb8E+nWx7NP65nM+9HuB/5T5tc3zouzs/q7Ri0d6vdHLb5GU2lNxa0txuLq6aw3scDVNHZcrsjE0jKwnEmPQnQiVLg26KvnSmwqVjb3DjXvVC8djRVOtVbvGTbmh19utY55z7Cle/NQN94/8IcYl+iq2U19m55Mmb2d51ijnR45TP7yrPvmaME1NnZrrzjy1+mo1tBp6OI6DndF2Ji/f3s03Si+6r34p0FNRb5q50ULd4iuj7Bi8reR7uFUgzjYYYFcLpfL5WT9I0sm9l2rbjQfxnWEFcvFJsIZgEi/O3LgiaVmUluMubr8UN2fkGUZl1QIQxjCEIYwhCEMYYdbUuE+D4QhDGEIQxjC/luYvBK667zE8zx/oc0XXNK3B8vL0716tsX75IOe3fzwxNtyged5vuX6QGhFNThkUfakJ0Sb4H6RyFOqrIZ7rIInmqdUSQbsxDEez+5mI3lKpRm3YOuLSAql2fi4g9gDSUObZ4vy+o2tu/dmATiOBZA1UIEzcQDAMiaO+aPV9nbtKtfkwhWW4wBUWVOh3FTFsce2YnhSAk9K4EmJvxt4UgJPSuCSCmEIQxjCEAYAAL8BrebxGP8KiJcAAAAASUVORK5CYII=" alt="">` 1712 if out != expected { 1713 t.Errorf( 1714 "test failed;\ninput : %s\noutput : %s\nexpected: %s", 1715 input, 1716 out, 1717 expected) 1718 } 1719} 1720 1721func TestIssue55ScriptTags(t *testing.T) { 1722 p1 := NewPolicy() 1723 p2 := UGCPolicy() 1724 p3 := UGCPolicy().AllowElements("script").AllowUnsafe(true) 1725 1726 in := `<SCRIPT>document.write('<h1><header/h1>')</SCRIPT>` 1727 expected := `` 1728 out := p1.Sanitize(in) 1729 if out != expected { 1730 t.Errorf( 1731 "test failed;\ninput : %s\noutput : %s\nexpected: %s", 1732 in, 1733 out, 1734 expected, 1735 ) 1736 } 1737 1738 expected = `` 1739 out = p2.Sanitize(in) 1740 if out != expected { 1741 t.Errorf( 1742 "test failed;\ninput : %s\noutput : %s\nexpected: %s", 1743 in, 1744 out, 1745 expected, 1746 ) 1747 } 1748 1749 expected = `<script>document.write('<h1><header/h1>')</script>` 1750 out = p3.Sanitize(in) 1751 if out != expected { 1752 t.Errorf( 1753 "test failed;\ninput : %s\noutput : %s\nexpected: %s", 1754 in, 1755 out, 1756 expected, 1757 ) 1758 } 1759} 1760 1761func TestIssue85NoReferrer(t *testing.T) { 1762 p := UGCPolicy() 1763 p.AllowAttrs("rel").OnElements("a") 1764 p.RequireNoReferrerOnLinks(true) 1765 p.AddTargetBlankToFullyQualifiedLinks(true) 1766 p.AllowAttrs("target").Matching(Paragraph).OnElements("a") 1767 1768 tests := []test{ 1769 { 1770 in: `<a href="/path" />`, 1771 expected: `<a href="/path" rel="nofollow noreferrer"/>`, 1772 }, 1773 { 1774 in: `<a href="/path" target="_blank" />`, 1775 expected: `<a href="/path" target="_blank" rel="nofollow noreferrer noopener"/>`, 1776 }, 1777 { 1778 in: `<a href="/path" target="foo" />`, 1779 expected: `<a href="/path" target="foo" rel="nofollow noreferrer"/>`, 1780 }, 1781 { 1782 in: `<a href="https://www.google.com/" />`, 1783 expected: `<a href="https://www.google.com/" rel="nofollow noreferrer noopener" target="_blank"/>`, 1784 }, 1785 { 1786 in: `<a href="https://www.google.com/" target="_blank"/>`, 1787 expected: `<a href="https://www.google.com/" target="_blank" rel="nofollow noreferrer noopener"/>`, 1788 }, 1789 { 1790 in: `<a href="https://www.google.com/" rel="nofollow"/>`, 1791 expected: `<a href="https://www.google.com/" rel="nofollow noreferrer noopener" target="_blank"/>`, 1792 }, 1793 { 1794 in: `<a href="https://www.google.com/" rel="noopener"/>`, 1795 expected: `<a href="https://www.google.com/" rel="noopener nofollow noreferrer" target="_blank"/>`, 1796 }, 1797 { 1798 in: `<a href="https://www.google.com/" rel="noopener nofollow" />`, 1799 expected: `<a href="https://www.google.com/" rel="noopener nofollow noreferrer" target="_blank"/>`, 1800 }, 1801 { 1802 in: `<a href="https://www.google.com/" target="foo" />`, 1803 expected: `<a href="https://www.google.com/" target="_blank" rel="nofollow noreferrer noopener"/>`, 1804 }, 1805 { 1806 in: `<a href="https://www.google.com/" rel="external"/>`, 1807 expected: `<a href="https://www.google.com/" rel="external nofollow noreferrer noopener" target="_blank"/>`, 1808 }, 1809 } 1810 1811 // These tests are run concurrently to enable the race detector to pick up 1812 // potential issues 1813 wg := sync.WaitGroup{} 1814 wg.Add(len(tests)) 1815 for ii, tt := range tests { 1816 go func(ii int, tt test) { 1817 out := p.Sanitize(tt.in) 1818 if out != tt.expected { 1819 t.Errorf( 1820 "test %d failed;\ninput : %s\noutput : %s\nexpected: %s", 1821 ii, 1822 tt.in, 1823 out, 1824 tt.expected, 1825 ) 1826 } 1827 wg.Done() 1828 }(ii, tt) 1829 } 1830 wg.Wait() 1831} 1832 1833func TestIssue107(t *testing.T) { 1834 p := UGCPolicy() 1835 p.RequireCrossOriginAnonymous(true) 1836 1837 tests := []test{ 1838 { 1839 in: `<img src="/path" />`, 1840 expected: `<img src="/path" crossorigin="anonymous"/>`, 1841 }, 1842 { 1843 in: `<img src="/path" crossorigin="use-credentials"/>`, 1844 expected: `<img src="/path" crossorigin="anonymous"/>`, 1845 }, 1846 { 1847 in: `<img src="/path" crossorigin=""/>`, 1848 expected: `<img src="/path" crossorigin="anonymous"/>`, 1849 }, 1850 } 1851 1852 // These tests are run concurrently to enable the race detector to pick up 1853 // potential issues 1854 wg := sync.WaitGroup{} 1855 wg.Add(len(tests)) 1856 for ii, tt := range tests { 1857 go func(ii int, tt test) { 1858 out := p.Sanitize(tt.in) 1859 if out != tt.expected { 1860 t.Errorf( 1861 "test %d failed;\ninput : %s\noutput : %s\nexpected: %s", 1862 ii, 1863 tt.in, 1864 out, 1865 tt.expected, 1866 ) 1867 } 1868 wg.Done() 1869 }(ii, tt) 1870 } 1871 wg.Wait() 1872} 1873 1874func TestSanitizedURL(t *testing.T) { 1875 tests := []test{ 1876 { 1877 in: `http://abc.com?d=1&a=2&a=3`, 1878 expected: `http://abc.com?d=1&a=2&a=3`, 1879 }, 1880 { 1881 in: `http://abc.com?d=1 2&a=2&a=3`, 1882 expected: `http://abc.com?d=1+2&a=2&a=3`, 1883 }, 1884 { 1885 in: `http://abc.com?d=1/2&a=2&a=3`, 1886 expected: `http://abc.com?d=1%2F2&a=2&a=3`, 1887 }, 1888 { 1889 in: `http://abc.com?<d>=1&a=2&a=3`, 1890 expected: `http://abc.com?%26lt%3Bd%26gt%3B=1&a=2&a=3`, 1891 }, 1892 } 1893 1894 for _, theTest := range tests { 1895 res, err := sanitizedURL(theTest.in) 1896 if err != nil { 1897 t.Errorf("sanitizedURL returned error: %v", err) 1898 } 1899 if theTest.expected != res { 1900 t.Errorf( 1901 "test failed;\ninput : %s\nexpected: %s, actual: %s", 1902 theTest.in, 1903 theTest.expected, 1904 res, 1905 ) 1906 } 1907 } 1908} 1909 1910func TestIssue111ScriptTags(t *testing.T) { 1911 p1 := NewPolicy() 1912 p2 := UGCPolicy() 1913 p3 := UGCPolicy().AllowElements("script") 1914 1915 in := `<scr\u0130pt><script>alert(document.domain)</script>` 1916 expected := `<script>alert(document.domain)</script>` 1917 out := p1.Sanitize(in) 1918 if out != expected { 1919 t.Errorf( 1920 "test failed;\ninput : %s\noutput : %s\nexpected: %s", 1921 in, 1922 out, 1923 expected, 1924 ) 1925 } 1926 1927 expected = `<script>alert(document.domain)</script>` 1928 out = p2.Sanitize(in) 1929 if out != expected { 1930 t.Errorf( 1931 "test failed;\ninput : %s\noutput : %s\nexpected: %s", 1932 in, 1933 out, 1934 expected, 1935 ) 1936 } 1937 1938 expected = `<script>alert(document.domain)</script>` 1939 out = p3.Sanitize(in) 1940 if out != expected { 1941 t.Errorf( 1942 "test failed;\ninput : %s\noutput : %s\nexpected: %s", 1943 in, 1944 out, 1945 expected, 1946 ) 1947 } 1948} 1949 1950func TestQuotes(t *testing.T) { 1951 p := UGCPolicy() 1952 1953 tests := []test{ 1954 { 1955 in: `noquotes`, 1956 expected: `noquotes`, 1957 }, 1958 { 1959 in: `"singlequotes"`, 1960 expected: `"singlequotes"`, 1961 }, 1962 { 1963 in: `""doublequotes""`, 1964 expected: `""doublequotes""`, 1965 }, 1966 } 1967 1968 // These tests are run concurrently to enable the race detector to pick up 1969 // potential issues 1970 wg := sync.WaitGroup{} 1971 wg.Add(len(tests)) 1972 for ii, tt := range tests { 1973 go func(ii int, tt test) { 1974 out := p.Sanitize(tt.in) 1975 if out != tt.expected { 1976 t.Errorf( 1977 "test %d failed;\ninput : %s\noutput : %s\nexpected: %s", 1978 ii, 1979 tt.in, 1980 out, 1981 tt.expected, 1982 ) 1983 } 1984 wg.Done() 1985 }(ii, tt) 1986 } 1987 wg.Wait() 1988} 1989 1990func TestComments(t *testing.T) { 1991 p := UGCPolicy() 1992 1993 tests := []test{ 1994 { 1995 in: `1 <!-- 2 --> 3`, 1996 expected: `1 3`, 1997 }, 1998 { 1999 in: `<!--[if gte mso 9]>Hello<![endif]-->`, 2000 expected: ``, 2001 }, 2002 } 2003 2004 // These tests are run concurrently to enable the race detector to pick up 2005 // potential issues 2006 wg := sync.WaitGroup{} 2007 wg.Add(len(tests)) 2008 for ii, tt := range tests { 2009 go func(ii int, tt test) { 2010 out := p.Sanitize(tt.in) 2011 if out != tt.expected { 2012 t.Errorf( 2013 "test %d failed;\ninput : %s\noutput : %s\nexpected: %s", 2014 ii, 2015 tt.in, 2016 out, 2017 tt.expected, 2018 ) 2019 } 2020 wg.Done() 2021 }(ii, tt) 2022 } 2023 wg.Wait() 2024 2025 p.AllowComments() 2026 2027 tests = []test{ 2028 { 2029 in: `1 <!-- 2 --> 3`, 2030 expected: `1 <!-- 2 --> 3`, 2031 }, 2032 { 2033 in: `<!--[if gte mso 9]>Hello<![endif]-->`, 2034 expected: `<!--[if gte mso 9]>Hello<![endif]-->`, 2035 }, 2036 } 2037 2038 // These tests are run concurrently to enable the race detector to pick up 2039 // potential issues 2040 wg = sync.WaitGroup{} 2041 wg.Add(len(tests)) 2042 for ii, tt := range tests { 2043 go func(ii int, tt test) { 2044 out := p.Sanitize(tt.in) 2045 if out != tt.expected { 2046 t.Errorf( 2047 "test %d failed;\ninput : %s\noutput : %s\nexpected: %s", 2048 ii, 2049 tt.in, 2050 out, 2051 tt.expected, 2052 ) 2053 } 2054 wg.Done() 2055 }(ii, tt) 2056 } 2057 wg.Wait() 2058} 2059 2060func TestDefaultStyleHandlers(t *testing.T) { 2061 2062 tests := []test{ 2063 { 2064 in: `<div style="nonexistentStyle: something;"></div>`, 2065 expected: `<div></div>`, 2066 }, 2067 { 2068 in: `<div style="aLiGn-cOntEnt: cEntEr;"></div>`, 2069 expected: `<div style="aLiGn-cOntEnt: cEntEr"></div>`, 2070 }, 2071 { 2072 in: `<div style="align-items: center;"></div>`, 2073 expected: `<div style="align-items: center"></div>`, 2074 }, 2075 { 2076 in: `<div style="align-self: center;"></div>`, 2077 expected: `<div style="align-self: center"></div>`, 2078 }, 2079 { 2080 in: `<div style="all: initial;"></div>`, 2081 expected: `<div style="all: initial"></div>`, 2082 }, 2083 { 2084 in: `<div style="animation: mymove 5s infinite;"></div><div` + 2085 ` style="animation: inherit;"></div>`, 2086 expected: `<div style="animation: mymove 5s infinite"></div><div` + 2087 ` style="animation: inherit"></div>`, 2088 }, 2089 { 2090 in: `<div style="animation-delay: 2s;"></div><div style=` + 2091 `"animation-delay: initial;"></div>`, 2092 expected: `<div style="animation-delay: 2s"></div><div style=` + 2093 `"animation-delay: initial"></div>`, 2094 }, 2095 { 2096 in: `<div style="animation-direction: alternate;"></div>`, 2097 expected: `<div style="animation-direction: alternate"></div>`, 2098 }, 2099 { 2100 in: `<div style="animation-duration: 2s;"></div><div style=` + 2101 `"animation-duration: initial;"></div>`, 2102 expected: `<div style="animation-duration: 2s"></div><div style=` + 2103 `"animation-duration: initial"></div>`, 2104 }, 2105 { 2106 in: `<div style="animation-fill-mode: forwards;"></div>`, 2107 expected: `<div style="animation-fill-mode: forwards"></div>`, 2108 }, 2109 { 2110 in: `<div style="animation-iteration-count: 4;"></div><div ` + 2111 `style="animation-iteration-count: inherit;"></div>`, 2112 expected: `<div style="animation-iteration-count: 4"></div><div ` + 2113 `style="animation-iteration-count: inherit"></div>`, 2114 }, 2115 { 2116 in: `<div style="animation-name: chuck;"></div><div style=` + 2117 `"animation-name: none"></div>`, 2118 expected: `<div style="animation-name: chuck"></div><div style=` + 2119 `"animation-name: none"></div>`, 2120 }, 2121 { 2122 in: `<div style="animation-play-state: running;"></div>`, 2123 expected: `<div style="animation-play-state: running"></div>`, 2124 }, 2125 { 2126 in: `<div style="animation-timing-function: ` + 2127 `cubic-bezier(1,1,1,1);"></div><div style=` + 2128 `"animation-timing-function: steps(2, start);"></div>`, 2129 expected: `<div style="animation-timing-function: ` + 2130 `cubic-bezier(1,1,1,1)"></div><div style=` + 2131 `"animation-timing-function: steps(2, start)"></div>`, 2132 }, 2133 { 2134 in: `<div style="backface-visibility: hidden"></div>`, 2135 expected: `<div style="backface-visibility: hidden"></div>`, 2136 }, 2137 { 2138 in: `<div style="background: lightblue ` + 2139 `url('https://img_tree.gif') no-repeat fixed center"></div>` + 2140 `<div style="background: initial"></div>`, 2141 expected: `<div style="background: lightblue ` + 2142 `url('https://img_tree.gif') no-repeat fixed center">` + 2143 `</div><div style="background: initial"></div>`, 2144 }, 2145 { 2146 in: `<div style="background-attachment: fixed"></div>`, 2147 expected: `<div style="background-attachment: fixed"></div>`, 2148 }, 2149 { 2150 in: `<div style="background-blend-mode: lighten"></div>`, 2151 expected: `<div style="background-blend-mode: lighten"></div>`, 2152 }, 2153 { 2154 in: `<div style="background-clip: padding-box"></div>`, 2155 expected: `<div style="background-clip: padding-box"></div>`, 2156 }, 2157 { 2158 in: `<div style="background-color: coral"></div>`, 2159 expected: `<div style="background-color: coral"></div>`, 2160 }, 2161 { 2162 in: `<div style="background-image: url('http://paper.gif')">` + 2163 `</div><div style="background-image: inherit"></div>`, 2164 expected: `<div style="background-image: ` + 2165 `url('http://paper.gif')"></div><div style="` + 2166 `background-image: inherit"></div>`, 2167 }, 2168 { 2169 in: `<div style="background-origin: content-box"></div>`, 2170 expected: `<div style="background-origin: content-box"></div>`, 2171 }, 2172 { 2173 in: `<div style="background-position: center"></div><div ` + 2174 `style="background-position: 20px 20px"></div>`, 2175 expected: `<div style="background-position: center"></div><div ` + 2176 `style="background-position: 20px 20px"></div>`, 2177 }, 2178 { 2179 in: `<div style="background-repeat: repeat-y"></div>`, 2180 expected: `<div style="background-repeat: repeat-y"></div>`, 2181 }, 2182 { 2183 in: `<div style="background-size: 300px 100px"></div><div ` + 2184 `style="background-size: initial"></div>`, 2185 expected: `<div style="background-size: 300px 100px"></div>` + 2186 `<div style="background-size: initial"></div>`, 2187 }, 2188 { 2189 in: `<div style="border: 4px dotted blue;"></div><div ` + 2190 `style="border: initial;"></div>`, 2191 expected: `<div style="border: 4px dotted blue"></div><div ` + 2192 `style="border: initial"></div>`, 2193 }, 2194 { 2195 in: `<div style="border-bottom: 4px dotted blue;"></div>` + 2196 `<div style="border-bottom: initial"></div>`, 2197 expected: `<div style="border-bottom: 4px dotted blue"></div>` + 2198 `<div style="border-bottom: initial"></div>`, 2199 }, 2200 { 2201 in: `<div style="border-bottom-color: blue;"></div>`, 2202 expected: `<div style="border-bottom-color: blue"></div>`, 2203 }, 2204 { 2205 in: `<div style="border-bottom-left-radius: 4px;"></div>` + 2206 `<div style="border-bottom-left-radius: initial"></div>`, 2207 expected: `<div style="border-bottom-left-radius: 4px"></div>` + 2208 `<div style="border-bottom-left-radius: initial"></div>`, 2209 }, 2210 { 2211 in: `<div style="border-bottom-right-radius: 40px 4px;">` + 2212 `</div>`, 2213 expected: `<div style="border-bottom-right-radius: 40px 4px">` + 2214 `</div>`, 2215 }, 2216 { 2217 in: `<div style="border-bottom-style: dotted;"></div>`, 2218 expected: `<div style="border-bottom-style: dotted"></div>`, 2219 }, 2220 { 2221 in: `<div style="border-bottom-width: thin;"></div>`, 2222 expected: `<div style="border-bottom-width: thin"></div>`, 2223 }, 2224 { 2225 in: `<div style="border-collapse: separate;"></div>`, 2226 expected: `<div style="border-collapse: separate"></div>`, 2227 }, 2228 { 2229 in: `<div style="border-color: coral;"></div>`, 2230 expected: `<div style="border-color: coral"></div>`, 2231 }, 2232 { 2233 in: `<div style="border-image: url(https://border.png) 30 ` + 2234 `round;"></div><div style="border-image: initial;"></div>`, 2235 expected: `<div style="border-image: url(https://border.png) 30 ` + 2236 `round"></div><div style="border-image: initial"></div>`, 2237 }, 2238 { 2239 in: `<div style="border-image-outset: 10px;"></div>`, 2240 expected: `<div style="border-image-outset: 10px"></div>`, 2241 }, 2242 { 2243 in: `<div style="border-image-repeat: repeat;"></div>`, 2244 expected: `<div style="border-image-repeat: repeat"></div>`, 2245 }, 2246 { 2247 in: `<div style="border-image-slice: 30%;"></div><div ` + 2248 `style="border-image-slice: fill;"></div><div style="` + 2249 `border-image-slice: 3% 3% 3% 3% 3%;"></div>`, 2250 expected: `<div style="border-image-slice: 30%"></div><div style` + 2251 `="border-image-slice: fill"></div><div></div>`, 2252 }, 2253 { 2254 in: `<div style="border-image-source: ` + 2255 `url(https://border.png);"></div>`, 2256 expected: `<div style="border-image-source: ` + 2257 `url(https://border.png)"></div>`, 2258 }, 2259 { 2260 in: `<div style="border-image-width: 10px;"></div>`, 2261 expected: `<div style="border-image-width: 10px"></div>`, 2262 }, 2263 { 2264 in: `<div style="border-left: 4px dotted blue;"></div>`, 2265 expected: `<div style="border-left: 4px dotted blue"></div>`, 2266 }, 2267 { 2268 in: `<div style="border-left-color: blue;"></div>`, 2269 expected: `<div style="border-left-color: blue"></div>`, 2270 }, 2271 { 2272 in: `<div style="border-left-style: dotted;"></div>`, 2273 expected: `<div style="border-left-style: dotted"></div>`, 2274 }, 2275 { 2276 in: `<div style="border-left-width: thin;"></div>`, 2277 expected: `<div style="border-left-width: thin"></div>`, 2278 }, 2279 { 2280 in: `<div style="border-radius: 25px;"></div><div style=` + 2281 `"border-radius: initial;"></div><div style="border-radius:` + 2282 ` 1px 1px 1px 1px 1px;"></div>`, 2283 expected: `<div style="border-radius: 25px"></div><div style=` + 2284 `"border-radius: initial"></div><div></div>`, 2285 }, 2286 { 2287 in: `<div style="border-left: 4px dotted blue;"></div>`, 2288 expected: `<div style="border-left: 4px dotted blue"></div>`, 2289 }, 2290 { 2291 in: `<div style="border-right-color: blue;"></div>`, 2292 expected: `<div style="border-right-color: blue"></div>`, 2293 }, 2294 { 2295 in: `<div style="border-right-style: dotted;"></div>`, 2296 expected: `<div style="border-right-style: dotted"></div>`, 2297 }, 2298 { 2299 in: `<div style="border-right-width: thin;"></div>`, 2300 expected: `<div style="border-right-width: thin"></div>`, 2301 }, 2302 { 2303 in: `<div style="border-spacing: 15px;"></div>`, 2304 expected: `<div style="border-spacing: 15px"></div>`, 2305 }, 2306 { 2307 in: `<div style="border-style: dotted;"></div><div style="` + 2308 `border-style: initial;"></div><div style="border-style: ` + 2309 `dotted dotted dotted dotted dotted;"></div>`, 2310 expected: `<div style="border-style: dotted"></div><div style=` + 2311 `"border-style: initial"></div><div></div>`, 2312 }, 2313 { 2314 in: `<div style="border-top: 4px dotted blue;"></div>`, 2315 expected: `<div style="border-top: 4px dotted blue"></div>`, 2316 }, 2317 { 2318 in: `<div style="border-top-color: blue;"></div>`, 2319 expected: `<div style="border-top-color: blue"></div>`, 2320 }, 2321 { 2322 in: `<div style="border-top-left-radius: 4px;"></div>`, 2323 expected: `<div style="border-top-left-radius: 4px"></div>`, 2324 }, 2325 { 2326 in: `<div style="border-top-right-radius: 40px 4px;"></div>`, 2327 expected: `<div style="border-top-right-radius: 40px 4px"></div>`, 2328 }, 2329 { 2330 in: `<div style="border-top-style: dotted;"></div>`, 2331 expected: `<div style="border-top-style: dotted"></div>`, 2332 }, 2333 { 2334 in: `<div style="border-top-width: thin;"></div>`, 2335 expected: `<div style="border-top-width: thin"></div>`, 2336 }, 2337 { 2338 in: `<div style="border-width: thin;"></div><div style="` + 2339 `border-width: initial;"></div><div style="border-width: ` + 2340 `thin thin thin thin thin;"></div>`, 2341 expected: `<div style="border-width: thin"></div><div style="` + 2342 `border-width: initial"></div><div></div>`, 2343 }, 2344 { 2345 in: `<div style="bottom: 10px;"></div><div style="bottom:` + 2346 ` auto;"></div>`, 2347 expected: `<div style="bottom: 10px"></div><div style="bottom:` + 2348 ` auto"></div>`, 2349 }, 2350 { 2351 in: `<div style="box-decoration-break: slice;"></div>`, 2352 expected: `<div style="box-decoration-break: slice"></div>`, 2353 }, 2354 { 2355 in: `<div style="box-shadow: 10px 10px #888888;"></div>` + 2356 `<div style="box-shadow: aa;"></div><div style="box-shadow: ` + 2357 `10px aa;"></div><div style="box-shadow: 10px;"></div><div ` + 2358 `style="box-shadow: 10px 10px aa;"></div>`, 2359 expected: `<div style="box-shadow: 10px 10px #888888"></div>` + 2360 `<div></div><div></div><div></div><div></div>`, 2361 }, 2362 { 2363 in: `<div style="box-sizing: border-box;"></div>`, 2364 expected: `<div style="box-sizing: border-box"></div>`, 2365 }, 2366 { 2367 in: `<div style="break-after: column;"></div>`, 2368 expected: `<div style="break-after: column"></div>`, 2369 }, 2370 { 2371 in: `<div style="break-before: column;"></div>`, 2372 expected: `<div style="break-before: column"></div>`, 2373 }, 2374 { 2375 in: `<div style="break-inside: avoid-column;"></div>`, 2376 expected: `<div style="break-inside: avoid-column"></div>`, 2377 }, 2378 { 2379 in: `<div style="caption-side: bottom;"></div>`, 2380 expected: `<div style="caption-side: bottom"></div>`, 2381 }, 2382 { 2383 in: `<div style="caret-color: red;"></div><div style=` + 2384 `"caret-color: rgb(2,2,2);"></div><div style="caret-color:` + 2385 ` rgba(2,2,2,0.5);"></div><div style="caret-color: ` + 2386 `hsl(2,2%,2%);"></div><div style="caret-color: ` + 2387 `hsla(2,2%,2%,0.5);"></div>`, 2388 expected: `<div style="caret-color: red"></div><div style=` + 2389 `"caret-color: rgb(2,2,2)"></div><div style="caret-color: ` + 2390 `rgba(2,2,2,0.5)"></div><div style="caret-color: ` + 2391 `hsl(2,2%,2%)"></div><div style="caret-color: ` + 2392 `hsla(2,2%,2%,0.5)"></div>`, 2393 }, 2394 { 2395 in: `<div style="clear: both;"></div>`, 2396 expected: `<div style="clear: both"></div>`, 2397 }, 2398 { 2399 in: `<div style="clip: rect(0px,60px,200px,0px);"></div>` + 2400 `<div style="clip: auto;"></div>`, 2401 expected: `<div style="clip: rect(0px,60px,200px,0px)"></div>` + 2402 `<div style="clip: auto"></div>`, 2403 }, 2404 { 2405 in: `<div style="color: red;"></div><div style="color: ` + 2406 `rgb(2,2,2);"></div><div style="color: rgba(2,2,2,0.5);">` + 2407 `</div><div style="color: hsl(2,2%,2%);"></div><div style="` + 2408 `color: hsla(2,2%,2%,0.5);"></div>`, 2409 expected: `<div style="color: red"></div><div style="color: ` + 2410 `rgb(2,2,2)"></div><div style="color: rgba(2,2,2,0.5)">` + 2411 `</div><div style="color: hsl(2,2%,2%)"></div><div style="` + 2412 `color: hsla(2,2%,2%,0.5)"></div>`, 2413 }, 2414 { 2415 in: `<div style="clear: both;"></div>`, 2416 expected: `<div style="clear: both"></div>`, 2417 }, 2418 { 2419 in: `<div style="column-count: 3;"></div><div style="` + 2420 `column-count: auto;"></div>`, 2421 expected: `<div style="column-count: 3"></div><div style="` + 2422 `column-count: auto"></div>`, 2423 }, 2424 { 2425 in: `<div style="column-fill: balance;"></div>`, 2426 expected: `<div style="column-fill: balance"></div>`, 2427 }, 2428 { 2429 in: `<div style="column-gap: 40px;"></div><div style="` + 2430 `column-gap: normal;"></div>`, 2431 expected: `<div style="column-gap: 40px"></div><div style="` + 2432 `column-gap: normal"></div>`, 2433 }, 2434 { 2435 in: `<div style="column-rule: 4px double #ff00ff;"></div>`, 2436 expected: `<div style="column-rule: 4px double #ff00ff"></div>`, 2437 }, 2438 { 2439 in: `<div style="column-rule-color: #ff00ff;"></div>`, 2440 expected: `<div style="column-rule-color: #ff00ff"></div>`, 2441 }, 2442 { 2443 in: `<div style="column-rule: red;"></div>`, 2444 expected: `<div style="column-rule: red"></div>`, 2445 }, 2446 { 2447 in: `<div style="column-rule-width: 4px;"></div>`, 2448 expected: `<div style="column-rule-width: 4px"></div>`, 2449 }, 2450 { 2451 in: `<div style="column-span: all;"></div>`, 2452 expected: `<div style="column-span: all"></div>`, 2453 }, 2454 { 2455 in: `<div style="column-width: 4px;"></div><div style="` + 2456 `column-width: auto;"></div>`, 2457 expected: `<div style="column-width: 4px"></div><div style="` + 2458 `column-width: auto"></div>`, 2459 }, 2460 { 2461 in: `<div style="columns: 4px 3"></div><div style="` + 2462 `columns: auto"></div>`, 2463 expected: `<div style="columns: 4px 3"></div><div style="` + 2464 `columns: auto"></div>`, 2465 }, 2466 { 2467 in: `<div style="cursor: alias"></div>`, 2468 expected: `<div style="cursor: alias"></div>`, 2469 }, 2470 { 2471 in: `<div style="direction: rtl"></div>`, 2472 expected: `<div style="direction: rtl"></div>`, 2473 }, 2474 { 2475 in: `<div style="display: block"></div>`, 2476 expected: `<div style="display: block"></div>`, 2477 }, 2478 { 2479 in: `<div style="empty-cells: hide"></div>`, 2480 expected: `<div style="empty-cells: hide"></div>`, 2481 }, 2482 { 2483 in: `<div style="filter: grayscale(100%)"></div><div style` + 2484 `="filter: sepia(100%)"></div>`, 2485 expected: `<div style="filter: grayscale(100%)"></div><div style` + 2486 `="filter: sepia(100%)"></div>`, 2487 }, 2488 { 2489 in: `<div style="flex: 1"></div><div style="flex: auto">` + 2490 `</div>`, 2491 expected: `<div style="flex: 1"></div><div style="flex: auto">` + 2492 `</div>`, 2493 }, 2494 { 2495 in: `<div style="flex-basis: 10px"></div><div style="` + 2496 `flex-basis: auto"></div>`, 2497 expected: `<div style="flex-basis: 10px"></div><div style="` + 2498 `flex-basis: auto"></div>`, 2499 }, 2500 { 2501 in: `<div style="flex-direction: row-reverse"></div>`, 2502 expected: `<div style="flex-direction: row-reverse"></div>`, 2503 }, 2504 { 2505 in: `<div style="flex-flow: row-reverse wrap"></div><div ` + 2506 `style="flex-flow: initial"></div>`, 2507 expected: `<div style="flex-flow: row-reverse wrap"></div><div ` + 2508 `style="flex-flow: initial"></div>`, 2509 }, 2510 { 2511 in: `<div style="flex-grow: 1"></div><div style="flex-grow` + 2512 `: initial"></div>`, 2513 expected: `<div style="flex-grow: 1"></div><div style="flex-grow` + 2514 `: initial"></div>`, 2515 }, 2516 { 2517 in: `<div style="flex-shrink: 3"></div>`, 2518 expected: `<div style="flex-shrink: 3"></div>`, 2519 }, 2520 { 2521 in: `<div style="flex-wrap: wrap"></div>`, 2522 expected: `<div style="flex-wrap: wrap"></div>`, 2523 }, 2524 { 2525 in: `<div style="float: right"></div>`, 2526 expected: `<div style="float: right"></div>`, 2527 }, 2528 { 2529 in: `<div style="font: italic bold 12px/30px Georgia, serif` + 2530 `"></div><div style="font: icon"></div>`, 2531 expected: `<div style="font: italic bold 12px/30px Georgia, serif` + 2532 `"></div><div style="font: icon"></div>`, 2533 }, 2534 { 2535 in: `<div style="font-family: 'Times New Roman', Times, ` + 2536 `serif"></div><span style="font-family: comic sans ms, ` + 2537 `cursive, sans-serif;">aaaaaa</span></span>`, 2538 expected: `<div style="font-family: 'Times New Roman',` + 2539 ` Times, serif"></div><span style="font-family: comic sans` + 2540 ` ms, cursive, sans-serif">aaaaaa</span></span>`, 2541 }, 2542 { 2543 in: `<div style="font-kerning: normal"></div>`, 2544 expected: `<div style="font-kerning: normal"></div>`, 2545 }, 2546 { 2547 in: `<div style="font-language-override: normal"></div>`, 2548 expected: `<div style="font-language-override: normal"></div>`, 2549 }, 2550 { 2551 in: `<div style="font-size: large"></div>`, 2552 expected: `<div style="font-size: large"></div>`, 2553 }, 2554 { 2555 in: `<div style="font-size-adjust: 0.58"></div><div style="` + 2556 `font-size-adjust: auto"></div>`, 2557 expected: `<div style="font-size-adjust: 0.58"></div><div style="` + 2558 `font-size-adjust: auto"></div>`, 2559 }, 2560 { 2561 in: `<div style="font-stretch: expanded"></div>`, 2562 expected: `<div style="font-stretch: expanded"></div>`, 2563 }, 2564 { 2565 in: `<div style="font-style: italic"></div>`, 2566 expected: `<div style="font-style: italic"></div>`, 2567 }, 2568 { 2569 in: `<div style="font-synthesis: style"></div>`, 2570 expected: `<div style="font-synthesis: style"></div>`, 2571 }, 2572 { 2573 in: `<div style="font-variant: small-caps"></div>`, 2574 expected: `<div style="font-variant: small-caps"></div>`, 2575 }, 2576 { 2577 in: `<div style="font-variant-caps: small-caps"></div>`, 2578 expected: `<div style="font-variant-caps: small-caps"></div>`, 2579 }, 2580 { 2581 in: `<div style="font-variant-position: sub"></div>`, 2582 expected: `<div style="font-variant-position: sub"></div>`, 2583 }, 2584 { 2585 in: `<div style="font-weight: normal"></div>`, 2586 expected: `<div style="font-weight: normal"></div>`, 2587 }, 2588 { 2589 in: `<div style="grid: 150px / auto auto auto;"></div><div ` + 2590 `style="grid: none;"></div>`, 2591 expected: `<div style="grid: 150px / auto auto auto"></div><div ` + 2592 `style="grid: none"></div>`, 2593 }, 2594 { 2595 in: `<div style="grid-area: 2 / 1 / span 2 / span 3;">` + 2596 `</div>`, 2597 expected: `<div style="grid-area: 2 / 1 / span 2 / span 3">` + 2598 `</div>`, 2599 }, 2600 { 2601 in: `<div style="grid-auto-columns: 150px;"></div>` + 2602 `<div style="grid-auto-columns: auto;"></div>`, 2603 expected: `<div style="grid-auto-columns: 150px"></div>` + 2604 `<div style="grid-auto-columns: auto"></div>`, 2605 }, 2606 { 2607 in: `<div style="grid-auto-flow: column;"></div>`, 2608 expected: `<div style="grid-auto-flow: column"></div>`, 2609 }, 2610 { 2611 in: `<div style="grid-auto-rows: 150px;"></div>`, 2612 expected: `<div style="grid-auto-rows: 150px"></div>`, 2613 }, 2614 { 2615 in: `<div style="grid-column: 1 / span 2;"></div>`, 2616 expected: `<div style="grid-column: 1 / span 2"></div>`, 2617 }, 2618 { 2619 in: `<div style="grid-column-end: span 2;"></div>` + 2620 `<div style="grid-column-end: auto;"></div>`, 2621 expected: `<div style="grid-column-end: span 2"></div>` + 2622 `<div style="grid-column-end: auto"></div>`, 2623 }, 2624 { 2625 in: `<div style="grid-column-gap: 10px;"></div>`, 2626 expected: `<div style="grid-column-gap: 10px"></div>`, 2627 }, 2628 { 2629 in: `<div style="grid-column-start: 1;"></div>`, 2630 expected: `<div style="grid-column-start: 1"></div>`, 2631 }, 2632 { 2633 in: `<div style="grid-gap: 1px;"></div><div style=` + 2634 `"grid-gap: 1px 1px 1px;"></div>`, 2635 expected: `<div style="grid-gap: 1px"></div><div></div>`, 2636 }, 2637 { 2638 in: `<div style="grid-row: 1 / span 2;"></div>`, 2639 expected: `<div style="grid-row: 1 / span 2"></div>`, 2640 }, 2641 { 2642 in: `<div style="grid-row-end: span 2;"></div>`, 2643 expected: `<div style="grid-row-end: span 2"></div>`, 2644 }, 2645 { 2646 in: `<div style="grid-row-gap: 10px;"></div>`, 2647 expected: `<div style="grid-row-gap: 10px"></div>`, 2648 }, 2649 { 2650 in: `<div style="grid-row-start: 1;"></div>`, 2651 expected: `<div style="grid-row-start: 1"></div>`, 2652 }, 2653 { 2654 in: `<div style="grid-template: 150px / auto auto auto;">` + 2655 `</div><div style="grid-template: none"></div><div style="` + 2656 `grid-template: a / a / a"></div>`, 2657 expected: `<div style="grid-template: 150px / auto auto auto">` + 2658 `</div><div style="grid-template: none"></div><div></div>`, 2659 }, 2660 { 2661 in: `<div style="grid-template-areas: none;"></div><div ` + 2662 `style="grid-template-areas: 'Billy'"></div>`, 2663 expected: `<div style="grid-template-areas: none"></div>` + 2664 `<div style="grid-template-areas: 'Billy'"></div>`, 2665 }, 2666 { 2667 in: `<div style="grid-template-columns: auto auto auto` + 2668 ` auto auto;"></div>`, 2669 expected: `<div style="grid-template-columns: auto auto` + 2670 ` auto auto auto"></div>`, 2671 }, 2672 { 2673 in: `<div style="grid-template-rows: 150px 150px">` + 2674 `</div><div style="grid-template-rows: aaaa aaaaa"></div>`, 2675 expected: `<div style="grid-template-rows: 150px 150px">` + 2676 `</div><div></div>`, 2677 }, 2678 { 2679 in: `<div style="hanging-punctuation: first;"></div>`, 2680 expected: `<div style="hanging-punctuation: first"></div>`, 2681 }, 2682 { 2683 in: `<div style="height: 50px;"></div><div style="height: ` + 2684 `auto;"></div>`, 2685 expected: `<div style="height: 50px"></div><div style="height: ` + 2686 `auto"></div>`, 2687 }, 2688 { 2689 in: `<div style="hyphens: manual;"></div>`, 2690 expected: `<div style="hyphens: manual"></div>`, 2691 }, 2692 { 2693 in: `<div style="isolation: isolate;"></div>`, 2694 expected: `<div style="isolation: isolate"></div>`, 2695 }, 2696 { 2697 in: `<div style="image-rendering: smooth;"></div>`, 2698 expected: `<div style="image-rendering: smooth"></div>`, 2699 }, 2700 { 2701 in: `<div style="justify-content: center;"></div>`, 2702 expected: `<div style="justify-content: center"></div>`, 2703 }, 2704 { 2705 in: `<div style="left: 150px;"></div>`, 2706 expected: `<div style="left: 150px"></div>`, 2707 }, 2708 { 2709 in: `<div style="letter-spacing: -3px;"></div><div style` + 2710 `="letter-spacing: normal;"></div>`, 2711 expected: `<div style="letter-spacing: -3px"></div><div style` + 2712 `="letter-spacing: normal"></div>`, 2713 }, 2714 { 2715 in: `<div style="line-break: auto"></div>`, 2716 expected: `<div style="line-break: auto"></div>`, 2717 }, 2718 { 2719 in: `<div style="line-height: 1.6;"></div><div style=` + 2720 `"line-height: normal;"></div>`, 2721 expected: `<div style="line-height: 1.6"></div><div style=` + 2722 `"line-height: normal"></div>`, 2723 }, 2724 { 2725 in: `<div style="list-style: square inside ` + 2726 `url(http://sqpurple.gif);"></div><div style="list-style: ` + 2727 `initial"></div>`, 2728 expected: `<div style="list-style: square inside ` + 2729 `url(http://sqpurple.gif)"></div><div style="list-style: ` + 2730 `initial"></div>`, 2731 }, 2732 { 2733 in: `<div style="list-style-image: ` + 2734 `url(http://sqpurple.gif);"></div>`, 2735 expected: `<div style="list-style-image: ` + 2736 `url(http://sqpurple.gif)"></div>`, 2737 }, 2738 { 2739 in: `<div style="list-style-position: inside;"></div>`, 2740 expected: `<div style="list-style-position: inside"></div>`, 2741 }, 2742 { 2743 in: `<div style="list-style-type: square;"></div>`, 2744 expected: `<div style="list-style-type: square"></div>`, 2745 }, 2746 { 2747 in: `<div style="margin: 150px;"></div><div style="margin:` + 2748 ` auto;"></div>`, 2749 expected: `<div style="margin: 150px"></div><div style="margin:` + 2750 ` auto"></div>`, 2751 }, 2752 { 2753 in: `<div style="margin-bottom: 150px;"></div><div ` + 2754 `style="margin-bottom: auto;"></div>`, 2755 expected: `<div style="margin-bottom: 150px"></div><div ` + 2756 `style="margin-bottom: auto"></div>`, 2757 }, 2758 { 2759 in: `<div style="margin-left: 150px;"></div>`, 2760 expected: `<div style="margin-left: 150px"></div>`, 2761 }, 2762 { 2763 in: `<div style="margin-right: 150px;"></div>`, 2764 expected: `<div style="margin-right: 150px"></div>`, 2765 }, 2766 { 2767 in: `<div style="margin-top: 150px;"></div>`, 2768 expected: `<div style="margin-top: 150px"></div>`, 2769 }, 2770 { 2771 in: `<div style="max-height: 150px;"></div><div style=` + 2772 `"max-height: initial;"></div>`, 2773 expected: `<div style="max-height: 150px"></div><div style=` + 2774 `"max-height: initial"></div>`, 2775 }, 2776 { 2777 in: `<div style="max-width: 150px;"></div>`, 2778 expected: `<div style="max-width: 150px"></div>`, 2779 }, 2780 { 2781 in: `<div style="min-height: 150px;"></div><div style=` + 2782 `"min-height: initial;"></div>`, 2783 expected: `<div style="min-height: 150px"></div><div style=` + 2784 `"min-height: initial"></div>`, 2785 }, 2786 { 2787 in: `<div style="min-width: 150px;"></div>`, 2788 expected: `<div style="min-width: 150px"></div>`, 2789 }, 2790 { 2791 in: `<div style="mix-blend-mode: darken;"></div>`, 2792 expected: `<div style="mix-blend-mode: darken"></div>`, 2793 }, 2794 { 2795 in: `<div style="object-fit: cover;"></div>`, 2796 expected: `<div style="object-fit: cover"></div>`, 2797 }, 2798 { 2799 in: `<div style="object-position: 5px 10%;"></div><div ` + 2800 `style="object-position: initial"></div><div style="` + 2801 `object-position: 5px 10% 5px;"></div>`, 2802 expected: `<div style="object-position: 5px 10%"></div><div ` + 2803 `style="object-position: initial"></div><div></div>`, 2804 }, 2805 { 2806 in: `<div style="opacity: 0.5;"></div><div style="opacity:` + 2807 ` initial"></div>`, 2808 expected: `<div style="opacity: 0.5"></div><div style="opacity:` + 2809 ` initial"></div>`, 2810 }, 2811 { 2812 in: `<div style="order: 2;"></div><div style="order: ` + 2813 `initial"></div>`, 2814 expected: `<div style="order: 2"></div><div style="order: ` + 2815 `initial"></div>`, 2816 }, 2817 { 2818 in: `<div style="outline: 2px dashed blue;"></div><div ` + 2819 `style="outline: initial"></div>`, 2820 expected: `<div style="outline: 2px dashed blue"></div><div ` + 2821 `style="outline: initial"></div>`, 2822 }, 2823 { 2824 in: `<div style="outline-color: blue;"></div>`, 2825 expected: `<div style="outline-color: blue"></div>`, 2826 }, 2827 { 2828 in: `<div style="outline-offset: 2px;"></div><div ` + 2829 `style="outline-offset: initial;"></div>`, 2830 expected: `<div style="outline-offset: 2px"></div><div ` + 2831 `style="outline-offset: initial"></div>`, 2832 }, 2833 { 2834 in: `<div style="outline-style: dashed;"></div>`, 2835 expected: `<div style="outline-style: dashed"></div>`, 2836 }, 2837 { 2838 in: `<div style="outline-width: thick;"></div>`, 2839 expected: `<div style="outline-width: thick"></div>`, 2840 }, 2841 { 2842 in: `<div style="overflow: scroll;"></div>`, 2843 expected: `<div style="overflow: scroll"></div>`, 2844 }, 2845 { 2846 in: `<div style="overflow-x: scroll;"></div>`, 2847 expected: `<div style="overflow-x: scroll"></div>`, 2848 }, 2849 { 2850 in: `<div style="overflow-y: scroll;"></div>`, 2851 expected: `<div style="overflow-y: scroll"></div>`, 2852 }, 2853 { 2854 in: `<div style="overflow-wrap: anywhere;"></div>`, 2855 expected: `<div style="overflow-wrap: anywhere"></div>`, 2856 }, 2857 { 2858 in: `<div style="orphans: 2;"></div>`, 2859 expected: `<div style="orphans: 2"></div>`, 2860 }, 2861 { 2862 in: `<div style="padding: 55px;"></div>`, 2863 expected: `<div style="padding: 55px"></div>`, 2864 }, 2865 { 2866 in: `<div style="padding-bottom: 55px;"></div><div style` + 2867 `="padding-bottom: initial;"></div>`, 2868 expected: `<div style="padding-bottom: 55px"></div><div style=` + 2869 `"padding-bottom: initial"></div>`, 2870 }, 2871 { 2872 in: `<div style="padding-left: 55px;"></div>`, 2873 expected: `<div style="padding-left: 55px"></div>`, 2874 }, 2875 { 2876 in: `<div style="padding-right: 55px;"></div>`, 2877 expected: `<div style="padding-right: 55px"></div>`, 2878 }, 2879 { 2880 in: `<div style="padding-top: 55px;"></div>`, 2881 expected: `<div style="padding-top: 55px"></div>`, 2882 }, 2883 { 2884 in: `<div style="page-break-after: always;"></div>`, 2885 expected: `<div style="page-break-after: always"></div>`, 2886 }, 2887 { 2888 in: `<div style="page-break-before: always;"></div>`, 2889 expected: `<div style="page-break-before: always"></div>`, 2890 }, 2891 { 2892 in: `<div style="page-break-inside: avoid;"></div>`, 2893 expected: `<div style="page-break-inside: avoid"></div>`, 2894 }, 2895 { 2896 in: `<div style="perspective: 100px;"></div><div style=` + 2897 `"perspective: none;"></div>`, 2898 expected: `<div style="perspective: 100px"></div><div style=` + 2899 `"perspective: none"></div>`, 2900 }, 2901 { 2902 in: `<div style="perspective-origin: left;"></div>`, 2903 expected: `<div style="perspective-origin: left"></div>`, 2904 }, 2905 { 2906 in: `<div style="pointer-events: auto;"></div>`, 2907 expected: `<div style="pointer-events: auto"></div>`, 2908 }, 2909 { 2910 in: `<div style="position: absolute;"></div>`, 2911 expected: `<div style="position: absolute"></div>`, 2912 }, 2913 { 2914 in: `<div style="quotes: '‹' '›';"></div>`, 2915 expected: `<div style="quotes: '‹' '›'"></div>`, 2916 }, 2917 { 2918 in: `<div style="resize: both;"></div>`, 2919 expected: `<div style="resize: both"></div>`, 2920 }, 2921 { 2922 in: `<div style="right: 10px;"></div>`, 2923 expected: `<div style="right: 10px"></div>`, 2924 }, 2925 { 2926 in: `<div style="scroll-behavior: smooth;"></div>`, 2927 expected: `<div style="scroll-behavior: smooth"></div>`, 2928 }, 2929 { 2930 in: `<div style="tab-size: 16;"></div><div style="tab-size:` + 2931 ` initial;"></div>`, 2932 expected: `<div style="tab-size: 16"></div><div style="tab-size:` + 2933 ` initial"></div>`, 2934 }, 2935 { 2936 in: `<div style="table-layout: fixed;"></div>`, 2937 expected: `<div style="table-layout: fixed"></div>`, 2938 }, 2939 { 2940 in: `<div style="text-align: justify;"></div>`, 2941 expected: `<div style="text-align: justify"></div>`, 2942 }, 2943 { 2944 in: `<div style="text-align-last: justify;"></div>`, 2945 expected: `<div style="text-align-last: justify"></div>`, 2946 }, 2947 { 2948 in: `<div style="text-combine-upright: none;"></div><div` + 2949 ` style="text-combine-upright: digits 2"></div>`, 2950 expected: `<div style="text-combine-upright: none"></div><div ` + 2951 `style="text-combine-upright: digits 2"></div>`, 2952 }, 2953 { 2954 in: `<div style="text-decoration: underline underline;">` + 2955 `</div><div style="text-decoration: initial"></div>`, 2956 expected: `<div style="text-decoration: underline underline">` + 2957 `</div><div style="text-decoration: initial"></div>`, 2958 }, 2959 { 2960 in: `<div style="text-decoration-color: red;"></div>`, 2961 expected: `<div style="text-decoration-color: red"></div>`, 2962 }, 2963 { 2964 in: `<div style="text-decoration-line: underline ` + 2965 `underline;"></div>`, 2966 expected: `<div style="text-decoration-line: underline ` + 2967 `underline"></div>`, 2968 }, 2969 { 2970 in: `<div style="text-decoration-style: solid;"></div>`, 2971 expected: `<div style="text-decoration-style: solid"></div>`, 2972 }, 2973 { 2974 in: `<div style="text-indent: 30%;"></div><div style=` + 2975 `"text-indent: initial"></div>`, 2976 expected: `<div style="text-indent: 30%"></div><div style=` + 2977 `"text-indent: initial"></div>`, 2978 }, 2979 { 2980 in: `<div style="text-orientation: mixed"></div>`, 2981 expected: `<div style="text-orientation: mixed"></div>`, 2982 }, 2983 { 2984 in: `<div style="text-justify: inter-word;"></div>`, 2985 expected: `<div style="text-justify: inter-word"></div>`, 2986 }, 2987 { 2988 in: `<div style="text-overflow: ellipsis;"></div><div ` + 2989 `style="text-overflow: 'something'"></div>`, 2990 expected: `<div style="text-overflow: ellipsis"></div><div ` + 2991 `style="text-overflow: 'something'"></div>`, 2992 }, 2993 { 2994 in: `<div style="text-shadow: 2px 2px #ff0000;"></div>`, 2995 expected: `<div style="text-shadow: 2px 2px #ff0000"></div>`, 2996 }, 2997 { 2998 in: `<div style="text-transform: uppercase;"></div>`, 2999 expected: `<div style="text-transform: uppercase"></div>`, 3000 }, 3001 { 3002 in: `<div style="top: 150px;"></div>`, 3003 expected: `<div style="top: 150px"></div>`, 3004 }, 3005 { 3006 in: `<div style="transform: scaleY(1.5);"></div><div ` + 3007 `style="transform: perspective(20px);"></div>`, 3008 expected: `<div style="transform: scaleY(1.5)"></div><div ` + 3009 `style="transform: perspective(20px)"></div>`, 3010 }, 3011 { 3012 in: `<div style="transform-origin: 40% 40%;"></div>`, 3013 expected: `<div style="transform-origin: 40% 40%"></div>`, 3014 }, 3015 { 3016 in: `<div style="transform-style: preserve-3d;"></div>`, 3017 expected: `<div style="transform-style: preserve-3d"></div>`, 3018 }, 3019 { 3020 in: `<div style="transition: width 2s;"></div>`, 3021 expected: `<div style="transition: width 2s"></div>`, 3022 }, 3023 { 3024 in: `<div style="transition-delay: 2s;"></div><div ` + 3025 `style="transition-delay: initial;"></div>`, 3026 expected: `<div style="transition-delay: 2s"></div><div ` + 3027 `style="transition-delay: initial"></div>`, 3028 }, 3029 { 3030 in: `<div style="transition-duration: 2s;"></div><div ` + 3031 `style="transition-duration: initial;"></div>`, 3032 expected: `<div style="transition-duration: 2s"></div><div ` + 3033 `style="transition-duration: initial"></div>`, 3034 }, 3035 { 3036 in: `<div style="transition-property: width;"></div><div ` + 3037 `style="transition-property: initial;"></div>`, 3038 expected: `<div style="transition-property: width"></div><div ` + 3039 `style="transition-property: initial"></div>`, 3040 }, 3041 { 3042 in: `<div style="transition-timing-function: linear;">` + 3043 `</div>`, 3044 expected: `<div style="transition-timing-function: linear">` + 3045 `</div>`, 3046 }, 3047 { 3048 in: `<div style="unicode-bidi: bidi-override;"></div>`, 3049 expected: `<div style="unicode-bidi: bidi-override"></div>`, 3050 }, 3051 { 3052 in: `<div style="user-select: none;"></div>`, 3053 expected: `<div style="user-select: none"></div>`, 3054 }, 3055 { 3056 in: `<div style="vertical-align: text-bottom;"></div>`, 3057 expected: `<div style="vertical-align: text-bottom"></div>`, 3058 }, 3059 { 3060 in: `<div style="visibility: visible;"></div>`, 3061 expected: `<div style="visibility: visible"></div>`, 3062 }, 3063 { 3064 in: `<div style="white-space: normal;"></div>`, 3065 expected: `<div style="white-space: normal"></div>`, 3066 }, 3067 { 3068 in: `<div style="width: 130px;"></div><div style="width: ` + 3069 `auto;"></div>`, 3070 expected: `<div style="width: 130px"></div><div style="width: ` + 3071 `auto"></div>`, 3072 }, 3073 { 3074 in: `<div style="word-break: break-all;"></div>`, 3075 expected: `<div style="word-break: break-all"></div>`, 3076 }, 3077 { 3078 in: `<div style="word-spacing: 30px;"></div><div style=` + 3079 `"word-spacing: normal"></div>`, 3080 expected: `<div style="word-spacing: 30px"></div><div style=` + 3081 `"word-spacing: normal"></div>`, 3082 }, 3083 { 3084 in: `<div style="word-wrap: break-word;"></div>`, 3085 expected: `<div style="word-wrap: break-word"></div>`, 3086 }, 3087 { 3088 in: `<div style="writing-mode: vertical-rl;"></div>`, 3089 expected: `<div style="writing-mode: vertical-rl"></div>`, 3090 }, 3091 { 3092 in: `<div style="z-index: -1;"></div><div style="z-index:` + 3093 ` auto;"></div>`, 3094 expected: `<div style="z-index: -1"></div><div style="z-index:` + 3095 ` auto"></div>`, 3096 }, 3097 } 3098 3099 p := UGCPolicy() 3100 p.AllowStyles("nonexistentStyle", "align-content", "align-items", 3101 "align-self", "all", "animation", "animation-delay", 3102 "animation-direction", "animation-duration", "animation-fill-mode", 3103 "animation-iteration-count", "animation-name", "animation-play-state", 3104 "animation-timing-function", "backface-visibility", "background", 3105 "background-attachment", "background-blend-mode", "background-clip", 3106 "background-color", "background-image", "background-origin", 3107 "background-position", "background-repeat", "background-size", 3108 "border", "border-bottom", "border-bottom-color", 3109 "border-bottom-left-radius", "border-bottom-right-radius", 3110 "border-bottom-style", "border-bottom-width", "border-collapse", 3111 "border-color", "border-image", "border-image-outset", 3112 "border-image-repeat", "border-image-slice", "border-image-source", 3113 "border-image-width", "border-left", "border-left-color", 3114 "border-left-style", "border-left-width", "border-radius", 3115 "border-right", "border-right-color", "border-right-style", 3116 "border-right-width", "border-spacing", "border-style", "border-top", 3117 "border-top-color", "border-top-left-radius", 3118 "border-top-right-radius", "border-top-style", "border-top-width", 3119 "border-width", "bottom", "box-decoration-break", "box-shadow", 3120 "box-sizing", "break-after", "break-before", "break-inside", 3121 "caption-side", "caret-color", "clear", "clip", "color", 3122 "column-count", "column-fill", "column-gap", "column-rule", 3123 "column-rule-color", "column-rule-style", "column-rule-width", 3124 "column-span", "column-width", "columns", "cursor", "direction", 3125 "display", "empty-cells", "filter", "flex", "flex-basis", 3126 "flex-direction", "flex-flow", "flex-grow", "flex-shrink", 3127 "flex-wrap", "float", "font", "font-family", "font-kerning", 3128 "font-language-override", "font-size", "font-size-adjust", 3129 "font-stretch", "font-style", "font-synthesis", "font-variant", 3130 "font-variant-caps", "font-variant-position", "font-weight", "grid", 3131 "grid-area", "grid-auto-columns", "grid-auto-flow", "grid-auto-rows", 3132 "grid-column", "grid-column-end", "grid-column-gap", 3133 "grid-column-start", "grid-gap", "grid-row", "grid-row-end", 3134 "grid-row-gap", "grid-row-start", "grid-template", 3135 "grid-template-areas", "grid-template-columns", "grid-template-rows", 3136 "hanging-punctuation", "height", "hyphens", "image-rendering", 3137 "isolation", "justify-content", "left", "letter-spacing", "line-break", 3138 "line-height", "list-style", "list-style-image", "list-style-position", 3139 "list-style-type", "margin", "margin-bottom", "margin-left", 3140 "margin-right", "margin-top", "max-height", "max-width", "min-height", 3141 "min-width", "mix-blend-mode", "object-fit", "object-position", 3142 "opacity", "order", "orphans", "outline", "outline-color", 3143 "outline-offset", "outline-style", "outline-width", "overflow", 3144 "overflow-wrap", "overflow-x", "overflow-y", "padding", 3145 "padding-bottom", "padding-left", "padding-right", "padding-top", 3146 "page-break-after", "page-break-before", "page-break-inside", 3147 "perspective", "perspective-origin", "pointer-events", "position", 3148 "quotes", "resize", "right", "scroll-behavior", "tab-size", 3149 "table-layout", "text-align", "text-align-last", 3150 "text-combine-upright", "text-decoration", "text-decoration-color", 3151 "text-decoration-line", "text-decoration-style", "text-indent", 3152 "text-justify", "text-orientation", "text-overflow", "text-shadow", 3153 "text-transform", "top", "transform", "transform-origin", 3154 "transform-style", "transition", "transition-delay", 3155 "transition-duration", "transition-property", 3156 "transition-timing-function", "unicode-bidi", "user-select", 3157 "vertical-align", "visibility", "white-space", "widows", "width", 3158 "word-break", "word-spacing", "word-wrap", "writing-mode", 3159 "z-index").Globally() 3160 p.RequireParseableURLs(true) 3161 3162 // These tests are run concurrently to enable the race detector to pick up 3163 // potential issues 3164 wg := sync.WaitGroup{} 3165 wg.Add(len(tests)) 3166 for ii, tt := range tests { 3167 go func(ii int, tt test) { 3168 out := p.Sanitize(tt.in) 3169 if out != tt.expected { 3170 t.Errorf( 3171 "test %d failed;\ninput : %s\noutput : %s\nexpected: %s", 3172 ii, 3173 tt.in, 3174 out, 3175 tt.expected, 3176 ) 3177 } 3178 wg.Done() 3179 }(ii, tt) 3180 } 3181 wg.Wait() 3182} 3183 3184func TestUnicodePoints(t *testing.T) { 3185 3186 tests := []test{ 3187 { 3188 in: `<div style="color: \72 ed;"></div>`, 3189 expected: `<div style="color: \72 ed"></div>`, 3190 }, 3191 { 3192 in: `<div style="color: \0072 ed;"></div>`, 3193 expected: `<div style="color: \0072 ed"></div>`, 3194 }, 3195 { 3196 in: `<div style="color: \000072 ed;"></div>`, 3197 expected: `<div style="color: \000072 ed"></div>`, 3198 }, 3199 { 3200 in: `<div style="color: \000072ed;"></div>`, 3201 expected: `<div style="color: \000072ed"></div>`, 3202 }, 3203 { 3204 in: `<div style="color: \100072ed;"></div>`, 3205 expected: `<div></div>`, 3206 }, 3207 } 3208 3209 p := UGCPolicy() 3210 p.AllowStyles("color").Globally() 3211 p.RequireParseableURLs(true) 3212 3213 // These tests are run concurrently to enable the race detector to pick up 3214 // potential issues 3215 wg := sync.WaitGroup{} 3216 wg.Add(len(tests)) 3217 for ii, tt := range tests { 3218 go func(ii int, tt test) { 3219 out := p.Sanitize(tt.in) 3220 if out != tt.expected { 3221 t.Errorf( 3222 "test %d failed;\ninput : %s\noutput : %s\nexpected: %s", 3223 ii, 3224 tt.in, 3225 out, 3226 tt.expected, 3227 ) 3228 } 3229 wg.Done() 3230 }(ii, tt) 3231 } 3232 wg.Wait() 3233} 3234 3235func TestMatchingHandler(t *testing.T) { 3236 truthHandler := func(value string) bool { 3237 return true 3238 } 3239 3240 tests := []test{ 3241 { 3242 in: `<div style="color: invalidValue"></div>`, 3243 expected: `<div style="color: invalidValue"></div>`, 3244 }, 3245 } 3246 3247 p := UGCPolicy() 3248 p.AllowStyles("color").MatchingHandler(truthHandler).Globally() 3249 p.RequireParseableURLs(true) 3250 3251 // These tests are run concurrently to enable the race detector to pick up 3252 // potential issues 3253 wg := sync.WaitGroup{} 3254 wg.Add(len(tests)) 3255 for ii, tt := range tests { 3256 go func(ii int, tt test) { 3257 out := p.Sanitize(tt.in) 3258 if out != tt.expected { 3259 t.Errorf( 3260 "test %d failed;\ninput : %s\noutput : %s\nexpected: %s", 3261 ii, 3262 tt.in, 3263 out, 3264 tt.expected, 3265 ) 3266 } 3267 wg.Done() 3268 }(ii, tt) 3269 } 3270 wg.Wait() 3271} 3272 3273func TestStyleBlockHandler(t *testing.T) { 3274 truthHandler := func(value string) bool { 3275 return true 3276 } 3277 3278 tests := []test{ 3279 { 3280 in: ``, 3281 expected: ``, 3282 }, 3283 } 3284 3285 p := UGCPolicy() 3286 p.AllowStyles("color").MatchingHandler(truthHandler).Globally() 3287 p.RequireParseableURLs(true) 3288 3289 // These tests are run concurrently to enable the race detector to pick up 3290 // potential issues 3291 wg := sync.WaitGroup{} 3292 wg.Add(len(tests)) 3293 for ii, tt := range tests { 3294 go func(ii int, tt test) { 3295 out := p.Sanitize(tt.in) 3296 if out != tt.expected { 3297 t.Errorf( 3298 "test %d failed;\ninput : %s\noutput : %s\nexpected: %s", 3299 ii, 3300 tt.in, 3301 out, 3302 tt.expected, 3303 ) 3304 } 3305 wg.Done() 3306 }(ii, tt) 3307 } 3308 wg.Wait() 3309} 3310 3311func TestAdditivePolicies(t *testing.T) { 3312 t.Run("AllowAttrs", func(t *testing.T) { 3313 p := NewPolicy() 3314 p.AllowAttrs("class").Matching(regexp.MustCompile("red")).OnElements("span") 3315 3316 t.Run("red", func(t *testing.T) { 3317 tests := []test{ 3318 { 3319 in: `<span class="red">test</span>`, 3320 expected: `<span class="red">test</span>`, 3321 }, 3322 { 3323 in: `<span class="green">test</span>`, 3324 expected: `<span>test</span>`, 3325 }, 3326 { 3327 in: `<span class="blue">test</span>`, 3328 expected: `<span>test</span>`, 3329 }, 3330 } 3331 3332 for ii, tt := range tests { 3333 out := p.Sanitize(tt.in) 3334 if out != tt.expected { 3335 t.Errorf( 3336 "test %d failed;\ninput : %s\noutput : %s\nexpected: %s", 3337 ii, 3338 tt.in, 3339 out, 3340 tt.expected, 3341 ) 3342 } 3343 } 3344 }) 3345 3346 p.AllowAttrs("class").Matching(regexp.MustCompile("green")).OnElements("span") 3347 3348 t.Run("green", func(t *testing.T) { 3349 tests := []test{ 3350 { 3351 in: `<span class="red">test</span>`, 3352 expected: `<span class="red">test</span>`, 3353 }, 3354 { 3355 in: `<span class="green">test</span>`, 3356 expected: `<span class="green">test</span>`, 3357 }, 3358 { 3359 in: `<span class="blue">test</span>`, 3360 expected: `<span>test</span>`, 3361 }, 3362 } 3363 3364 for ii, tt := range tests { 3365 out := p.Sanitize(tt.in) 3366 if out != tt.expected { 3367 t.Errorf( 3368 "test %d failed;\ninput : %s\noutput : %s\nexpected: %s", 3369 ii, 3370 tt.in, 3371 out, 3372 tt.expected, 3373 ) 3374 } 3375 } 3376 }) 3377 3378 p.AllowAttrs("class").Matching(regexp.MustCompile("yellow")).OnElements("span") 3379 3380 t.Run("yellow", func(t *testing.T) { 3381 tests := []test{ 3382 { 3383 in: `<span class="red">test</span>`, 3384 expected: `<span class="red">test</span>`, 3385 }, 3386 { 3387 in: `<span class="green">test</span>`, 3388 expected: `<span class="green">test</span>`, 3389 }, 3390 { 3391 in: `<span class="blue">test</span>`, 3392 expected: `<span>test</span>`, 3393 }, 3394 } 3395 3396 for ii, tt := range tests { 3397 out := p.Sanitize(tt.in) 3398 if out != tt.expected { 3399 t.Errorf( 3400 "test %d failed;\ninput : %s\noutput : %s\nexpected: %s", 3401 ii, 3402 tt.in, 3403 out, 3404 tt.expected, 3405 ) 3406 } 3407 } 3408 }) 3409 }) 3410 3411 t.Run("AllowStyles", func(t *testing.T) { 3412 p := NewPolicy() 3413 p.AllowAttrs("style").OnElements("span") 3414 p.AllowStyles("color").Matching(regexp.MustCompile("red")).OnElements("span") 3415 3416 t.Run("red", func(t *testing.T) { 3417 tests := []test{ 3418 { 3419 in: `<span style="color: red">test</span>`, 3420 expected: `<span style="color: red">test</span>`, 3421 }, 3422 { 3423 in: `<span style="color: green">test</span>`, 3424 expected: `<span>test</span>`, 3425 }, 3426 { 3427 in: `<span style="color: blue">test</span>`, 3428 expected: `<span>test</span>`, 3429 }, 3430 } 3431 3432 for ii, tt := range tests { 3433 out := p.Sanitize(tt.in) 3434 if out != tt.expected { 3435 t.Errorf( 3436 "test %d failed;\ninput : %s\noutput : %s\nexpected: %s", 3437 ii, 3438 tt.in, 3439 out, 3440 tt.expected, 3441 ) 3442 } 3443 } 3444 }) 3445 3446 p.AllowStyles("color").Matching(regexp.MustCompile("green")).OnElements("span") 3447 3448 t.Run("green", func(t *testing.T) { 3449 tests := []test{ 3450 { 3451 in: `<span style="color: red">test</span>`, 3452 expected: `<span style="color: red">test</span>`, 3453 }, 3454 { 3455 in: `<span style="color: green">test</span>`, 3456 expected: `<span style="color: green">test</span>`, 3457 }, 3458 { 3459 in: `<span style="color: blue">test</span>`, 3460 expected: `<span>test</span>`, 3461 }, 3462 } 3463 3464 for ii, tt := range tests { 3465 out := p.Sanitize(tt.in) 3466 if out != tt.expected { 3467 t.Errorf( 3468 "test %d failed;\ninput : %s\noutput : %s\nexpected: %s", 3469 ii, 3470 tt.in, 3471 out, 3472 tt.expected, 3473 ) 3474 } 3475 } 3476 }) 3477 3478 p.AllowStyles("color").Matching(regexp.MustCompile("yellow")).OnElements("span") 3479 3480 t.Run("yellow", func(t *testing.T) { 3481 tests := []test{ 3482 { 3483 in: `<span style="color: red">test</span>`, 3484 expected: `<span style="color: red">test</span>`, 3485 }, 3486 { 3487 in: `<span style="color: green">test</span>`, 3488 expected: `<span style="color: green">test</span>`, 3489 }, 3490 { 3491 in: `<span style="color: blue">test</span>`, 3492 expected: `<span>test</span>`, 3493 }, 3494 } 3495 3496 for ii, tt := range tests { 3497 out := p.Sanitize(tt.in) 3498 if out != tt.expected { 3499 t.Errorf( 3500 "test %d failed;\ninput : %s\noutput : %s\nexpected: %s", 3501 ii, 3502 tt.in, 3503 out, 3504 tt.expected, 3505 ) 3506 } 3507 } 3508 }) 3509 }) 3510 3511 t.Run("AllowURLSchemeWithCustomPolicy", func(t *testing.T) { 3512 p := NewPolicy() 3513 p.AllowAttrs("href").OnElements("a") 3514 3515 p.AllowURLSchemeWithCustomPolicy( 3516 "http", 3517 func(url *url.URL) bool { 3518 return url.Hostname() == "example.org" 3519 }, 3520 ) 3521 3522 t.Run("example.org", func(t *testing.T) { 3523 tests := []test{ 3524 { 3525 in: `<a href="http://example.org/">test</a>`, 3526 expected: `<a href="http://example.org/">test</a>`, 3527 }, 3528 { 3529 in: `<a href="http://example2.org/">test</a>`, 3530 expected: `test`, 3531 }, 3532 { 3533 in: `<a href="http://example4.org/">test</a>`, 3534 expected: `test`, 3535 }, 3536 } 3537 3538 for ii, tt := range tests { 3539 out := p.Sanitize(tt.in) 3540 if out != tt.expected { 3541 t.Errorf( 3542 "test %d failed;\ninput : %s\noutput : %s\nexpected: %s", 3543 ii, 3544 tt.in, 3545 out, 3546 tt.expected, 3547 ) 3548 } 3549 } 3550 }) 3551 3552 p.AllowURLSchemeWithCustomPolicy( 3553 "http", 3554 func(url *url.URL) bool { 3555 return url.Hostname() == "example2.org" 3556 }, 3557 ) 3558 3559 t.Run("example2.org", func(t *testing.T) { 3560 tests := []test{ 3561 { 3562 in: `<a href="http://example.org/">test</a>`, 3563 expected: `<a href="http://example.org/">test</a>`, 3564 }, 3565 { 3566 in: `<a href="http://example2.org/">test</a>`, 3567 expected: `<a href="http://example2.org/">test</a>`, 3568 }, 3569 { 3570 in: `<a href="http://example4.org/">test</a>`, 3571 expected: `test`, 3572 }, 3573 } 3574 3575 for ii, tt := range tests { 3576 out := p.Sanitize(tt.in) 3577 if out != tt.expected { 3578 t.Errorf( 3579 "test %d failed;\ninput : %s\noutput : %s\nexpected: %s", 3580 ii, 3581 tt.in, 3582 out, 3583 tt.expected, 3584 ) 3585 } 3586 } 3587 }) 3588 3589 p.AllowURLSchemeWithCustomPolicy( 3590 "http", 3591 func(url *url.URL) bool { 3592 return url.Hostname() == "example3.org" 3593 }, 3594 ) 3595 3596 t.Run("example3.org", func(t *testing.T) { 3597 tests := []test{ 3598 { 3599 in: `<a href="http://example.org/">test</a>`, 3600 expected: `<a href="http://example.org/">test</a>`, 3601 }, 3602 { 3603 in: `<a href="http://example2.org/">test</a>`, 3604 expected: `<a href="http://example2.org/">test</a>`, 3605 }, 3606 { 3607 in: `<a href="http://example4.org/">test</a>`, 3608 expected: `test`, 3609 }, 3610 } 3611 3612 for ii, tt := range tests { 3613 out := p.Sanitize(tt.in) 3614 if out != tt.expected { 3615 t.Errorf( 3616 "test %d failed;\ninput : %s\noutput : %s\nexpected: %s", 3617 ii, 3618 tt.in, 3619 out, 3620 tt.expected, 3621 ) 3622 } 3623 } 3624 }) 3625 }) 3626} 3627 3628func TestHrefSanitization(t *testing.T) { 3629 tests := []test{ 3630 { 3631 in: `abc<a href="https://abc"><script>alert(1)</script/">CLICK`, 3632 expected: `abc<a href="https://abc&quot;><script>alert(1)</script/" rel="nofollow">CLICK`, 3633 }, 3634 { 3635 in: `<a href="https://abc"><script>alert(1)</script/">`, 3636 expected: `<a href="https://abc&quot;><script>alert(1)</script/" rel="nofollow">`, 3637 }, 3638 } 3639 3640 p := UGCPolicy() 3641 3642 // These tests are run concurrently to enable the race detector to pick up 3643 // potential issues 3644 wg := sync.WaitGroup{} 3645 wg.Add(len(tests)) 3646 for ii, tt := range tests { 3647 go func(ii int, tt test) { 3648 out := p.Sanitize(tt.in) 3649 if out != tt.expected { 3650 t.Errorf( 3651 "test %d failed;\ninput : %s\noutput : %s\nexpected: %s", 3652 ii, 3653 tt.in, 3654 out, 3655 tt.expected, 3656 ) 3657 } 3658 wg.Done() 3659 }(ii, tt) 3660 } 3661 wg.Wait() 3662} 3663 3664func TestInsertionModeSanitization(t *testing.T) { 3665 tests := []test{ 3666 { 3667 in: `<select><option><style><script>alert(1)</script>`, 3668 expected: `<select><option>`, 3669 }, 3670 } 3671 3672 p := UGCPolicy() 3673 p.AllowElements("select", "option", "style") 3674 3675 // These tests are run concurrently to enable the race detector to pick up 3676 // potential issues 3677 wg := sync.WaitGroup{} 3678 wg.Add(len(tests)) 3679 for ii, tt := range tests { 3680 go func(ii int, tt test) { 3681 out := p.Sanitize(tt.in) 3682 if out != tt.expected { 3683 t.Errorf( 3684 "test %d failed;\ninput : %s\noutput : %s\nexpected: %s", 3685 ii, 3686 tt.in, 3687 out, 3688 tt.expected, 3689 ) 3690 } 3691 wg.Done() 3692 }(ii, tt) 3693 } 3694 wg.Wait() 3695} 3696