18403881c365ab36b721ccc4500af1b3a5bd25870mikesamuel// Copyright (c) 2011, Mike Samuel 28403881c365ab36b721ccc4500af1b3a5bd25870mikesamuel// All rights reserved. 38403881c365ab36b721ccc4500af1b3a5bd25870mikesamuel// 48403881c365ab36b721ccc4500af1b3a5bd25870mikesamuel// Redistribution and use in source and binary forms, with or without 58403881c365ab36b721ccc4500af1b3a5bd25870mikesamuel// modification, are permitted provided that the following conditions 68403881c365ab36b721ccc4500af1b3a5bd25870mikesamuel// are met: 78403881c365ab36b721ccc4500af1b3a5bd25870mikesamuel// 88403881c365ab36b721ccc4500af1b3a5bd25870mikesamuel// Redistributions of source code must retain the above copyright 98403881c365ab36b721ccc4500af1b3a5bd25870mikesamuel// notice, this list of conditions and the following disclaimer. 108403881c365ab36b721ccc4500af1b3a5bd25870mikesamuel// Redistributions in binary form must reproduce the above copyright 118403881c365ab36b721ccc4500af1b3a5bd25870mikesamuel// notice, this list of conditions and the following disclaimer in the 128403881c365ab36b721ccc4500af1b3a5bd25870mikesamuel// documentation and/or other materials provided with the distribution. 138403881c365ab36b721ccc4500af1b3a5bd25870mikesamuel// Neither the name of the OWASP nor the names of its contributors may 148403881c365ab36b721ccc4500af1b3a5bd25870mikesamuel// be used to endorse or promote products derived from this software 158403881c365ab36b721ccc4500af1b3a5bd25870mikesamuel// without specific prior written permission. 168403881c365ab36b721ccc4500af1b3a5bd25870mikesamuel// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 178403881c365ab36b721ccc4500af1b3a5bd25870mikesamuel// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 188403881c365ab36b721ccc4500af1b3a5bd25870mikesamuel// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 198403881c365ab36b721ccc4500af1b3a5bd25870mikesamuel// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 208403881c365ab36b721ccc4500af1b3a5bd25870mikesamuel// COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 218403881c365ab36b721ccc4500af1b3a5bd25870mikesamuel// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 228403881c365ab36b721ccc4500af1b3a5bd25870mikesamuel// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 238403881c365ab36b721ccc4500af1b3a5bd25870mikesamuel// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 248403881c365ab36b721ccc4500af1b3a5bd25870mikesamuel// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 258403881c365ab36b721ccc4500af1b3a5bd25870mikesamuel// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 268403881c365ab36b721ccc4500af1b3a5bd25870mikesamuel// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 278403881c365ab36b721ccc4500af1b3a5bd25870mikesamuel// POSSIBILITY OF SUCH DAMAGE. 288403881c365ab36b721ccc4500af1b3a5bd25870mikesamuel 295c702c12be71d8070da9287cc4a044617dd726a7manico.james@gmail.compackage org.owasp.html; 305c702c12be71d8070da9287cc4a044617dd726a7manico.james@gmail.com 315c702c12be71d8070da9287cc4a044617dd726a7manico.james@gmail.comimport com.google.common.collect.ImmutableList; 325c702c12be71d8070da9287cc4a044617dd726a7manico.james@gmail.com 335c702c12be71d8070da9287cc4a044617dd726a7manico.james@gmail.comimport junit.framework.TestCase; 345c702c12be71d8070da9287cc4a044617dd726a7manico.james@gmail.com 35550c8d3230c152db7156b266d089512b72ac0024mikesamuelimport org.junit.Test; 36550c8d3230c152db7156b266d089512b72ac0024mikesamuelimport org.junit.Before; 37550c8d3230c152db7156b266d089512b72ac0024mikesamuel 38550c8d3230c152db7156b266d089512b72ac0024mikesamuelimport static org.owasp.html.TagBalancingHtmlStreamEventReceiver 39550c8d3230c152db7156b266d089512b72ac0024mikesamuel .isInterElementWhitespace; 40550c8d3230c152db7156b266d089512b72ac0024mikesamuel 41550c8d3230c152db7156b266d089512b72ac0024mikesamuel 425c702c12be71d8070da9287cc4a044617dd726a7manico.james@gmail.compublic class TagBalancingHtmlStreamRendererTest extends TestCase { 435c702c12be71d8070da9287cc4a044617dd726a7manico.james@gmail.com 4427b4be957534ebb90e21ac8d31bf722e4c9273bfmikesamuel StringBuilder htmlOutputBuffer; 453f54e49f2181c52ca40d99fbe738b2484ba91528mikesamuel TagBalancingHtmlStreamEventReceiver balancer; 4627b4be957534ebb90e21ac8d31bf722e4c9273bfmikesamuel 47550c8d3230c152db7156b266d089512b72ac0024mikesamuel @Before @Override protected void setUp() throws Exception { 4827b4be957534ebb90e21ac8d31bf722e4c9273bfmikesamuel super.setUp(); 4927b4be957534ebb90e21ac8d31bf722e4c9273bfmikesamuel htmlOutputBuffer = new StringBuilder(); 5027b4be957534ebb90e21ac8d31bf722e4c9273bfmikesamuel balancer = new TagBalancingHtmlStreamEventReceiver( 515c702c12be71d8070da9287cc4a044617dd726a7manico.james@gmail.com HtmlStreamRenderer.create(htmlOutputBuffer, new Handler<String>() { 525c702c12be71d8070da9287cc4a044617dd726a7manico.james@gmail.com public void handle(String x) { 535c702c12be71d8070da9287cc4a044617dd726a7manico.james@gmail.com fail("An unexpected error was raised during the testcase"); 545c702c12be71d8070da9287cc4a044617dd726a7manico.james@gmail.com } 555c702c12be71d8070da9287cc4a044617dd726a7manico.james@gmail.com })); 5627b4be957534ebb90e21ac8d31bf722e4c9273bfmikesamuel } 575c702c12be71d8070da9287cc4a044617dd726a7manico.james@gmail.com 58550c8d3230c152db7156b266d089512b72ac0024mikesamuel @Test 5927b4be957534ebb90e21ac8d31bf722e4c9273bfmikesamuel public final void testTagBalancing() { 605c702c12be71d8070da9287cc4a044617dd726a7manico.james@gmail.com balancer.openDocument(); 615c702c12be71d8070da9287cc4a044617dd726a7manico.james@gmail.com balancer.openTag("html", ImmutableList.<String>of()); 625c702c12be71d8070da9287cc4a044617dd726a7manico.james@gmail.com balancer.openTag("head", ImmutableList.<String>of()); 635c702c12be71d8070da9287cc4a044617dd726a7manico.james@gmail.com balancer.openTag("title", ImmutableList.<String>of()); 645c702c12be71d8070da9287cc4a044617dd726a7manico.james@gmail.com balancer.text("Hello, <<World>>!"); 650df9131f7be5c0f90ce70d43b7e4239a6a6df016mikesamuel // TITLE closed with case-sensitively different name. 665c702c12be71d8070da9287cc4a044617dd726a7manico.james@gmail.com balancer.closeTag("TITLE"); 675c702c12be71d8070da9287cc4a044617dd726a7manico.james@gmail.com balancer.closeTag("head"); 685c702c12be71d8070da9287cc4a044617dd726a7manico.james@gmail.com balancer.openTag("body", ImmutableList.<String>of()); 695c702c12be71d8070da9287cc4a044617dd726a7manico.james@gmail.com balancer.openTag("p", ImmutableList.of("id", "p'0")); 705c702c12be71d8070da9287cc4a044617dd726a7manico.james@gmail.com balancer.text("Hello,"); 715c702c12be71d8070da9287cc4a044617dd726a7manico.james@gmail.com balancer.openTag("Br", ImmutableList.<String>of()); 725c702c12be71d8070da9287cc4a044617dd726a7manico.james@gmail.com balancer.text("<<World>>!"); 735c702c12be71d8070da9287cc4a044617dd726a7manico.james@gmail.com // HTML, P, and BODY unclosed, but BR not. 745c702c12be71d8070da9287cc4a044617dd726a7manico.james@gmail.com balancer.closeDocument(); 755c702c12be71d8070da9287cc4a044617dd726a7manico.james@gmail.com 765c702c12be71d8070da9287cc4a044617dd726a7manico.james@gmail.com assertEquals( 775c702c12be71d8070da9287cc4a044617dd726a7manico.james@gmail.com "<html><head><title>Hello, <<World>>!</title></head>" 785c702c12be71d8070da9287cc4a044617dd726a7manico.james@gmail.com + "<body><p id=\"p'0\">Hello," 790df9131f7be5c0f90ce70d43b7e4239a6a6df016mikesamuel + "<br /><<World>>!</p></body></html>", 805c702c12be71d8070da9287cc4a044617dd726a7manico.james@gmail.com htmlOutputBuffer.toString()); 815c702c12be71d8070da9287cc4a044617dd726a7manico.james@gmail.com } 8227b4be957534ebb90e21ac8d31bf722e4c9273bfmikesamuel 83550c8d3230c152db7156b266d089512b72ac0024mikesamuel @Test 8427b4be957534ebb90e21ac8d31bf722e4c9273bfmikesamuel public final void testTagSoupIronedOut() { 8527b4be957534ebb90e21ac8d31bf722e4c9273bfmikesamuel balancer.openDocument(); 8627b4be957534ebb90e21ac8d31bf722e4c9273bfmikesamuel balancer.openTag("i", ImmutableList.<String>of()); 8727b4be957534ebb90e21ac8d31bf722e4c9273bfmikesamuel balancer.text("a"); 8827b4be957534ebb90e21ac8d31bf722e4c9273bfmikesamuel balancer.openTag("b", ImmutableList.<String>of()); 8927b4be957534ebb90e21ac8d31bf722e4c9273bfmikesamuel balancer.text("b"); 9027b4be957534ebb90e21ac8d31bf722e4c9273bfmikesamuel balancer.closeTag("i"); 9127b4be957534ebb90e21ac8d31bf722e4c9273bfmikesamuel balancer.text("c"); 9227b4be957534ebb90e21ac8d31bf722e4c9273bfmikesamuel balancer.closeDocument(); 9327b4be957534ebb90e21ac8d31bf722e4c9273bfmikesamuel 9427b4be957534ebb90e21ac8d31bf722e4c9273bfmikesamuel assertEquals( 9527b4be957534ebb90e21ac8d31bf722e4c9273bfmikesamuel "<i>a<b>b</b></i><b>c</b>", 9627b4be957534ebb90e21ac8d31bf722e4c9273bfmikesamuel htmlOutputBuffer.toString()); 9727b4be957534ebb90e21ac8d31bf722e4c9273bfmikesamuel } 9827b4be957534ebb90e21ac8d31bf722e4c9273bfmikesamuel 99550c8d3230c152db7156b266d089512b72ac0024mikesamuel @Test 1001ecbdce5dd203e7aca2b93650ca3afce17dbc095mikesamuel public final void testListInListDirectly() { 1011ecbdce5dd203e7aca2b93650ca3afce17dbc095mikesamuel balancer.openDocument(); 1021ecbdce5dd203e7aca2b93650ca3afce17dbc095mikesamuel balancer.openTag("ul", ImmutableList.<String>of()); 1031ecbdce5dd203e7aca2b93650ca3afce17dbc095mikesamuel balancer.openTag("li", ImmutableList.<String>of()); 1041ecbdce5dd203e7aca2b93650ca3afce17dbc095mikesamuel balancer.text("foo"); 1051ecbdce5dd203e7aca2b93650ca3afce17dbc095mikesamuel balancer.closeTag("li"); 1061ecbdce5dd203e7aca2b93650ca3afce17dbc095mikesamuel balancer.openTag("ul", ImmutableList.<String>of()); 1071ecbdce5dd203e7aca2b93650ca3afce17dbc095mikesamuel balancer.openTag("li", ImmutableList.<String>of()); 1081ecbdce5dd203e7aca2b93650ca3afce17dbc095mikesamuel balancer.text("bar"); 1091ecbdce5dd203e7aca2b93650ca3afce17dbc095mikesamuel balancer.closeTag("li"); 1101ecbdce5dd203e7aca2b93650ca3afce17dbc095mikesamuel balancer.closeTag("ul"); 1111ecbdce5dd203e7aca2b93650ca3afce17dbc095mikesamuel balancer.closeTag("ul"); 1121ecbdce5dd203e7aca2b93650ca3afce17dbc095mikesamuel balancer.closeDocument(); 1131ecbdce5dd203e7aca2b93650ca3afce17dbc095mikesamuel 1141ecbdce5dd203e7aca2b93650ca3afce17dbc095mikesamuel assertEquals( 1151ecbdce5dd203e7aca2b93650ca3afce17dbc095mikesamuel "<ul><li>foo</li><li><ul><li>bar</li></ul></li></ul>", 1161ecbdce5dd203e7aca2b93650ca3afce17dbc095mikesamuel htmlOutputBuffer.toString()); 1171ecbdce5dd203e7aca2b93650ca3afce17dbc095mikesamuel } 1181ecbdce5dd203e7aca2b93650ca3afce17dbc095mikesamuel 119550c8d3230c152db7156b266d089512b72ac0024mikesamuel @Test 12063dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel public final void testTextContent() { 12163dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel balancer.openDocument(); 12263dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel balancer.openTag("title", ImmutableList.<String>of()); 12363dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel balancer.text("Hello, World!"); 12463dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel balancer.closeTag("title"); 12563dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel balancer.text("Hello, "); 12663dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel balancer.openTag("b", ImmutableList.<String>of()); 12763dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel balancer.text("World!"); 12863dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel balancer.closeTag("b"); 12963dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel balancer.openTag("p", ImmutableList.<String>of()); 13063dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel balancer.text("Hello, "); 13163dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel balancer.openTag("textarea", ImmutableList.<String>of()); 13263dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel balancer.text("World!"); 13363dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel balancer.closeTag("textarea"); 13463dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel balancer.closeTag("p"); 13563dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel balancer.openTag("h1", ImmutableList.<String>of()); 13663dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel balancer.text("Hello"); 13763dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel balancer.openTag("style", ImmutableList.<String>of("type", "text/css")); 13863dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel balancer.text("\n.World {\n color: blue\n}\n"); 13963dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel balancer.closeTag("style"); 14063dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel balancer.closeTag("h1"); 14163dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel balancer.openTag("ul", ImmutableList.<String>of()); 14263dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel balancer.text("\n "); 14363dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel balancer.openTag("li", ImmutableList.<String>of()); 14463dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel balancer.text("Hello,"); 14563dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel balancer.closeTag("li"); 14663dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel balancer.text("\n "); 14763dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel balancer.text("World!"); 14863dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel balancer.closeDocument(); 14963dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel 15063dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel assertEquals( 15163dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel // Text and only text allowed in title 15263dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel "<title>Hello, World!</title>" 15363dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel // Text allowed at top level and in phrasing content 15463dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel + "Hello, <b>World!</b>" 15563dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel // Text allowed in block elements and in text areas. 15663dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel + "<p>Hello, <textarea>World!</textarea></p>" 15763dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel + "<h1>Hello" 15863dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel // Text allowed in special style tag. 15963dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel + "<style type=\"text/css\">\n.World {\n color: blue\n}\n</style></h1>" 16063dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel // Whitespace allowed inside <ul> but non-whitespace text nodes are 16163dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel // moved inside <li>. 16263dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel + "<ul>\n <li>Hello,</li>\n <li>World!</li></ul>", 16363dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel htmlOutputBuffer.toString()); 16463dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel } 16563dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel 166550c8d3230c152db7156b266d089512b72ac0024mikesamuel @Test 16763dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel public final void testMismatchedHeaders() { 16863dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel balancer.openDocument(); 16963dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel balancer.openTag("H1", ImmutableList.<String>of()); 17063dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel balancer.text("header"); 17163dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel balancer.closeTag("h1"); 17263dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel balancer.text("body"); 17363dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel balancer.openTag("H2", ImmutableList.<String>of()); 17463dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel balancer.text("sub-header"); 17563dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel balancer.closeTag("h3"); 17663dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel balancer.text("sub-body"); 17763dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel balancer.openTag("h3", ImmutableList.<String>of()); 17863dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel balancer.text("sub-sub-"); 17963dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel balancer.closeTag("hr"); // hr is not a header tag so does not close an h3. 18063dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel balancer.text("header"); 18163dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel // <h3> is not allowed in h3. 18263dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel balancer.openTag("hr", ImmutableList.<String>of()); 18363dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel balancer.closeTag("hr"); 18463dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel balancer.text("sub-sub-body"); 18563dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel balancer.closeTag("H4"); 18663dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel balancer.closeTag("h2"); 18763dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel balancer.closeDocument(); 18863dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel 18963dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel assertEquals( 19063dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel "<h1>header</h1>body" 19163dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel + "<h2>sub-header</h2>sub-body" 19263dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel + "<h3>sub-sub-header</h3><hr />sub-sub-body", 19363dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel htmlOutputBuffer.toString()); 19463dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel } 19563dba946a9a0b3af438ca08b6824e653e5ca58c5mikesamuel 196550c8d3230c152db7156b266d089512b72ac0024mikesamuel @Test 19727b4be957534ebb90e21ac8d31bf722e4c9273bfmikesamuel public final void testListNesting() { 19827b4be957534ebb90e21ac8d31bf722e4c9273bfmikesamuel balancer.openDocument(); 19927b4be957534ebb90e21ac8d31bf722e4c9273bfmikesamuel balancer.openTag("ul", ImmutableList.<String>of()); 20027b4be957534ebb90e21ac8d31bf722e4c9273bfmikesamuel balancer.openTag("li", ImmutableList.<String>of()); 20127b4be957534ebb90e21ac8d31bf722e4c9273bfmikesamuel balancer.openTag("ul", ImmutableList.<String>of()); 20227b4be957534ebb90e21ac8d31bf722e4c9273bfmikesamuel balancer.openTag("li", ImmutableList.<String>of()); 20327b4be957534ebb90e21ac8d31bf722e4c9273bfmikesamuel balancer.text("foo"); 20427b4be957534ebb90e21ac8d31bf722e4c9273bfmikesamuel balancer.closeTag("li"); 205783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel // Does not closes the second <ul> since only </ol> and </ul> can close a 206783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel // <ul> based on the "has an element in list scope test" used by the HTML5 207783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel // tree building algo. 208783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel balancer.closeTag("li"); 209783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel // This would append inside a list, not an item. We insert an <li>. 21027b4be957534ebb90e21ac8d31bf722e4c9273bfmikesamuel balancer.openTag("ul", ImmutableList.<String>of()); 21127b4be957534ebb90e21ac8d31bf722e4c9273bfmikesamuel balancer.openTag("li", ImmutableList.<String>of()); 21227b4be957534ebb90e21ac8d31bf722e4c9273bfmikesamuel balancer.text("bar"); 21327b4be957534ebb90e21ac8d31bf722e4c9273bfmikesamuel balancer.closeDocument(); 21427b4be957534ebb90e21ac8d31bf722e4c9273bfmikesamuel 21527b4be957534ebb90e21ac8d31bf722e4c9273bfmikesamuel assertEquals( 216783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel "<ul><li><ul><li>foo</li><li><ul><li>bar</li></ul></li></ul></li></ul>", 2171ecbdce5dd203e7aca2b93650ca3afce17dbc095mikesamuel htmlOutputBuffer.toString()); 2181ecbdce5dd203e7aca2b93650ca3afce17dbc095mikesamuel } 2191ecbdce5dd203e7aca2b93650ca3afce17dbc095mikesamuel 220550c8d3230c152db7156b266d089512b72ac0024mikesamuel @Test 2211ecbdce5dd203e7aca2b93650ca3afce17dbc095mikesamuel public final void testTableNesting() { 2221ecbdce5dd203e7aca2b93650ca3afce17dbc095mikesamuel balancer.openDocument(); 2231ecbdce5dd203e7aca2b93650ca3afce17dbc095mikesamuel balancer.openTag("table", ImmutableList.<String>of()); 2241ecbdce5dd203e7aca2b93650ca3afce17dbc095mikesamuel balancer.openTag("tbody", ImmutableList.<String>of()); 2251ecbdce5dd203e7aca2b93650ca3afce17dbc095mikesamuel balancer.openTag("tr", ImmutableList.<String>of()); 2261ecbdce5dd203e7aca2b93650ca3afce17dbc095mikesamuel balancer.openTag("td", ImmutableList.<String>of()); 2271ecbdce5dd203e7aca2b93650ca3afce17dbc095mikesamuel balancer.text("foo"); 2281ecbdce5dd203e7aca2b93650ca3afce17dbc095mikesamuel balancer.closeTag("td"); 2291ecbdce5dd203e7aca2b93650ca3afce17dbc095mikesamuel // Insert a td to contain this mis-nested table. 2301ecbdce5dd203e7aca2b93650ca3afce17dbc095mikesamuel balancer.openTag("table", ImmutableList.<String>of()); 2311ecbdce5dd203e7aca2b93650ca3afce17dbc095mikesamuel balancer.openTag("tbody", ImmutableList.<String>of()); 2321ecbdce5dd203e7aca2b93650ca3afce17dbc095mikesamuel balancer.openTag("tr", ImmutableList.<String>of()); 2331ecbdce5dd203e7aca2b93650ca3afce17dbc095mikesamuel balancer.openTag("th", ImmutableList.<String>of()); 2341ecbdce5dd203e7aca2b93650ca3afce17dbc095mikesamuel balancer.text("bar"); 2351ecbdce5dd203e7aca2b93650ca3afce17dbc095mikesamuel balancer.closeTag("table"); 2361ecbdce5dd203e7aca2b93650ca3afce17dbc095mikesamuel balancer.closeTag("table"); 2371ecbdce5dd203e7aca2b93650ca3afce17dbc095mikesamuel balancer.closeDocument(); 2381ecbdce5dd203e7aca2b93650ca3afce17dbc095mikesamuel 2391ecbdce5dd203e7aca2b93650ca3afce17dbc095mikesamuel assertEquals( 2401ecbdce5dd203e7aca2b93650ca3afce17dbc095mikesamuel "<table><tbody><tr><td>foo</td><td>" 2411ecbdce5dd203e7aca2b93650ca3afce17dbc095mikesamuel + "<table><tbody><tr><th>bar</th></tr></tbody></table>" 2421ecbdce5dd203e7aca2b93650ca3afce17dbc095mikesamuel + "</td></tr></tbody></table>", 24327b4be957534ebb90e21ac8d31bf722e4c9273bfmikesamuel htmlOutputBuffer.toString()); 24427b4be957534ebb90e21ac8d31bf722e4c9273bfmikesamuel } 24527b4be957534ebb90e21ac8d31bf722e4c9273bfmikesamuel 246550c8d3230c152db7156b266d089512b72ac0024mikesamuel @Test 2473f54e49f2181c52ca40d99fbe738b2484ba91528mikesamuel public final void testNestingLimits() { 2483f54e49f2181c52ca40d99fbe738b2484ba91528mikesamuel // Some browsers can be DoSed by deeply nested structures. 2493f54e49f2181c52ca40d99fbe738b2484ba91528mikesamuel // See Issue 3, "Deeply nested elements crash FF 8, Chrome 11" 2503f54e49f2181c52ca40d99fbe738b2484ba91528mikesamuel // @ http://code.google.com/p/owasp-java-html-sanitizer/issues/detail?id=3 2513f54e49f2181c52ca40d99fbe738b2484ba91528mikesamuel 2523f54e49f2181c52ca40d99fbe738b2484ba91528mikesamuel balancer.setNestingLimit(10); 2533f54e49f2181c52ca40d99fbe738b2484ba91528mikesamuel balancer.openDocument(); 2543f54e49f2181c52ca40d99fbe738b2484ba91528mikesamuel ImmutableList<String> attrs = ImmutableList.<String>of(); 2553f54e49f2181c52ca40d99fbe738b2484ba91528mikesamuel for (int i = 20000; --i >= 0;) { 2563f54e49f2181c52ca40d99fbe738b2484ba91528mikesamuel balancer.openTag("div", attrs); 2573f54e49f2181c52ca40d99fbe738b2484ba91528mikesamuel } 2583f54e49f2181c52ca40d99fbe738b2484ba91528mikesamuel balancer.openTag("hr", attrs); 2593f54e49f2181c52ca40d99fbe738b2484ba91528mikesamuel balancer.closeDocument(); 2603f54e49f2181c52ca40d99fbe738b2484ba91528mikesamuel assertEquals( 2613f54e49f2181c52ca40d99fbe738b2484ba91528mikesamuel "<div><div><div><div><div><div><div><div><div><div>" 2623f54e49f2181c52ca40d99fbe738b2484ba91528mikesamuel + "</div></div></div></div></div></div></div></div></div></div>", 2633f54e49f2181c52ca40d99fbe738b2484ba91528mikesamuel htmlOutputBuffer.toString()); 2643f54e49f2181c52ca40d99fbe738b2484ba91528mikesamuel } 265783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel 266550c8d3230c152db7156b266d089512b72ac0024mikesamuel @Test 267783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel public final void testTablesGuarded() { 268783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel // Derived from issue 12. 269783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel balancer.openDocument(); 270783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel balancer.openTag("html", ImmutableList.<String>of()); 271783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel balancer.openTag("head", ImmutableList.<String>of()); 272783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel balancer.openTag("meta", ImmutableList.<String>of()); 273783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel balancer.closeTag("head"); 274783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel balancer.openTag("body", ImmutableList.<String>of()); 275783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel balancer.openTag("p", ImmutableList.<String>of()); 276783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel balancer.text("Hi"); 277783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel balancer.closeTag("p"); 278783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel balancer.openTag("p", ImmutableList.<String>of()); 279783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel balancer.text("How are you"); 280783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel balancer.closeTag("p"); 281783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel balancer.text("\n"); 282783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel balancer.openTag("p", ImmutableList.<String>of()); 283783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel balancer.openTag("table", ImmutableList.<String>of()); 284783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel balancer.openTag("tbody", ImmutableList.<String>of()); 285783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel balancer.openTag("tr", ImmutableList.<String>of()); 286783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel for (int i = 2; --i >= 0;) { 287783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel balancer.openTag("td", ImmutableList.<String>of()); 288783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel balancer.openTag("b", ImmutableList.<String>of()); 289783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel balancer.openTag("font", ImmutableList.<String>of()); 290783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel balancer.openTag("font", ImmutableList.<String>of()); 291783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel balancer.openTag("p", ImmutableList.<String>of()); 292783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel balancer.text("Cell"); 293783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel balancer.closeTag("b"); 294783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel balancer.closeTag("font"); 295783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel balancer.closeTag("font"); 296783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel balancer.closeTag("p"); 297783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel balancer.text("\n"); 298783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel balancer.closeTag("td"); 299783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel } 300783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel balancer.closeTag("tr"); 301783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel balancer.closeTag("tbody"); 302783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel balancer.closeTag("table"); 303783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel balancer.closeTag("p"); 304783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel balancer.text("\n"); 305783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel balancer.openTag("p", ImmutableList.<String>of()); 306783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel balancer.text("x"); 307783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel balancer.closeTag("p"); 308783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel balancer.closeTag("body"); 309783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel balancer.closeTag("html"); 310783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel balancer.closeDocument(); 311783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel 312783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel assertEquals( 313783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel "<html><head><meta /></head><body><p>Hi</p><p>How are you</p>\n" 314783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel + "<p><table><tbody><tr>" 315783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel + "<td><b><font><font></font></font></b><b><p>Cell</p></b>\n</td>" 316783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel // The close </p> tag does not close the whole table. 317783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel + "<td><b><font><font></font></font></b><b><p>Cell</p></b>\n</td>" 318783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel + "</tr></tbody></table></p>\n" 319783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel + "<p>x</p></body></html>", 320783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel htmlOutputBuffer.toString()); 321783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel } 322783908cf042927b900d42383d30ec1fb8ee83d1fmikesamuel 323550c8d3230c152db7156b266d089512b72ac0024mikesamuel @Test 324550c8d3230c152db7156b266d089512b72ac0024mikesamuel public final void testIsInterElementWhitespace() { 325550c8d3230c152db7156b266d089512b72ac0024mikesamuel assertFalse(isInterElementWhitespace("foo")); 326550c8d3230c152db7156b266d089512b72ac0024mikesamuel assertTrue(isInterElementWhitespace("")); 327550c8d3230c152db7156b266d089512b72ac0024mikesamuel assertTrue(isInterElementWhitespace(" ")); 328550c8d3230c152db7156b266d089512b72ac0024mikesamuel assertTrue(isInterElementWhitespace("\t")); 329550c8d3230c152db7156b266d089512b72ac0024mikesamuel assertTrue(isInterElementWhitespace("\n")); 330550c8d3230c152db7156b266d089512b72ac0024mikesamuel assertTrue(isInterElementWhitespace(" \n")); 331550c8d3230c152db7156b266d089512b72ac0024mikesamuel assertTrue(isInterElementWhitespace("\r\n")); 332550c8d3230c152db7156b266d089512b72ac0024mikesamuel assertTrue(isInterElementWhitespace("\r")); 333550c8d3230c152db7156b266d089512b72ac0024mikesamuel assertTrue(isInterElementWhitespace(" ")); 334550c8d3230c152db7156b266d089512b72ac0024mikesamuel assertTrue(isInterElementWhitespace(" \t ")); 335550c8d3230c152db7156b266d089512b72ac0024mikesamuel assertFalse(isInterElementWhitespace(" foo ")); 336550c8d3230c152db7156b266d089512b72ac0024mikesamuel assertFalse(isInterElementWhitespace("\u00A0")); 337550c8d3230c152db7156b266d089512b72ac0024mikesamuel assertFalse(isInterElementWhitespace("\u0000")); 338550c8d3230c152db7156b266d089512b72ac0024mikesamuel } 339550c8d3230c152db7156b266d089512b72ac0024mikesamuel 3405c702c12be71d8070da9287cc4a044617dd726a7manico.james@gmail.com} 341