• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011, Mike Samuel
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions
6 // are met:
7 //
8 // Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // Redistributions in binary form must reproduce the above copyright
11 // notice, this list of conditions and the following disclaimer in the
12 // documentation and/or other materials provided with the distribution.
13 // Neither the name of the OWASP nor the names of its contributors may
14 // be used to endorse or promote products derived from this software
15 // without specific prior written permission.
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
19 // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
20 // COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
21 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
22 // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 // POSSIBILITY OF SUCH DAMAGE.
28 
29 package org.owasp.html;
30 
31 import java.util.List;
32 import java.util.concurrent.LinkedBlockingQueue;
33 import java.util.concurrent.ThreadPoolExecutor;
34 import java.util.concurrent.TimeUnit;
35 
36 import com.google.common.base.Charsets;
37 import com.google.common.base.Throwables;
38 import com.google.common.io.Resources;
39 
40 /**
41  * Throws malformed inputs at the HTML sanitizer to try and crash it.
42  * This test is stochastic -- not guaranteed to pass or fail consistently.
43  * If you see a failure, please report it along with the seed from the output.
44  * If you want to repeat a failure, set the system property "junit.seed".
45  *
46  * @author Mike Samuel <mikesamuel@gmail.com>
47  */
48 public class HtmlSanitizerFuzzerTest extends FuzzyTestCase {
49 
50   private static final HtmlSanitizer.Policy DO_NOTHING_POLICY
51       = new HtmlSanitizer.Policy() {
52         public void openDocument() { /* do nothing */ }
53         public void closeDocument() { /* do nothing */ }
54         public void openTag(String elementName, List<String> attrs) {
55           /* do nothing */
56         }
57         public void closeTag(String elementName) { /* do nothing */ }
58         public void text(String textChunk) { /* do nothing */ }
59       };
60 
testFuzzHtmlParser()61   public final void testFuzzHtmlParser() throws Exception {
62     String html = Resources.toString(
63         Resources.getResource("Yahoo!.html"), Charsets.UTF_8);
64     int length = html.length();
65 
66     char[] fuzzyHtml0 = new char[length];
67     char[] fuzzyHtml1 = new char[length];
68 
69     final LinkedBlockingQueue<Throwable> failures
70         = new LinkedBlockingQueue<Throwable>();
71 
72     final int runCount = 1000;
73     // Use an executor so that any infinite loops do not cause the test runner
74     // to fail.
75     ThreadPoolExecutor executor = new ThreadPoolExecutor(
76         10, 10, 10, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
77 
78     for (int run = runCount; --run >= 0;) {
79       for (int i = length; --i >= 0;) { fuzzyHtml0[i] = html.charAt(i); }
80       for (int fuzz = 1 + rnd.nextInt(25); --fuzz >= 0;) {
81         if (rnd.nextBoolean()) {
82           fuzzyHtml0[rnd.nextInt(length)] = (char) rnd.nextInt(0x10000);
83           continue;
84         }
85         int s0 = rnd.nextInt(length - 1);
86         double d = Math.abs(rnd.nextGaussian()) / 3.0d;
87         int e0 = s0 + (int) (rnd.nextInt(length - s0) * d);
88         if (e0 >= length) { e0 = s0 + 1; }
89 
90         int s1 = rnd.nextInt(length - 1);
91         d = Math.abs(rnd.nextGaussian()) / 3.0d;
92         int e1 = s1 + (int) (rnd.nextInt(length - s1) * d);
93         if (e1 >= length) { e1 = s1 + 1; }
94 
95         if (s0 > s1) {
96           int st = s0, et = e0;
97           s0 = s1;
98           e0 = e1;
99           s1 = st;
100           e1 = et;
101         }
102 
103         if (e0 > s1) { e0 = s1; }
104 
105         // Swap the ranges [s0, e0) and [s1, e1) into fuzzyHtml1.
106         int i0, i1 = 0;
107         for (i0 = 0; i0 < s0; ++i0, ++i1) {
108           fuzzyHtml1[i1] = fuzzyHtml0[i0];
109         }
110         for (i0 = s1; i0 < e1; ++i0, ++i1) {
111           fuzzyHtml1[i1] = fuzzyHtml0[i0];
112         }
113         for (i0 = e0; i0 < s1; ++i0, ++i1) {
114           fuzzyHtml1[i1] = fuzzyHtml0[i0];
115         }
116         for (i0 = s0; i0 < e0; ++i0, ++i1) {
117           fuzzyHtml1[i1] = fuzzyHtml0[i0];
118         }
119         for (i0 = e1; i0 < length; ++i0, ++i1) {
120           fuzzyHtml1[i1] = fuzzyHtml0[i0];
121         }
122         // Swap the two buffers.
123         char[] swap = fuzzyHtml0;
124         fuzzyHtml0 = fuzzyHtml1;
125         fuzzyHtml1 = swap;
126       }
127       final String fuzzyHtml = new String(fuzzyHtml0);
128       executor.execute(new Runnable() {
129         public void run() {
130           try {
131             HtmlSanitizer.sanitize(fuzzyHtml, DO_NOTHING_POLICY);
132           } catch (Exception ex) {
133             System.err.println(
134                 "Using seed " + seed + "L\n"
135                 + "Failed on <<<" + fuzzyHtml + ">>>");
136             failures.add(ex);
137           }
138         }
139       });
140     }
141     executor.shutdown();
142     executor.awaitTermination(runCount * 4, TimeUnit.SECONDS);
143     assertTrue("seed=" + seed, executor.isTerminated());
144     Throwable failure = failures.poll();
145     if (failure != null) {
146       Throwables.propagate(failure);
147     }
148   }
149 
150 }
151