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.jupiter.api.Assertions.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.junit.jupiter.api.Test;
26  
27  /**
28   * Tests for {@link ScriptPreProcessor}.
29   *
30   * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
31   * @author <a href="mailto:cse@dynabean.de">Christian Sell</a>
32   * @author <a href="mailto:bcurren@esomnie.com">Ben Curren</a>
33   * @author Marc Guillemot
34   * @author David D. Kilzer
35   * @author Chris Erskine
36   * @author Hans Donner
37   * @author Paul King
38   * @author Ahmed Ashour
39   * @author Daniel Gredler
40   * @author Sudhan Moghe
41   */
42  public class ScriptPreProcessorTest extends WebServerTestCase {
43  
44      /**
45       * Test the script preprocessor.
46       * @throws IOException if the test fails
47       */
48      @Test
49      public void scriptPreProcessor() throws IOException {
50          final WebClient client = getWebClient();
51          final MockWebConnection webConnection = new MockWebConnection();
52          final String alertText = "content";
53          final String newAlertText = "newcontent";
54          final String content = DOCTYPE_HTML
55              + "<html><head><title>foo</title><script>\n"
56              + "<!--\n   alert('" + alertText + "');\n// -->\n"
57              + "</script></head><body>\n"
58              + "<p>hello world</p>\n"
59              + "<form name='form1'>\n"
60              + "  <input type='text' name='textfield1' id='textfield1' value='foo' />\n"
61              + "  <input type='text' name='textfield2' id='textfield2'/>\n"
62              + "</form>\n"
63              + "</body></html>";
64  
65          webConnection.setDefaultResponse(content);
66          client.setWebConnection(webConnection);
67  
68          // Test null return from pre processor
69          client.setScriptPreProcessor(new ScriptPreProcessor() {
70              @Override
71              public String preProcess(final HtmlPage htmlPage, final String sourceCode, final String sourceName,
72                      final int lineNumber, final HtmlElement htmlElement) {
73                  return null;
74              }
75          });
76          client.setAlertHandler(new AlertHandler() {
77              @Override
78              public void handleAlert(final Page page, final String message) {
79                  fail("The pre processor did not remove the JavaScript");
80              }
81  
82          });
83          client.getPage(URL_FIRST);
84  
85          // Test modify script in pre processor
86          client.setScriptPreProcessor(new ScriptPreProcessor() {
87              @Override
88              public String preProcess(final HtmlPage htmlPage, final String sourceCode, final String sourceName,
89                      final int lineNumber, final HtmlElement htmlElement) {
90                  final int start = sourceCode.indexOf(alertText);
91                  final int end = start + alertText.length();
92  
93                  return sourceCode.substring(0, start) + newAlertText + sourceCode.substring(end);
94              }
95          });
96          client.setAlertHandler(new AlertHandler() {
97              @Override
98              public void handleAlert(final Page page, final String message) {
99                  if (!message.equals(newAlertText)) {
100                     fail("The pre processor did not modify the JavaScript");
101                 }
102             }
103 
104         });
105         client.getPage(URL_FIRST);
106     }
107 
108     /**
109      * Test the ScriptPreProcessor's ability to filter out a JavaScript method
110      * that is not implemented without affecting the rest of the page.
111      *
112      * @throws Exception if the test fails
113      */
114     @Test
115     public void scriptPreProcessor_UnimplementedJavascript() throws Exception {
116         final WebClient client = getWebClient();
117         final MockWebConnection webConnection = new MockWebConnection();
118         final String content = DOCTYPE_HTML
119             + "<html><head><title>foo</title></head><body>\n"
120             + "<p>hello world</p>\n"
121             + "<script>document.unimplementedFunction();</script>\n"
122             + "<script>alert('implemented function');</script>\n"
123             + "</body></html>";
124 
125         webConnection.setDefaultResponse(content);
126         client.setWebConnection(webConnection);
127 
128         client.setScriptPreProcessor(new ScriptPreProcessor() {
129             @Override
130             public String preProcess(final HtmlPage htmlPage, final String sourceCode, final String sourceName,
131                     final int lineNumber, final HtmlElement htmlElement) {
132                 if (sourceCode.indexOf("unimplementedFunction") > -1) {
133                     return "";
134                 }
135                 return sourceCode;
136             }
137         });
138         final List<String> alerts = new ArrayList<>();
139         client.setAlertHandler(new CollectingAlertHandler(alerts));
140         client.getPage("http://page");
141 
142         assertEquals(1, alerts.size());
143         assertEquals("implemented function", alerts.get(0).toString());
144     }
145 
146     /**
147      * Verifies that script preprocessing is applied to eval()'ed scripts (bug 2630555).
148      * @throws Exception if the test fails
149      */
150     @Test
151     public void scriptPreProcessor_Eval() throws Exception {
152         final String html = DOCTYPE_HTML
153                 + "<html><body><script>eval('aX'+'ert(\"abc\")');</script></body></html>";
154 
155         final WebClient client = getWebClient();
156         final MockWebConnection conn = new MockWebConnection();
157         conn.setDefaultResponse(html);
158         client.setWebConnection(conn);
159 
160         client.setScriptPreProcessor(new ScriptPreProcessor() {
161             @Override
162             public String preProcess(final HtmlPage p, final String src, final String srcName,
163                     final int lineNumber, final HtmlElement htmlElement) {
164                 return src.replaceAll("aXert", "alert");
165             }
166         });
167 
168         final List<String> alerts = new ArrayList<>();
169         client.setAlertHandler(new CollectingAlertHandler(alerts));
170         client.getPage(URL_FIRST);
171 
172         assertEquals(1, alerts.size());
173         assertEquals("abc", alerts.get(0));
174     }
175 }