1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 package org.chromium.android_webview.test;
6 
7 import android.net.http.SslError;
8 import android.support.test.InstrumentationRegistry;
9 
10 import androidx.test.filters.SmallTest;
11 
12 import org.junit.Assert;
13 import org.junit.Before;
14 import org.junit.Rule;
15 import org.junit.Test;
16 import org.junit.runner.RunWith;
17 
18 import org.chromium.android_webview.AwContents;
19 import org.chromium.android_webview.test.TestAwContentsClient.OnReceivedSslErrorHelper;
20 import org.chromium.base.test.util.Feature;
21 import org.chromium.net.test.EmbeddedTestServer;
22 import org.chromium.net.test.ServerCertificate;
23 
24 /**
25  * SslError tests.
26  */
27 @RunWith(AwJUnit4ClassRunner.class)
28 public class SslPreferencesTest {
29     @Rule
30     public AwActivityTestRule mActivityTestRule = new AwActivityTestRule();
31 
32     private AwTestContainerView mTestContainerView;
33     private TestAwContentsClient mContentsClient;
34     private AwTestContainerView mContainerView;
35     private AwContents mAwContents;
36     private EmbeddedTestServer mTestServer;
37 
38     private static final String HELLO_WORLD_HTML = "/android_webview/test/data/hello_world.html";
39     private static final String HELLO_WORLD_TITLE = "Hello, World!";
40 
41     @Before
setUp()42     public void setUp() {
43         mContentsClient = new TestAwContentsClient();
44         mTestContainerView = mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient);
45         mAwContents = mTestContainerView.getAwContents();
46     }
47 
48     @Test
49     @Feature({"AndroidWebView"})
50     @SmallTest
testSslErrorNotCalledForOkCert()51     public void testSslErrorNotCalledForOkCert() throws Throwable {
52         mTestServer = EmbeddedTestServer.createAndStartHTTPSServer(
53                 InstrumentationRegistry.getInstrumentation().getContext(),
54                 ServerCertificate.CERT_OK);
55         try {
56             final String pageUrl = mTestServer.getURL(HELLO_WORLD_HTML);
57             final OnReceivedSslErrorHelper onReceivedSslErrorHelper =
58                     mContentsClient.getOnReceivedSslErrorHelper();
59 
60             int onSslErrorCallCount = onReceivedSslErrorHelper.getCallCount();
61 
62             mContentsClient.setAllowSslError(true);
63             mActivityTestRule.loadUrlSync(
64                     mAwContents, mContentsClient.getOnPageFinishedHelper(), pageUrl);
65 
66             if (onSslErrorCallCount != onReceivedSslErrorHelper.getCallCount()) {
67                 Assert.fail("onReceivedSslError should not be called, but was called with error "
68                         + onReceivedSslErrorHelper.getError());
69             }
70         } finally {
71             mTestServer.stopAndDestroyServer();
72         }
73     }
74 
75     @Test
76     @Feature({"AndroidWebView"})
77     @SmallTest
testSslErrorMismatchedName()78     public void testSslErrorMismatchedName() throws Throwable {
79         mTestServer = EmbeddedTestServer.createAndStartHTTPSServer(
80                 InstrumentationRegistry.getInstrumentation().getContext(),
81                 ServerCertificate.CERT_MISMATCHED_NAME);
82         try {
83             final String pageUrl = mTestServer.getURL(HELLO_WORLD_HTML);
84             final OnReceivedSslErrorHelper onReceivedSslErrorHelper =
85                     mContentsClient.getOnReceivedSslErrorHelper();
86 
87             int onSslErrorCallCount = onReceivedSslErrorHelper.getCallCount();
88 
89             mContentsClient.setAllowSslError(true);
90             mActivityTestRule.loadUrlSync(
91                     mAwContents, mContentsClient.getOnPageFinishedHelper(), pageUrl);
92 
93             Assert.assertEquals("onReceivedSslError should be called once", onSslErrorCallCount + 1,
94                     onReceivedSslErrorHelper.getCallCount());
95 
96             SslError error = onReceivedSslErrorHelper.getError();
97             Assert.assertTrue("Expected SSL_IDMISMATCH", error.hasError(SslError.SSL_IDMISMATCH));
98         } finally {
99             mTestServer.stopAndDestroyServer();
100         }
101     }
102 
103     @Test
104     @Feature({"AndroidWebView"})
105     @SmallTest
testSslErrorInvalidDate()106     public void testSslErrorInvalidDate() throws Throwable {
107         mTestServer = EmbeddedTestServer.createAndStartHTTPSServer(
108                 InstrumentationRegistry.getInstrumentation().getContext(),
109                 // WebView currently returns DATE_INVALID instead of SSL_EXPIRED (see SslUtil.java).
110                 ServerCertificate.CERT_EXPIRED);
111         try {
112             final String pageUrl = mTestServer.getURL(HELLO_WORLD_HTML);
113             final OnReceivedSslErrorHelper onReceivedSslErrorHelper =
114                     mContentsClient.getOnReceivedSslErrorHelper();
115 
116             int onSslErrorCallCount = onReceivedSslErrorHelper.getCallCount();
117 
118             mContentsClient.setAllowSslError(true);
119             mActivityTestRule.loadUrlSync(
120                     mAwContents, mContentsClient.getOnPageFinishedHelper(), pageUrl);
121 
122             Assert.assertEquals("onReceivedSslError should be called once", onSslErrorCallCount + 1,
123                     onReceivedSslErrorHelper.getCallCount());
124 
125             SslError error = onReceivedSslErrorHelper.getError();
126             Assert.assertTrue(
127                     "Expected SSL_DATE_INVALID", error.hasError(SslError.SSL_DATE_INVALID));
128         } finally {
129             mTestServer.stopAndDestroyServer();
130         }
131     }
132 
133     @Test
134     @Feature({"AndroidWebView"})
135     @SmallTest
testSslErrorCommonNameOnly()136     public void testSslErrorCommonNameOnly() throws Throwable {
137         mTestServer = EmbeddedTestServer.createAndStartHTTPSServer(
138                 InstrumentationRegistry.getInstrumentation().getContext(),
139                 ServerCertificate.CERT_COMMON_NAME_ONLY);
140         try {
141             final String pageUrl = mTestServer.getURL(HELLO_WORLD_HTML);
142             final OnReceivedSslErrorHelper onReceivedSslErrorHelper =
143                     mContentsClient.getOnReceivedSslErrorHelper();
144 
145             int onSslErrorCallCount = onReceivedSslErrorHelper.getCallCount();
146 
147             mContentsClient.setAllowSslError(true);
148             mActivityTestRule.loadUrlSync(
149                     mAwContents, mContentsClient.getOnPageFinishedHelper(), pageUrl);
150 
151             Assert.assertEquals("onReceivedSslError should be called once", onSslErrorCallCount + 1,
152                     onReceivedSslErrorHelper.getCallCount());
153 
154             SslError error = onReceivedSslErrorHelper.getError();
155             Assert.assertTrue("Expected SSL_IDMISMATCH", error.hasError(SslError.SSL_IDMISMATCH));
156         } finally {
157             mTestServer.stopAndDestroyServer();
158         }
159     }
160 
161     // onReceivedSslError() does not imply any callbacks other than onPageFinished().
162     @Test
163     @Feature({"AndroidWebView"})
164     @SmallTest
testCancelSslErrorDoesNotCallOtherCallbacks()165     public void testCancelSslErrorDoesNotCallOtherCallbacks() throws Throwable {
166         mTestServer = EmbeddedTestServer.createAndStartHTTPSServer(
167                 InstrumentationRegistry.getInstrumentation().getContext(),
168                 ServerCertificate.CERT_EXPIRED);
169         try {
170             final String pageUrl = mTestServer.getURL(HELLO_WORLD_HTML);
171             final OnReceivedSslErrorHelper onReceivedSslErrorHelper =
172                     mContentsClient.getOnReceivedSslErrorHelper();
173 
174             int onSslErrorCallCount = onReceivedSslErrorHelper.getCallCount();
175             int errorCount = mContentsClient.getOnReceivedError2Helper().getCallCount();
176             int httpErrorCount = mContentsClient.getOnReceivedHttpErrorHelper().getCallCount();
177 
178             // Load the page and cancel the SslError
179             mContentsClient.setAllowSslError(false);
180             mActivityTestRule.loadUrlSync(
181                     mAwContents, mContentsClient.getOnPageFinishedHelper(), pageUrl);
182 
183             Assert.assertEquals("onReceivedSslError should be called once", onSslErrorCallCount + 1,
184                     onReceivedSslErrorHelper.getCallCount());
185             Assert.assertEquals("Canceled SslErrors should not trigger network errors", errorCount,
186                     mContentsClient.getOnReceivedError2Helper().getCallCount());
187             Assert.assertEquals("Canceled SslErrors should not trigger HTTP errors", httpErrorCount,
188                     mContentsClient.getOnReceivedHttpErrorHelper().getCallCount());
189 
190             // Same thing, but allow the SslError this time
191             mContentsClient.setAllowSslError(true);
192             mActivityTestRule.loadUrlSync(
193                     mAwContents, mContentsClient.getOnPageFinishedHelper(), pageUrl);
194 
195             Assert.assertEquals("onReceivedSslError should be called a second time",
196                     onSslErrorCallCount + 2, onReceivedSslErrorHelper.getCallCount());
197             Assert.assertEquals("Allowed SslErrors should not trigger network errors", errorCount,
198                     mContentsClient.getOnReceivedError2Helper().getCallCount());
199             Assert.assertEquals("Allowed SslErrors should not trigger HTTP errors", httpErrorCount,
200                     mContentsClient.getOnReceivedHttpErrorHelper().getCallCount());
201         } finally {
202             mTestServer.stopAndDestroyServer();
203         }
204     }
205 
206     @Test
207     @Feature({"AndroidWebView"})
208     @SmallTest
testAllowSslErrorShowsPage()209     public void testAllowSslErrorShowsPage() throws Throwable {
210         mTestServer = EmbeddedTestServer.createAndStartHTTPSServer(
211                 InstrumentationRegistry.getInstrumentation().getContext(),
212                 ServerCertificate.CERT_EXPIRED);
213         try {
214             final String pageUrl = mTestServer.getURL(HELLO_WORLD_HTML);
215             final OnReceivedSslErrorHelper onReceivedSslErrorHelper =
216                     mContentsClient.getOnReceivedSslErrorHelper();
217 
218             int onSslErrorCallCount = onReceivedSslErrorHelper.getCallCount();
219 
220             mContentsClient.setAllowSslError(true);
221             mActivityTestRule.loadUrlSync(
222                     mAwContents, mContentsClient.getOnPageFinishedHelper(), pageUrl);
223 
224             Assert.assertEquals("onReceivedSslError should be called once", onSslErrorCallCount + 1,
225                     onReceivedSslErrorHelper.getCallCount());
226 
227             // Assert the page has successfully loaded, which can be indicated by changing the page
228             // title.
229             Assert.assertEquals("Page has loaded and set the title", HELLO_WORLD_TITLE,
230                     mActivityTestRule.getTitleOnUiThread(mAwContents));
231         } finally {
232             mTestServer.stopAndDestroyServer();
233         }
234     }
235 
236     @Test
237     @Feature({"AndroidWebView"})
238     @SmallTest
testCancelSslErrorBlocksPage()239     public void testCancelSslErrorBlocksPage() throws Throwable {
240         mTestServer = EmbeddedTestServer.createAndStartHTTPSServer(
241                 InstrumentationRegistry.getInstrumentation().getContext(),
242                 ServerCertificate.CERT_EXPIRED);
243         try {
244             final String pageUrl = mTestServer.getURL(HELLO_WORLD_HTML);
245             final OnReceivedSslErrorHelper onReceivedSslErrorHelper =
246                     mContentsClient.getOnReceivedSslErrorHelper();
247 
248             int onSslErrorCallCount = onReceivedSslErrorHelper.getCallCount();
249 
250             mContentsClient.setAllowSslError(false);
251             mActivityTestRule.loadUrlSync(
252                     mAwContents, mContentsClient.getOnPageFinishedHelper(), pageUrl);
253 
254             Assert.assertEquals("onReceivedSslError should be called once", onSslErrorCallCount + 1,
255                     onReceivedSslErrorHelper.getCallCount());
256 
257             // Assert the page did not load. This is generally hard to check, so we instead check
258             // that the title is the empty string (as the real HTML sets the title to
259             // HELLO_WORLD_TITLE).
260             Assert.assertEquals("Page should not be loaded and title should be empty", "",
261                     mActivityTestRule.getTitleOnUiThread(mAwContents));
262         } finally {
263             mTestServer.stopAndDestroyServer();
264         }
265     }
266 
267     // If the user allows the ssl error, the same ssl error will not trigger the onReceivedSslError
268     // callback.
269     @Test
270     @Feature({"AndroidWebView"})
271     @SmallTest
testAllowSslErrorIsRemembered()272     public void testAllowSslErrorIsRemembered() throws Throwable {
273         mTestServer = EmbeddedTestServer.createAndStartHTTPSServer(
274                 InstrumentationRegistry.getInstrumentation().getContext(),
275                 ServerCertificate.CERT_EXPIRED);
276         try {
277             final String pageUrl = mTestServer.getURL(HELLO_WORLD_HTML);
278             final OnReceivedSslErrorHelper onReceivedSslErrorHelper =
279                     mContentsClient.getOnReceivedSslErrorHelper();
280 
281             int onSslErrorCallCount = onReceivedSslErrorHelper.getCallCount();
282 
283             mContentsClient.setAllowSslError(true);
284             mActivityTestRule.loadUrlSync(
285                     mAwContents, mContentsClient.getOnPageFinishedHelper(), pageUrl);
286 
287             Assert.assertEquals("onReceivedSslError should be called once", onSslErrorCallCount + 1,
288                     onReceivedSslErrorHelper.getCallCount());
289 
290             // Now load the page again. This time, we expect no ssl error, because
291             // user's decision should be remembered.
292             onSslErrorCallCount = onReceivedSslErrorHelper.getCallCount();
293             mActivityTestRule.loadUrlSync(
294                     mAwContents, mContentsClient.getOnPageFinishedHelper(), pageUrl);
295             Assert.assertEquals("onReceivedSslError should not be called again",
296                     onSslErrorCallCount, onReceivedSslErrorHelper.getCallCount());
297         } finally {
298             mTestServer.stopAndDestroyServer();
299         }
300     }
301 
302     // If the user cancels the ssl error, the same ssl error should trigger the onReceivedSslError
303     // callback again.
304     @Test
305     @Feature({"AndroidWebView"})
306     @SmallTest
testCancelSslErrorIsRemembered()307     public void testCancelSslErrorIsRemembered() throws Throwable {
308         mTestServer = EmbeddedTestServer.createAndStartHTTPSServer(
309                 InstrumentationRegistry.getInstrumentation().getContext(),
310                 ServerCertificate.CERT_EXPIRED);
311         try {
312             final String pageUrl = mTestServer.getURL(HELLO_WORLD_HTML);
313             final OnReceivedSslErrorHelper onReceivedSslErrorHelper =
314                     mContentsClient.getOnReceivedSslErrorHelper();
315 
316             int onSslErrorCallCount = onReceivedSslErrorHelper.getCallCount();
317 
318             mContentsClient.setAllowSslError(false);
319             mActivityTestRule.loadUrlSync(
320                     mAwContents, mContentsClient.getOnPageFinishedHelper(), pageUrl);
321 
322             Assert.assertEquals("onReceivedSslError should be called once", onSslErrorCallCount + 1,
323                     onReceivedSslErrorHelper.getCallCount());
324             SslError error = onReceivedSslErrorHelper.getError();
325             Assert.assertTrue(
326                     "Expected SSL_DATE_INVALID", error.hasError(SslError.SSL_DATE_INVALID));
327 
328             // Now load the page again. This time, we expect the same ssl error, because
329             // user's decision should be remembered.
330             mActivityTestRule.loadUrlSync(
331                     mAwContents, mContentsClient.getOnPageFinishedHelper(), pageUrl);
332             Assert.assertEquals("onReceivedSslError should be called a second time",
333                     onSslErrorCallCount + 2, onReceivedSslErrorHelper.getCallCount());
334             // And that error should have the same error code.
335             error = onReceivedSslErrorHelper.getError();
336             Assert.assertTrue(
337                     "Expected SSL_DATE_INVALID", error.hasError(SslError.SSL_DATE_INVALID));
338         } finally {
339             mTestServer.stopAndDestroyServer();
340         }
341     }
342 }
343