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