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, &lt;&lt;World&gt;&gt;!</title></head>"
785c702c12be71d8070da9287cc4a044617dd726a7manico.james@gmail.com        + "<body><p id=\"p&#39;0\">Hello,"
790df9131f7be5c0f90ce70d43b7e4239a6a6df016mikesamuel        + "<br />&lt;&lt;World&gt;&gt;!</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