View Javadoc
1   /*
2    * Copyright (c) 2002-2025 Gargoyle Software Inc.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * https://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software
10   * distributed under the License is distributed on an "AS IS" BASIS,
11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12   * See the License for the specific language governing permissions and
13   * limitations under the License.
14   */
15  package org.htmlunit;
16  
17  import static org.junit.Assert.fail;
18  
19  import java.io.IOException;
20  import java.util.ArrayList;
21  import java.util.List;
22  
23  import org.htmlunit.html.HtmlElement;
24  import org.htmlunit.html.HtmlPage;
25  import org.htmlunit.junit.BrowserRunner;
26  import org.junit.Test;
27  import org.junit.runner.RunWith;
28  
29  /**
30   * Tests for {@link ScriptPreProcessor}.
31   *
32   * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
33   * @author <a href="mailto:cse@dynabean.de">Christian Sell</a>
34   * @author <a href="mailto:bcurren@esomnie.com">Ben Curren</a>
35   * @author Marc Guillemot
36   * @author David D. Kilzer
37   * @author Chris Erskine
38   * @author Hans Donner
39   * @author Paul King
40   * @author Ahmed Ashour
41   * @author Daniel Gredler
42   * @author Sudhan Moghe
43   */
44  @RunWith(BrowserRunner.class)
45  public class ScriptPreProcessorTest extends WebServerTestCase {
46  
47      /**
48       * Test the script preprocessor.
49       * @throws IOException if the test fails
50       */
51      @Test
52      public void scriptPreProcessor() throws IOException {
53          final WebClient client = getWebClient();
54          final MockWebConnection webConnection = new MockWebConnection();
55          final String alertText = "content";
56          final String newAlertText = "newcontent";
57          final String content = DOCTYPE_HTML
58              + "<html><head><title>foo</title><script>\n"
59              + "<!--\n   alert('" + alertText + "');\n// -->\n"
60              + "</script></head><body>\n"
61              + "<p>hello world</p>\n"
62              + "<form name='form1'>\n"
63              + "  <input type='text' name='textfield1' id='textfield1' value='foo' />\n"
64              + "  <input type='text' name='textfield2' id='textfield2'/>\n"
65              + "</form>\n"
66              + "</body></html>";
67  
68          webConnection.setDefaultResponse(content);
69          client.setWebConnection(webConnection);
70  
71          // Test null return from pre processor
72          client.setScriptPreProcessor(new ScriptPreProcessor() {
73              @Override
74              public String preProcess(final HtmlPage htmlPage, final String sourceCode, final String sourceName,
75                      final int lineNumber, final HtmlElement htmlElement) {
76                  return null;
77              }
78          });
79          client.setAlertHandler(new AlertHandler() {
80              @Override
81              public void handleAlert(final Page page, final String message) {
82                  fail("The pre processor did not remove the JavaScript");
83              }
84  
85          });
86          client.getPage(URL_FIRST);
87  
88          // Test modify script in pre processor
89          client.setScriptPreProcessor(new ScriptPreProcessor() {
90              @Override
91              public String preProcess(final HtmlPage htmlPage, final String sourceCode, final String sourceName,
92                      final int lineNumber, final HtmlElement htmlElement) {
93                  final int start = sourceCode.indexOf(alertText);
94                  final int end = start + alertText.length();
95  
96                  return sourceCode.substring(0, start) + newAlertText + sourceCode.substring(end);
97              }
98          });
99          client.setAlertHandler(new AlertHandler() {
100             @Override
101             public void handleAlert(final Page page, final String message) {
102                 if (!message.equals(newAlertText)) {
103                     fail("The pre processor did not modify the JavaScript");
104                 }
105             }
106 
107         });
108         client.getPage(URL_FIRST);
109     }
110 
111     /**
112      * Test the ScriptPreProcessor's ability to filter out a JavaScript method
113      * that is not implemented without affecting the rest of the page.
114      *
115      * @throws Exception if the test fails
116      */
117     @Test
118     public void scriptPreProcessor_UnimplementedJavascript() throws Exception {
119         final WebClient client = getWebClient();
120         final MockWebConnection webConnection = new MockWebConnection();
121         final String content = DOCTYPE_HTML
122             + "<html><head><title>foo</title></head><body>\n"
123             + "<p>hello world</p>\n"
124             + "<script>document.unimplementedFunction();</script>\n"
125             + "<script>alert('implemented function');</script>\n"
126             + "</body></html>";
127 
128         webConnection.setDefaultResponse(content);
129         client.setWebConnection(webConnection);
130 
131         client.setScriptPreProcessor(new ScriptPreProcessor() {
132             @Override
133             public String preProcess(final HtmlPage htmlPage, final String sourceCode, final String sourceName,
134                     final int lineNumber, final HtmlElement htmlElement) {
135                 if (sourceCode.indexOf("unimplementedFunction") > -1) {
136                     return "";
137                 }
138                 return sourceCode;
139             }
140         });
141         final List<String> alerts = new ArrayList<>();
142         client.setAlertHandler(new CollectingAlertHandler(alerts));
143         client.getPage("http://page");
144 
145         assertEquals(1, alerts.size());
146         assertEquals("implemented function", alerts.get(0).toString());
147     }
148 
149     /**
150      * Verifies that script preprocessing is applied to eval()'ed scripts (bug 2630555).
151      * @throws Exception if the test fails
152      */
153     @Test
154     public void scriptPreProcessor_Eval() throws Exception {
155         final String html = DOCTYPE_HTML
156                 + "<html><body><script>eval('aX'+'ert(\"abc\")');</script></body></html>";
157 
158         final WebClient client = getWebClient();
159         final MockWebConnection conn = new MockWebConnection();
160         conn.setDefaultResponse(html);
161         client.setWebConnection(conn);
162 
163         client.setScriptPreProcessor(new ScriptPreProcessor() {
164             @Override
165             public String preProcess(final HtmlPage p, final String src, final String srcName,
166                     final int lineNumber, final HtmlElement htmlElement) {
167                 return src.replaceAll("aXert", "alert");
168             }
169         });
170 
171         final List<String> alerts = new ArrayList<>();
172         client.setAlertHandler(new CollectingAlertHandler(alerts));
173         client.getPage(URL_FIRST);
174 
175         assertEquals(1, alerts.size());
176         assertEquals("abc", alerts.get(0));
177     }
178 }