1 // Copyright 2017 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 static org.junit.Assert.assertNotEquals; 8 9 import android.content.Context; 10 import android.content.ContextWrapper; 11 import android.content.SharedPreferences; 12 import android.graphics.Bitmap; 13 import android.graphics.Color; 14 import android.net.Uri; 15 import android.support.test.InstrumentationRegistry; 16 import android.view.ViewGroup; 17 18 import androidx.test.filters.SmallTest; 19 20 import org.hamcrest.Matchers; 21 import org.junit.After; 22 import org.junit.Assert; 23 import org.junit.Before; 24 import org.junit.Rule; 25 import org.junit.Test; 26 import org.junit.runner.RunWith; 27 28 import org.chromium.android_webview.AwBrowserContext; 29 import org.chromium.android_webview.AwContents; 30 import org.chromium.android_webview.AwContents.DependencyFactory; 31 import org.chromium.android_webview.AwContents.InternalAccessDelegate; 32 import org.chromium.android_webview.AwContents.NativeDrawFunctorFactory; 33 import org.chromium.android_webview.AwContentsClient; 34 import org.chromium.android_webview.AwContentsStatics; 35 import org.chromium.android_webview.AwSettings; 36 import org.chromium.android_webview.SafeBrowsingAction; 37 import org.chromium.android_webview.WebviewErrorCode; 38 import org.chromium.android_webview.common.AwSwitches; 39 import org.chromium.android_webview.safe_browsing.AwSafeBrowsingConfigHelper; 40 import org.chromium.android_webview.safe_browsing.AwSafeBrowsingConversionHelper; 41 import org.chromium.android_webview.safe_browsing.AwSafeBrowsingResponse; 42 import org.chromium.android_webview.test.TestAwContentsClient.OnReceivedError2Helper; 43 import org.chromium.android_webview.test.util.GraphicsTestUtils; 44 import org.chromium.base.BuildInfo; 45 import org.chromium.base.Callback; 46 import org.chromium.base.ThreadUtils; 47 import org.chromium.base.task.PostTask; 48 import org.chromium.base.test.util.CallbackHelper; 49 import org.chromium.base.test.util.CommandLineFlags; 50 import org.chromium.base.test.util.Criteria; 51 import org.chromium.base.test.util.CriteriaHelper; 52 import org.chromium.base.test.util.CriteriaNotSatisfiedException; 53 import org.chromium.base.test.util.DisabledTest; 54 import org.chromium.base.test.util.Feature; 55 import org.chromium.base.test.util.InMemorySharedPreferences; 56 import org.chromium.components.safe_browsing.SafeBrowsingApiBridge; 57 import org.chromium.components.safe_browsing.SafeBrowsingApiHandler; 58 import org.chromium.content_public.browser.UiThreadTaskTraits; 59 import org.chromium.content_public.browser.test.util.TestThreadUtils; 60 import org.chromium.content_public.common.ContentUrlConstants; 61 import org.chromium.net.test.EmbeddedTestServer; 62 import org.chromium.url.GURL; 63 64 import java.util.ArrayList; 65 import java.util.Arrays; 66 67 /** 68 * Test suite for SafeBrowsing. 69 * 70 * Ensures that interstitials can be successfully created for malicous pages. 71 */ 72 @RunWith(AwJUnit4ClassRunner.class) 73 public class SafeBrowsingTest { 74 @Rule 75 public AwActivityTestRule mActivityTestRule = new AwActivityTestRule() { 76 77 /** 78 * Creates a special BrowserContext that has a safebrowsing api handler which always says 79 * sites are malicious 80 */ 81 @Override 82 public AwBrowserContext createAwBrowserContextOnUiThread(InMemorySharedPreferences prefs) { 83 return new MockAwBrowserContext(prefs); 84 } 85 }; 86 87 private SafeBrowsingContentsClient mContentsClient; 88 private AwTestContainerView mContainerView; 89 private MockAwContents mAwContents; 90 91 private EmbeddedTestServer mTestServer; 92 93 // Used to check which thread a callback is invoked on. 94 private volatile boolean mOnUiThread; 95 96 // Used to verify the getSafeBrowsingPrivacyPolicyUrl() API. 97 private volatile Uri mPrivacyPolicyUrl; 98 99 // These colors correspond to the body.background attribute in GREEN_HTML_PATH, SAFE_HTML_PATH, 100 // MALWARE_HTML_PATH, IFRAME_HTML_PATH, etc. They should only be changed if those values are 101 // changed as well 102 private static final int GREEN_PAGE_BACKGROUND_COLOR = Color.rgb(0, 255, 0); 103 private static final int SAFE_PAGE_BACKGROUND_COLOR = Color.rgb(0, 0, 255); 104 private static final int PHISHING_PAGE_BACKGROUND_COLOR = Color.rgb(0, 0, 255); 105 private static final int MALWARE_PAGE_BACKGROUND_COLOR = Color.rgb(0, 0, 255); 106 private static final int UNWANTED_SOFTWARE_PAGE_BACKGROUND_COLOR = Color.rgb(0, 0, 255); 107 private static final int BILLING_PAGE_BACKGROUND_COLOR = Color.rgb(0, 0, 255); 108 private static final int IFRAME_EMBEDDER_BACKGROUND_COLOR = Color.rgb(10, 10, 10); 109 110 private static final String RESOURCE_PATH = "/android_webview/test/data"; 111 112 // A blank green page 113 private static final String GREEN_HTML_PATH = RESOURCE_PATH + "/green.html"; 114 115 // Blank blue pages 116 private static final String SAFE_HTML_PATH = RESOURCE_PATH + "/safe.html"; 117 private static final String PHISHING_HTML_PATH = RESOURCE_PATH + "/phishing.html"; 118 private static final String MALWARE_HTML_PATH = RESOURCE_PATH + "/malware.html"; 119 private static final String MALWARE_WITH_IMAGE_HTML_PATH = 120 RESOURCE_PATH + "/malware_with_image.html"; 121 private static final String UNWANTED_SOFTWARE_HTML_PATH = 122 RESOURCE_PATH + "/unwanted_software.html"; 123 private static final String BILLING_HTML_PATH = RESOURCE_PATH + "/billing.html"; 124 125 // A gray page with an iframe to MALWARE_HTML_PATH 126 private static final String IFRAME_HTML_PATH = RESOURCE_PATH + "/iframe.html"; 127 128 // These URLs will be CTS-tested and should not be changed. 129 private static final String WEB_UI_MALWARE_URL = "chrome://safe-browsing/match?type=malware"; 130 private static final String WEB_UI_PHISHING_URL = "chrome://safe-browsing/match?type=phishing"; 131 private static final String WEB_UI_HOST = "safe-browsing"; 132 133 /** 134 * A fake SafeBrowsingApiHandler which treats URLs ending in MALWARE_HTML_PATH as malicious URLs 135 * that should be blocked. 136 */ 137 public static class MockSafeBrowsingApiHandler implements SafeBrowsingApiHandler { 138 private Observer mObserver; 139 private static final String SAFE_METADATA = "{}"; 140 141 // These codes are defined in "safebrowsing.proto". 142 private static final int PHISHING_CODE = 5; 143 private static final int MALWARE_CODE = 4; 144 private static final int UNWANTED_SOFTWARE_CODE = 3; 145 private static final int BILLING_CODE = 15; 146 147 // Mock time it takes for a lookup request to complete. 148 private static final long CHECK_DELTA_US = 10; 149 150 @Override init(Observer result)151 public boolean init(Observer result) { 152 mObserver = result; 153 return true; 154 } 155 buildMetadataFromCode(int code)156 private String buildMetadataFromCode(int code) { 157 return "{\"matches\":[{\"threat_type\":\"" + code + "\"}]}"; 158 } 159 160 @Override startUriLookup(final long callbackId, String uri, int[] threatsOfInterest)161 public void startUriLookup(final long callbackId, String uri, int[] threatsOfInterest) { 162 final String metadata; 163 Arrays.sort(threatsOfInterest); 164 165 if (uri.endsWith(PHISHING_HTML_PATH) 166 && Arrays.binarySearch(threatsOfInterest, PHISHING_CODE) >= 0) { 167 metadata = buildMetadataFromCode(PHISHING_CODE); 168 } else if (uri.endsWith(MALWARE_HTML_PATH) 169 && Arrays.binarySearch(threatsOfInterest, MALWARE_CODE) >= 0) { 170 metadata = buildMetadataFromCode(MALWARE_CODE); 171 } else if (uri.endsWith(UNWANTED_SOFTWARE_HTML_PATH) 172 && Arrays.binarySearch(threatsOfInterest, UNWANTED_SOFTWARE_CODE) >= 0) { 173 metadata = buildMetadataFromCode(UNWANTED_SOFTWARE_CODE); 174 } else if (uri.endsWith(BILLING_HTML_PATH) 175 && Arrays.binarySearch(threatsOfInterest, BILLING_CODE) >= 0) { 176 metadata = buildMetadataFromCode(BILLING_CODE); 177 } else { 178 metadata = SAFE_METADATA; 179 } 180 181 // clang-format off 182 PostTask.runOrPostTask(UiThreadTaskTraits.DEFAULT, 183 (Runnable) () -> mObserver.onUrlCheckDone( 184 callbackId, SafeBrowsingResult.SUCCESS, metadata, CHECK_DELTA_US)); 185 // clang-format on 186 } 187 188 @Override startAllowlistLookup(final String uri, int threatType)189 public boolean startAllowlistLookup(final String uri, int threatType) { 190 return false; 191 } 192 } 193 194 /** 195 * A fake AwBrowserContext which loads the MockSafeBrowsingApiHandler instead of the real one. 196 */ 197 private static class MockAwBrowserContext extends AwBrowserContext { MockAwBrowserContext(SharedPreferences sharedPreferences)198 public MockAwBrowserContext(SharedPreferences sharedPreferences) { 199 super(sharedPreferences, 0, true); 200 SafeBrowsingApiBridge.setSafeBrowsingHandlerType(MockSafeBrowsingApiHandler.class); 201 } 202 } 203 204 private static class MockAwContents extends TestAwContents { 205 private boolean mCanShowInterstitial; 206 private boolean mCanShowBigInterstitial; 207 MockAwContents(AwBrowserContext browserContext, ViewGroup containerView, Context context, InternalAccessDelegate internalAccessAdapter, NativeDrawFunctorFactory nativeDrawFunctorFactory, AwContentsClient contentsClient, AwSettings settings, DependencyFactory dependencyFactory)208 public MockAwContents(AwBrowserContext browserContext, ViewGroup containerView, 209 Context context, InternalAccessDelegate internalAccessAdapter, 210 NativeDrawFunctorFactory nativeDrawFunctorFactory, AwContentsClient contentsClient, 211 AwSettings settings, DependencyFactory dependencyFactory) { 212 super(browserContext, containerView, context, internalAccessAdapter, 213 nativeDrawFunctorFactory, contentsClient, settings, dependencyFactory); 214 mCanShowInterstitial = true; 215 mCanShowBigInterstitial = true; 216 } 217 setCanShowInterstitial(boolean able)218 public void setCanShowInterstitial(boolean able) { 219 mCanShowInterstitial = able; 220 } 221 setCanShowBigInterstitial(boolean able)222 public void setCanShowBigInterstitial(boolean able) { 223 mCanShowBigInterstitial = able; 224 } 225 226 @Override canShowInterstitial()227 protected boolean canShowInterstitial() { 228 return mCanShowInterstitial; 229 } 230 231 @Override canShowBigInterstitial()232 protected boolean canShowBigInterstitial() { 233 return mCanShowBigInterstitial; 234 } 235 } 236 237 /** 238 * An AwContentsClient with customizable behavior for onSafeBrowsingHit(). 239 */ 240 private static class SafeBrowsingContentsClient extends TestAwContentsClient { 241 private AwWebResourceRequest mLastRequest; 242 private int mLastThreatType; 243 private int mAction = SafeBrowsingAction.SHOW_INTERSTITIAL; 244 private int mOnSafeBrowsingHitCount; 245 private boolean mReporting = true; 246 247 @Override onSafeBrowsingHit(AwWebResourceRequest request, int threatType, Callback<AwSafeBrowsingResponse> callback)248 public void onSafeBrowsingHit(AwWebResourceRequest request, int threatType, 249 Callback<AwSafeBrowsingResponse> callback) { 250 mLastRequest = request; 251 mLastThreatType = threatType; 252 mOnSafeBrowsingHitCount++; 253 callback.onResult(new AwSafeBrowsingResponse(mAction, mReporting)); 254 } 255 getLastRequest()256 public AwWebResourceRequest getLastRequest() { 257 return mLastRequest; 258 } 259 getLastThreatType()260 public int getLastThreatType() { 261 return mLastThreatType; 262 } 263 getOnSafeBrowsingHitCount()264 public int getOnSafeBrowsingHitCount() { 265 return mOnSafeBrowsingHitCount; 266 } 267 setSafeBrowsingAction(int action)268 public void setSafeBrowsingAction(int action) { 269 mAction = action; 270 } 271 setReporting(boolean value)272 public void setReporting(boolean value) { 273 mReporting = value; 274 } 275 } 276 277 private static class SafeBrowsingDependencyFactory 278 extends AwActivityTestRule.TestDependencyFactory { 279 @Override createAwContents(AwBrowserContext browserContext, ViewGroup containerView, Context context, InternalAccessDelegate internalAccessAdapter, NativeDrawFunctorFactory nativeDrawFunctorFactory, AwContentsClient contentsClient, AwSettings settings, DependencyFactory dependencyFactory)280 public AwContents createAwContents(AwBrowserContext browserContext, ViewGroup containerView, 281 Context context, InternalAccessDelegate internalAccessAdapter, 282 NativeDrawFunctorFactory nativeDrawFunctorFactory, AwContentsClient contentsClient, 283 AwSettings settings, DependencyFactory dependencyFactory) { 284 return new MockAwContents(browserContext, containerView, context, internalAccessAdapter, 285 nativeDrawFunctorFactory, contentsClient, settings, dependencyFactory); 286 } 287 } 288 289 private static class JavaScriptHelper extends CallbackHelper { 290 private String mValue; 291 setValue(String s)292 public void setValue(String s) { 293 mValue = s; 294 } 295 getValue()296 public String getValue() { 297 return mValue; 298 } 299 } 300 301 private static class AllowlistHelper extends CallbackHelper implements Callback<Boolean> { 302 public boolean success; 303 304 @Override onResult(Boolean success)305 public void onResult(Boolean success) { 306 this.success = success; 307 notifyCalled(); 308 } 309 } 310 311 @Before setUp()312 public void setUp() { 313 mContentsClient = new SafeBrowsingContentsClient(); 314 mContainerView = mActivityTestRule.createAwTestContainerViewOnMainSync( 315 mContentsClient, false, new SafeBrowsingDependencyFactory()); 316 mAwContents = (MockAwContents) mContainerView.getAwContents(); 317 318 mTestServer = EmbeddedTestServer.createAndStartServer( 319 InstrumentationRegistry.getInstrumentation().getContext()); 320 321 // Need to configure user opt-in, otherwise WebView won't perform Safe Browsing checks. 322 AwSafeBrowsingConfigHelper.setSafeBrowsingUserOptIn(true); 323 } 324 325 @After tearDown()326 public void tearDown() { 327 mTestServer.stopAndDestroyServer(); 328 } 329 getPageColor()330 private int getPageColor() { 331 Bitmap bitmap = GraphicsTestUtils.drawAwContentsOnUiThread( 332 mAwContents, mContainerView.getWidth(), mContainerView.getHeight()); 333 return bitmap.getPixel(0, 0); 334 } 335 loadGreenPage()336 private void loadGreenPage() throws Exception { 337 mActivityTestRule.loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(), 338 mTestServer.getURL(GREEN_HTML_PATH)); 339 340 // Make sure we actually wait for the page to be visible 341 mActivityTestRule.waitForVisualStateCallback(mAwContents); 342 } 343 evaluateJavaScriptOnInterstitialOnUiThread( final String script, final Callback<String> callback)344 private void evaluateJavaScriptOnInterstitialOnUiThread( 345 final String script, final Callback<String> callback) { 346 PostTask.runOrPostTask( 347 UiThreadTaskTraits.DEFAULT, () -> mAwContents.evaluateJavaScript(script, callback)); 348 } 349 evaluateJavaScriptOnInterstitialOnUiThreadSync(final String script)350 private String evaluateJavaScriptOnInterstitialOnUiThreadSync(final String script) 351 throws Exception { 352 final JavaScriptHelper helper = new JavaScriptHelper(); 353 final Callback<String> callback = value -> { 354 helper.setValue(value); 355 helper.notifyCalled(); 356 }; 357 final int count = helper.getCallCount(); 358 evaluateJavaScriptOnInterstitialOnUiThread(script, callback); 359 helper.waitForCallback(count); 360 return helper.getValue(); 361 } 362 waitForInterstitialDomToLoad()363 private void waitForInterstitialDomToLoad() { 364 final String script = "document.readyState;"; 365 final String expected = "\"complete\""; 366 367 CriteriaHelper.pollInstrumentationThread(() -> { 368 try { 369 Criteria.checkThat(evaluateJavaScriptOnInterstitialOnUiThreadSync(script), 370 Matchers.is(expected)); 371 } catch (Exception e) { 372 throw new RuntimeException(e); 373 } 374 }); 375 } 376 clickBackToSafety()377 private void clickBackToSafety() { 378 clickLinkById("primary-button"); 379 } 380 clickVisitUnsafePageQuietInterstitial()381 private void clickVisitUnsafePageQuietInterstitial() { 382 clickLinkById("details-link"); 383 clickLinkById("proceed-link"); 384 } 385 clickVisitUnsafePage()386 private void clickVisitUnsafePage() { 387 clickLinkById("details-button"); 388 clickLinkById("proceed-link"); 389 } 390 clickLinkById(String id)391 private void clickLinkById(String id) { 392 final String script = "document.getElementById('" + id + "').click();"; 393 evaluateJavaScriptOnInterstitialOnUiThread(script, null); 394 } 395 loadPathAndWaitForInterstitial(final String path)396 private void loadPathAndWaitForInterstitial(final String path) throws Exception { 397 final String responseUrl = mTestServer.getURL(path); 398 mActivityTestRule.loadUrlAsync(mAwContents, responseUrl); 399 // Subresource triggered interstitials will trigger after the page containing the 400 // subresource has loaded (and displayed), so we first wait for the interstitial to be 401 // triggered, then for a visual state callback to allow the interstitial to render. 402 CriteriaHelper.pollUiThread(() -> mAwContents.isDisplayingInterstitialForTesting()); 403 // Wait for the interstitial to actually render. 404 mActivityTestRule.waitForVisualStateCallback(mAwContents); 405 } 406 assertTargetPageHasLoaded(int pageColor)407 private void assertTargetPageHasLoaded(int pageColor) throws Exception { 408 mActivityTestRule.waitForVisualStateCallback(mAwContents); 409 Assert.assertEquals("Target page should be visible", colorToString(pageColor), 410 colorToString(GraphicsTestUtils.getPixelColorAtCenterOfView( 411 mAwContents, mContainerView))); 412 } 413 assertGreenPageShowing()414 private void assertGreenPageShowing() { 415 Assert.assertEquals("Original page should be showing", 416 colorToString(GREEN_PAGE_BACKGROUND_COLOR), 417 colorToString(GraphicsTestUtils.getPixelColorAtCenterOfView( 418 mAwContents, mContainerView))); 419 } 420 assertGreenPageNotShowing()421 private void assertGreenPageNotShowing() { 422 assertNotEquals("Original page should not be showing", 423 colorToString(GREEN_PAGE_BACKGROUND_COLOR), 424 colorToString(GraphicsTestUtils.getPixelColorAtCenterOfView( 425 mAwContents, mContainerView))); 426 } 427 assertTargetPageNotShowing(int pageColor)428 private void assertTargetPageNotShowing(int pageColor) { 429 assertNotEquals("Target page should not be showing", colorToString(pageColor), 430 colorToString(GraphicsTestUtils.getPixelColorAtCenterOfView( 431 mAwContents, mContainerView))); 432 } 433 434 /** 435 * Converts a color from the confusing integer representation to a more readable string 436 * respresentation. There is a 1:1 mapping between integer and string representations, so it's 437 * valid to compare strings directly. The string representation is better for assert output. 438 * 439 * @param color integer representation of the color 440 * @return a String representation of the color in RGBA format 441 */ colorToString(int color)442 private String colorToString(int color) { 443 return "(" + Color.red(color) + "," + Color.green(color) + "," + Color.blue(color) + "," 444 + Color.alpha(color) + ")"; 445 } 446 447 @Test 448 @SmallTest 449 @Feature({"AndroidWebView"}) testSafeBrowsingGetterAndSetter()450 public void testSafeBrowsingGetterAndSetter() throws Throwable { 451 Assert.assertTrue("Getter API should follow manifest tag by default", 452 mActivityTestRule.getAwSettingsOnUiThread(mAwContents).getSafeBrowsingEnabled()); 453 mActivityTestRule.getAwSettingsOnUiThread(mAwContents).setSafeBrowsingEnabled(false); 454 Assert.assertFalse("setSafeBrowsingEnabled(false) should change the getter", 455 mActivityTestRule.getAwSettingsOnUiThread(mAwContents).getSafeBrowsingEnabled()); 456 mActivityTestRule.getAwSettingsOnUiThread(mAwContents).setSafeBrowsingEnabled(true); 457 Assert.assertTrue("setSafeBrowsingEnabled(true) should change the getter", 458 mActivityTestRule.getAwSettingsOnUiThread(mAwContents).getSafeBrowsingEnabled()); 459 AwSafeBrowsingConfigHelper.setSafeBrowsingUserOptIn(false); 460 Assert.assertTrue("Getter API should ignore user opt-out", 461 mActivityTestRule.getAwSettingsOnUiThread(mAwContents).getSafeBrowsingEnabled()); 462 } 463 464 @Test 465 @SmallTest 466 @Feature({"AndroidWebView"}) testSafeBrowsingDoesNotBlockSafePages()467 public void testSafeBrowsingDoesNotBlockSafePages() throws Throwable { 468 loadGreenPage(); 469 final String responseUrl = mTestServer.getURL(SAFE_HTML_PATH); 470 mActivityTestRule.loadUrlSync( 471 mAwContents, mContentsClient.getOnPageFinishedHelper(), responseUrl); 472 assertTargetPageHasLoaded(SAFE_PAGE_BACKGROUND_COLOR); 473 } 474 475 @Test 476 @SmallTest 477 @Feature({"AndroidWebView"}) testSafeBrowsingBlocksUnwantedSoftwarePages()478 public void testSafeBrowsingBlocksUnwantedSoftwarePages() throws Throwable { 479 loadGreenPage(); 480 loadPathAndWaitForInterstitial(UNWANTED_SOFTWARE_HTML_PATH); 481 assertGreenPageNotShowing(); 482 assertTargetPageNotShowing(UNWANTED_SOFTWARE_PAGE_BACKGROUND_COLOR); 483 // Assume that we are rendering the interstitial, since we see neither the previous page nor 484 // the target page 485 } 486 487 @Test 488 @SmallTest 489 @Feature({"AndroidWebView"}) testSafeBrowsingBlocksBillingPages()490 public void testSafeBrowsingBlocksBillingPages() throws Throwable { 491 loadGreenPage(); 492 loadPathAndWaitForInterstitial(BILLING_HTML_PATH); 493 assertGreenPageNotShowing(); 494 assertTargetPageNotShowing(BILLING_PAGE_BACKGROUND_COLOR); 495 // Assume that we are rendering the interstitial, since we see neither the previous page nor 496 // the target page 497 } 498 499 @Test 500 @SmallTest 501 @Feature({"AndroidWebView"}) testSafeBrowsingOnSafeBrowsingHitBillingCode()502 public void testSafeBrowsingOnSafeBrowsingHitBillingCode() throws Throwable { 503 loadGreenPage(); 504 loadPathAndWaitForInterstitial(BILLING_HTML_PATH); 505 506 // Check onSafeBrowsingHit arguments 507 final String responseUrl = mTestServer.getURL(BILLING_HTML_PATH); 508 Assert.assertEquals(responseUrl, mContentsClient.getLastRequest().url); 509 // The expectedCode intentionally depends on targetSdk (and is disconnected from SDK_INT). 510 // This is for backwards compatibility with apps with a lower targetSdk. 511 int expectedCode = BuildInfo.targetsAtLeastQ() 512 ? AwSafeBrowsingConversionHelper.SAFE_BROWSING_THREAT_BILLING 513 : AwSafeBrowsingConversionHelper.SAFE_BROWSING_THREAT_UNKNOWN; 514 Assert.assertEquals(expectedCode, mContentsClient.getLastThreatType()); 515 } 516 517 @Test 518 @SmallTest 519 @Feature({"AndroidWebView"}) testSafeBrowsingBlocksPhishingPages()520 public void testSafeBrowsingBlocksPhishingPages() throws Throwable { 521 loadGreenPage(); 522 loadPathAndWaitForInterstitial(PHISHING_HTML_PATH); 523 assertGreenPageNotShowing(); 524 assertTargetPageNotShowing(PHISHING_PAGE_BACKGROUND_COLOR); 525 // Assume that we are rendering the interstitial, since we see neither the previous page nor 526 // the target page 527 } 528 529 @Test 530 @SmallTest 531 @Feature({"AndroidWebView"}) testSafeBrowsingAllowlistedUnsafePagesDontShowInterstitial()532 public void testSafeBrowsingAllowlistedUnsafePagesDontShowInterstitial() throws Throwable { 533 loadGreenPage(); 534 final String responseUrl = mTestServer.getURL(MALWARE_HTML_PATH); 535 verifyAllowlistRule(Uri.parse(responseUrl).getHost(), true); 536 mActivityTestRule.loadUrlSync( 537 mAwContents, mContentsClient.getOnPageFinishedHelper(), responseUrl); 538 assertTargetPageHasLoaded(MALWARE_PAGE_BACKGROUND_COLOR); 539 } 540 541 @Test 542 @SmallTest 543 @Feature({"AndroidWebView"}) testSafeBrowsingAllowlistHardcodedWebUiPages()544 public void testSafeBrowsingAllowlistHardcodedWebUiPages() throws Throwable { 545 loadGreenPage(); 546 verifyAllowlistRule(WEB_UI_HOST, true); 547 mActivityTestRule.loadUrlSync( 548 mAwContents, mContentsClient.getOnPageFinishedHelper(), WEB_UI_MALWARE_URL); 549 mActivityTestRule.loadUrlSync( 550 mAwContents, mContentsClient.getOnPageFinishedHelper(), WEB_UI_PHISHING_URL); 551 552 // Assume the pages are allowed, since we successfully loaded them. 553 } 554 555 @Test 556 @SmallTest 557 @Feature({"AndroidWebView"}) testSafeBrowsingAllowlistHardcodedWebUiPageBackToSafety()558 public void testSafeBrowsingAllowlistHardcodedWebUiPageBackToSafety() throws Throwable { 559 mContentsClient.setSafeBrowsingAction(SafeBrowsingAction.BACK_TO_SAFETY); 560 561 loadGreenPage(); 562 OnReceivedError2Helper errorHelper = mContentsClient.getOnReceivedError2Helper(); 563 int errorCount = errorHelper.getCallCount(); 564 mActivityTestRule.loadUrlSync( 565 mAwContents, mContentsClient.getOnPageFinishedHelper(), WEB_UI_MALWARE_URL); 566 errorHelper.waitForCallback(errorCount); 567 Assert.assertEquals( 568 WebviewErrorCode.ERROR_UNSAFE_RESOURCE, errorHelper.getError().errorCode); 569 Assert.assertEquals("Network error is for the malicious page", WEB_UI_MALWARE_URL, 570 errorHelper.getRequest().url); 571 572 assertGreenPageShowing(); 573 574 // Check onSafeBrowsingHit arguments 575 Assert.assertEquals(WEB_UI_MALWARE_URL, mContentsClient.getLastRequest().url); 576 Assert.assertEquals(AwSafeBrowsingConversionHelper.SAFE_BROWSING_THREAT_MALWARE, 577 mContentsClient.getLastThreatType()); 578 } 579 580 @Test 581 @SmallTest 582 @Feature({"AndroidWebView"}) testCallbackCalledOnSafeBrowsingBadAllowlistRule()583 public void testCallbackCalledOnSafeBrowsingBadAllowlistRule() throws Throwable { 584 verifyAllowlistRule("http://www.google.com", false); 585 } 586 587 @Test 588 @SmallTest 589 @Feature({"AndroidWebView"}) testCallbackCalledOnSafeBrowsingGoodAllowlistRule()590 public void testCallbackCalledOnSafeBrowsingGoodAllowlistRule() throws Throwable { 591 verifyAllowlistRule("www.google.com", true); 592 } 593 verifyAllowlistRule(final String rule, boolean expected)594 private void verifyAllowlistRule(final String rule, boolean expected) throws Throwable { 595 final AllowlistHelper helper = new AllowlistHelper(); 596 final int count = helper.getCallCount(); 597 TestThreadUtils.runOnUiThreadBlocking(() -> { 598 ArrayList<String> s = new ArrayList<String>(); 599 s.add(rule); 600 AwContentsStatics.setSafeBrowsingAllowlist(s, helper); 601 }); 602 helper.waitForCallback(count); 603 Assert.assertEquals(expected, helper.success); 604 } 605 606 @Test 607 @SmallTest 608 @Feature({"AndroidWebView"}) testSafeBrowsingShowsInterstitialForMainFrame()609 public void testSafeBrowsingShowsInterstitialForMainFrame() throws Throwable { 610 loadGreenPage(); 611 loadPathAndWaitForInterstitial(MALWARE_HTML_PATH); 612 assertGreenPageNotShowing(); 613 assertTargetPageNotShowing(MALWARE_PAGE_BACKGROUND_COLOR); 614 // Assume that we are rendering the interstitial, since we see neither the previous page 615 // nor the target page 616 } 617 618 @Test 619 @DisabledTest(message = "Wait for interstitial is flaky. crbug.com/1107540") 620 @SmallTest 621 @Feature({"AndroidWebView"}) testSafeBrowsingShowsInterstitialForSubresource()622 public void testSafeBrowsingShowsInterstitialForSubresource() throws Throwable { 623 loadGreenPage(); 624 loadPathAndWaitForInterstitial(IFRAME_HTML_PATH); 625 assertGreenPageNotShowing(); 626 assertTargetPageNotShowing(IFRAME_EMBEDDER_BACKGROUND_COLOR); 627 // Assume that we are rendering the interstitial, since we see neither the previous page 628 // nor the target page 629 } 630 631 @Test 632 @SmallTest 633 @Feature({"AndroidWebView"}) testSafeBrowsingProceedThroughInterstitialForMainFrame()634 public void testSafeBrowsingProceedThroughInterstitialForMainFrame() throws Throwable { 635 int pageFinishedCount = mContentsClient.getOnPageFinishedHelper().getCallCount(); 636 loadPathAndWaitForInterstitial(MALWARE_HTML_PATH); 637 waitForInterstitialDomToLoad(); 638 int onSafeBrowsingCountBeforeClick = mContentsClient.getOnSafeBrowsingHitCount(); 639 clickVisitUnsafePage(); 640 mContentsClient.getOnPageFinishedHelper().waitForCallback(pageFinishedCount); 641 assertTargetPageHasLoaded(MALWARE_PAGE_BACKGROUND_COLOR); 642 // Check there is not an extra onSafeBrowsingHit call after proceeding. 643 Assert.assertEquals( 644 onSafeBrowsingCountBeforeClick, mContentsClient.getOnSafeBrowsingHitCount()); 645 } 646 647 @Test 648 @DisabledTest(message = "Wait for interstitial is flaky. crbug.com/1107540") 649 @SmallTest 650 @Feature({"AndroidWebView"}) testSafeBrowsingProceedThroughInterstitialForSubresource()651 public void testSafeBrowsingProceedThroughInterstitialForSubresource() throws Throwable { 652 int pageFinishedCount = mContentsClient.getOnPageFinishedHelper().getCallCount(); 653 loadPathAndWaitForInterstitial(IFRAME_HTML_PATH); 654 waitForInterstitialDomToLoad(); 655 clickVisitUnsafePage(); 656 // For subresources, the initial site finishes loading before the interstitial is shown, 657 // causing an extra onPageFinished call if committed interstitials are enabled (since the 658 // proceed action triggers a reload). 659 mContentsClient.getOnPageFinishedHelper().waitForCallback(pageFinishedCount, 2); 660 assertTargetPageHasLoaded(IFRAME_EMBEDDER_BACKGROUND_COLOR); 661 } 662 663 @Test 664 @SmallTest 665 @Feature({"AndroidWebView"}) testSafeBrowsingDontProceedCausesNetworkErrorForMainFrame()666 public void testSafeBrowsingDontProceedCausesNetworkErrorForMainFrame() throws Throwable { 667 loadGreenPage(); 668 loadPathAndWaitForInterstitial(MALWARE_HTML_PATH); 669 OnReceivedError2Helper errorHelper = mContentsClient.getOnReceivedError2Helper(); 670 int errorCount = errorHelper.getCallCount(); 671 waitForInterstitialDomToLoad(); 672 clickBackToSafety(); 673 errorHelper.waitForCallback(errorCount); 674 Assert.assertEquals( 675 WebviewErrorCode.ERROR_UNSAFE_RESOURCE, errorHelper.getError().errorCode); 676 final String responseUrl = mTestServer.getURL(MALWARE_HTML_PATH); 677 Assert.assertEquals("Network error is for the malicious page", responseUrl, 678 errorHelper.getRequest().url); 679 } 680 681 @Test 682 @SmallTest 683 @Feature({"AndroidWebView"}) testSafeBrowsingDontProceedNavigatesBackForMainFrame()684 public void testSafeBrowsingDontProceedNavigatesBackForMainFrame() throws Throwable { 685 loadGreenPage(); 686 loadPathAndWaitForInterstitial(MALWARE_HTML_PATH); 687 waitForInterstitialDomToLoad(); 688 OnReceivedError2Helper errorHelper = mContentsClient.getOnReceivedError2Helper(); 689 int errorCount = errorHelper.getCallCount(); 690 clickBackToSafety(); 691 errorHelper.waitForCallback(errorCount); 692 mActivityTestRule.waitForVisualStateCallback(mAwContents); 693 assertTargetPageNotShowing(MALWARE_PAGE_BACKGROUND_COLOR); 694 assertGreenPageShowing(); 695 } 696 697 @Test 698 @DisabledTest(message = "Wait for interstitial is flaky. crbug.com/1107540") 699 @SmallTest 700 @Feature({"AndroidWebView"}) testSafeBrowsingDontProceedNavigatesBackForSubResource()701 public void testSafeBrowsingDontProceedNavigatesBackForSubResource() throws Throwable { 702 loadGreenPage(); 703 loadPathAndWaitForInterstitial(IFRAME_HTML_PATH); 704 waitForInterstitialDomToLoad(); 705 OnReceivedError2Helper errorHelper = mContentsClient.getOnReceivedError2Helper(); 706 int errorCount = errorHelper.getCallCount(); 707 clickBackToSafety(); 708 errorHelper.waitForCallback(errorCount); 709 mActivityTestRule.waitForVisualStateCallback(mAwContents); 710 assertTargetPageNotShowing(IFRAME_EMBEDDER_BACKGROUND_COLOR); 711 assertGreenPageShowing(); 712 } 713 714 @Test 715 @SmallTest 716 @Feature({"AndroidWebView"}) testSafeBrowsingCanBeDisabledPerWebview()717 public void testSafeBrowsingCanBeDisabledPerWebview() throws Throwable { 718 mActivityTestRule.getAwSettingsOnUiThread(mAwContents).setSafeBrowsingEnabled(false); 719 720 final String responseUrl = mTestServer.getURL(MALWARE_HTML_PATH); 721 mActivityTestRule.loadUrlSync( 722 mAwContents, mContentsClient.getOnPageFinishedHelper(), responseUrl); 723 assertTargetPageHasLoaded(MALWARE_PAGE_BACKGROUND_COLOR); 724 } 725 726 @Test 727 @SmallTest 728 @Feature({"AndroidWebView"}) testSafeBrowsingCanBeDisabledPerWebview_withImage()729 public void testSafeBrowsingCanBeDisabledPerWebview_withImage() throws Throwable { 730 // In particular this test checks that there is no crash when network service 731 // is enabled, safebrowsing is disabled and the RendererURLLoaderThrottle 732 // attempts to check a url through loading an image (crbug.com/889479). 733 mActivityTestRule.getAwSettingsOnUiThread(mAwContents).setSafeBrowsingEnabled(false); 734 735 final String responseUrl = mTestServer.getURL(MALWARE_WITH_IMAGE_HTML_PATH); 736 mActivityTestRule.loadUrlSync( 737 mAwContents, mContentsClient.getOnPageFinishedHelper(), responseUrl); 738 assertTargetPageHasLoaded(MALWARE_PAGE_BACKGROUND_COLOR); 739 } 740 741 @Test 742 @SmallTest 743 @Feature({"AndroidWebView"}) 744 @CommandLineFlags.Add(AwSwitches.WEBVIEW_DISABLE_SAFEBROWSING_SUPPORT) testSafeBrowsingCanBeEnabledPerWebview()745 public void testSafeBrowsingCanBeEnabledPerWebview() throws Throwable { 746 final String responseUrl = mTestServer.getURL(MALWARE_HTML_PATH); 747 mActivityTestRule.loadUrlSync( 748 mAwContents, mContentsClient.getOnPageFinishedHelper(), responseUrl); 749 assertTargetPageHasLoaded(MALWARE_PAGE_BACKGROUND_COLOR); 750 751 mActivityTestRule.getAwSettingsOnUiThread(mAwContents).setSafeBrowsingEnabled(true); 752 753 loadGreenPage(); 754 loadPathAndWaitForInterstitial(MALWARE_HTML_PATH); 755 assertGreenPageNotShowing(); 756 assertTargetPageNotShowing(MALWARE_PAGE_BACKGROUND_COLOR); 757 } 758 759 @Test 760 @SmallTest 761 @Feature({"AndroidWebView"}) testSafeBrowsingShowsNetworkErrorForInvisibleViews()762 public void testSafeBrowsingShowsNetworkErrorForInvisibleViews() throws Throwable { 763 mAwContents.setCanShowInterstitial(false); 764 mAwContents.setCanShowBigInterstitial(false); 765 final String responseUrl = mTestServer.getURL(MALWARE_HTML_PATH); 766 OnReceivedError2Helper errorHelper = mContentsClient.getOnReceivedError2Helper(); 767 int errorCount = errorHelper.getCallCount(); 768 mActivityTestRule.loadUrlAsync(mAwContents, responseUrl); 769 errorHelper.waitForCallback(errorCount); 770 Assert.assertEquals( 771 WebviewErrorCode.ERROR_UNSAFE_RESOURCE, errorHelper.getError().errorCode); 772 Assert.assertEquals("Network error is for the malicious page", responseUrl, 773 errorHelper.getRequest().url); 774 } 775 776 @Test 777 @SmallTest 778 @Feature({"AndroidWebView"}) testSafeBrowsingShowsQuietInterstitialForOddSizedViews()779 public void testSafeBrowsingShowsQuietInterstitialForOddSizedViews() throws Throwable { 780 mAwContents.setCanShowBigInterstitial(false); 781 loadGreenPage(); 782 loadPathAndWaitForInterstitial(MALWARE_HTML_PATH); 783 assertGreenPageNotShowing(); 784 assertTargetPageNotShowing(MALWARE_PAGE_BACKGROUND_COLOR); 785 } 786 787 @Test 788 @SmallTest 789 @Feature({"AndroidWebView"}) testSafeBrowsingCanShowQuietPhishingInterstitial()790 public void testSafeBrowsingCanShowQuietPhishingInterstitial() throws Throwable { 791 mAwContents.setCanShowBigInterstitial(false); 792 loadGreenPage(); 793 loadPathAndWaitForInterstitial(PHISHING_HTML_PATH); 794 assertGreenPageNotShowing(); 795 assertTargetPageNotShowing(PHISHING_PAGE_BACKGROUND_COLOR); 796 } 797 798 @Test 799 @SmallTest 800 @Feature({"AndroidWebView"}) testSafeBrowsingCanShowQuietUnwantedSoftwareInterstitial()801 public void testSafeBrowsingCanShowQuietUnwantedSoftwareInterstitial() throws Throwable { 802 mAwContents.setCanShowBigInterstitial(false); 803 loadGreenPage(); 804 loadPathAndWaitForInterstitial(UNWANTED_SOFTWARE_HTML_PATH); 805 assertGreenPageNotShowing(); 806 assertTargetPageNotShowing(UNWANTED_SOFTWARE_PAGE_BACKGROUND_COLOR); 807 } 808 809 @Test 810 @SmallTest 811 @Feature({"AndroidWebView"}) testSafeBrowsingCanShowQuietBillingInterstitial()812 public void testSafeBrowsingCanShowQuietBillingInterstitial() throws Throwable { 813 mAwContents.setCanShowBigInterstitial(false); 814 loadGreenPage(); 815 loadPathAndWaitForInterstitial(BILLING_HTML_PATH); 816 assertGreenPageNotShowing(); 817 assertTargetPageNotShowing(BILLING_PAGE_BACKGROUND_COLOR); 818 } 819 820 @Test 821 @SmallTest 822 @Feature({"AndroidWebView"}) testSafeBrowsingProceedQuietInterstitial()823 public void testSafeBrowsingProceedQuietInterstitial() throws Throwable { 824 mAwContents.setCanShowBigInterstitial(false); 825 int pageFinishedCount = mContentsClient.getOnPageFinishedHelper().getCallCount(); 826 loadPathAndWaitForInterstitial(PHISHING_HTML_PATH); 827 waitForInterstitialDomToLoad(); 828 clickVisitUnsafePageQuietInterstitial(); 829 mContentsClient.getOnPageFinishedHelper().waitForCallback(pageFinishedCount); 830 assertTargetPageHasLoaded(PHISHING_PAGE_BACKGROUND_COLOR); 831 } 832 833 @Test 834 @SmallTest 835 @Feature({"AndroidWebView"}) testSafeBrowsingOnSafeBrowsingHitShowInterstitial()836 public void testSafeBrowsingOnSafeBrowsingHitShowInterstitial() throws Throwable { 837 mContentsClient.setSafeBrowsingAction(SafeBrowsingAction.SHOW_INTERSTITIAL); 838 839 loadGreenPage(); 840 loadPathAndWaitForInterstitial(PHISHING_HTML_PATH); 841 assertGreenPageNotShowing(); 842 assertTargetPageNotShowing(PHISHING_PAGE_BACKGROUND_COLOR); 843 // Assume that we are rendering the interstitial, since we see neither the previous page nor 844 // the target page 845 846 // Check onSafeBrowsingHit arguments 847 final String responseUrl = mTestServer.getURL(PHISHING_HTML_PATH); 848 Assert.assertEquals(responseUrl, mContentsClient.getLastRequest().url); 849 Assert.assertEquals(AwSafeBrowsingConversionHelper.SAFE_BROWSING_THREAT_PHISHING, 850 mContentsClient.getLastThreatType()); 851 } 852 853 @Test 854 @SmallTest 855 @Feature({"AndroidWebView"}) testSafeBrowsingOnSafeBrowsingHitProceed()856 public void testSafeBrowsingOnSafeBrowsingHitProceed() throws Throwable { 857 mContentsClient.setSafeBrowsingAction(SafeBrowsingAction.PROCEED); 858 859 loadGreenPage(); 860 final String responseUrl = mTestServer.getURL(PHISHING_HTML_PATH); 861 mActivityTestRule.loadUrlSync( 862 mAwContents, mContentsClient.getOnPageFinishedHelper(), responseUrl); 863 mActivityTestRule.waitForVisualStateCallback(mAwContents); 864 assertTargetPageHasLoaded(PHISHING_PAGE_BACKGROUND_COLOR); 865 866 // Check onSafeBrowsingHit arguments 867 Assert.assertEquals(responseUrl, mContentsClient.getLastRequest().url); 868 Assert.assertEquals(AwSafeBrowsingConversionHelper.SAFE_BROWSING_THREAT_PHISHING, 869 mContentsClient.getLastThreatType()); 870 } 871 872 @Test 873 @SmallTest 874 @Feature({"AndroidWebView"}) testSafeBrowsingOnSafeBrowsingHitBackToSafety()875 public void testSafeBrowsingOnSafeBrowsingHitBackToSafety() throws Throwable { 876 mContentsClient.setSafeBrowsingAction(SafeBrowsingAction.BACK_TO_SAFETY); 877 878 loadGreenPage(); 879 final String responseUrl = mTestServer.getURL(MALWARE_HTML_PATH); 880 OnReceivedError2Helper errorHelper = mContentsClient.getOnReceivedError2Helper(); 881 int errorCount = errorHelper.getCallCount(); 882 mActivityTestRule.loadUrlAsync(mAwContents, responseUrl); 883 errorHelper.waitForCallback(errorCount); 884 Assert.assertEquals( 885 WebviewErrorCode.ERROR_UNSAFE_RESOURCE, errorHelper.getError().errorCode); 886 Assert.assertEquals("Network error is for the malicious page", responseUrl, 887 errorHelper.getRequest().url); 888 889 assertGreenPageShowing(); 890 891 // Check onSafeBrowsingHit arguments 892 Assert.assertEquals(responseUrl, mContentsClient.getLastRequest().url); 893 Assert.assertEquals(AwSafeBrowsingConversionHelper.SAFE_BROWSING_THREAT_MALWARE, 894 mContentsClient.getLastThreatType()); 895 } 896 897 @Test 898 @SmallTest 899 @Feature({"AndroidWebView"}) testSafeBrowsingOnSafeBrowsingHitForSubresourceNoPreviousPage()900 public void testSafeBrowsingOnSafeBrowsingHitForSubresourceNoPreviousPage() throws Throwable { 901 mContentsClient.setSafeBrowsingAction(SafeBrowsingAction.BACK_TO_SAFETY); 902 final String responseUrl = mTestServer.getURL(IFRAME_HTML_PATH); 903 final String subresourceUrl = mTestServer.getURL(MALWARE_HTML_PATH); 904 int pageFinishedCount = mContentsClient.getOnPageFinishedHelper().getCallCount(); 905 mActivityTestRule.loadUrlAsync(mAwContents, responseUrl); 906 907 // We'll successfully load IFRAME_HTML_PATH, and will soon call onSafeBrowsingHit 908 mContentsClient.getOnPageFinishedHelper().waitForCallback(pageFinishedCount); 909 910 final GURL aboutBlank = new GURL(ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL); 911 912 // Wait for the onSafeBrowsingHit to call BACK_TO_SAFETY and navigate back 913 mActivityTestRule.pollUiThread(() -> aboutBlank.equals(mAwContents.getUrl())); 914 915 // Check onSafeBrowsingHit arguments 916 Assert.assertFalse(mContentsClient.getLastRequest().isMainFrame); 917 Assert.assertEquals(subresourceUrl, mContentsClient.getLastRequest().url); 918 Assert.assertEquals(AwSafeBrowsingConversionHelper.SAFE_BROWSING_THREAT_MALWARE, 919 mContentsClient.getLastThreatType()); 920 } 921 922 @Test 923 @SmallTest 924 @Feature({"AndroidWebView"}) testSafeBrowsingOnSafeBrowsingHitForSubresource()925 public void testSafeBrowsingOnSafeBrowsingHitForSubresource() throws Throwable { 926 mContentsClient.setSafeBrowsingAction(SafeBrowsingAction.BACK_TO_SAFETY); 927 loadGreenPage(); 928 final String responseUrl = mTestServer.getURL(IFRAME_HTML_PATH); 929 final String subresourceUrl = mTestServer.getURL(MALWARE_HTML_PATH); 930 int pageFinishedCount = mContentsClient.getOnPageFinishedHelper().getCallCount(); 931 mActivityTestRule.loadUrlAsync(mAwContents, responseUrl); 932 933 // We'll successfully load IFRAME_HTML_PATH, and will soon call onSafeBrowsingHit 934 mContentsClient.getOnPageFinishedHelper().waitForCallback(pageFinishedCount); 935 936 // Wait for the onSafeBrowsingHit to call BACK_TO_SAFETY and navigate back 937 // clang-format off 938 mActivityTestRule.pollUiThread(() -> colorToString(GREEN_PAGE_BACKGROUND_COLOR).equals( 939 colorToString(GraphicsTestUtils.getPixelColorAtCenterOfView(mAwContents, 940 mContainerView)))); 941 // clang-format on 942 943 // Check onSafeBrowsingHit arguments 944 Assert.assertFalse(mContentsClient.getLastRequest().isMainFrame); 945 Assert.assertEquals(subresourceUrl, mContentsClient.getLastRequest().url); 946 Assert.assertEquals(AwSafeBrowsingConversionHelper.SAFE_BROWSING_THREAT_MALWARE, 947 mContentsClient.getLastThreatType()); 948 949 mContentsClient.setSafeBrowsingAction(SafeBrowsingAction.PROCEED); 950 951 mActivityTestRule.loadUrlSync( 952 mAwContents, mContentsClient.getOnPageFinishedHelper(), responseUrl); 953 mActivityTestRule.waitForVisualStateCallback(mAwContents); 954 assertTargetPageHasLoaded(IFRAME_EMBEDDER_BACKGROUND_COLOR); 955 956 Assert.assertFalse(mContentsClient.getLastRequest().isMainFrame); 957 Assert.assertEquals(subresourceUrl, mContentsClient.getLastRequest().url); 958 Assert.assertEquals(AwSafeBrowsingConversionHelper.SAFE_BROWSING_THREAT_MALWARE, 959 mContentsClient.getLastThreatType()); 960 } 961 962 @Test 963 @SmallTest 964 @Feature({"AndroidWebView"}) testSafeBrowsingOnSafeBrowsingHitHideReportingCheckbox()965 public void testSafeBrowsingOnSafeBrowsingHitHideReportingCheckbox() throws Throwable { 966 mContentsClient.setReporting(false); 967 loadGreenPage(); 968 loadPathAndWaitForInterstitial(PHISHING_HTML_PATH); 969 waitForInterstitialDomToLoad(); 970 971 Assert.assertFalse(getVisibilityByIdOnInterstitial("extended-reporting-opt-in")); 972 } 973 974 @Test 975 @SmallTest 976 @Feature({"AndroidWebView"}) testSafeBrowsingReportingCheckboxVisibleByDefault()977 public void testSafeBrowsingReportingCheckboxVisibleByDefault() throws Throwable { 978 loadGreenPage(); 979 loadPathAndWaitForInterstitial(PHISHING_HTML_PATH); 980 waitForInterstitialDomToLoad(); 981 982 Assert.assertTrue(getVisibilityByIdOnInterstitial("extended-reporting-opt-in")); 983 } 984 985 /** 986 * @return whether {@code domNodeId} is visible on the interstitial page. 987 * @throws Exception if the node cannot be found in the interstitial DOM or unable to evaluate 988 * JS. 989 */ getVisibilityByIdOnInterstitial(String domNodeId)990 private boolean getVisibilityByIdOnInterstitial(String domNodeId) throws Exception { 991 // clang-format off 992 final String script = 993 "(function isNodeVisible(node) {" 994 + " if (!node) return 'node not found';" 995 + " return !node.classList.contains('hidden');" 996 + "})(document.getElementById('" + domNodeId + "'))"; 997 // clang-format on 998 999 String value = evaluateJavaScriptOnInterstitialOnUiThreadSync(script); 1000 1001 if (value.equals("true")) { 1002 return true; 1003 } else if (value.equals("false")) { 1004 return false; 1005 } else { 1006 throw new Exception("Node not found"); 1007 } 1008 } 1009 1010 @Test 1011 @SmallTest 1012 @Feature({"AndroidWebView"}) testSafeBrowsingUserOptOutOverridesManifest()1013 public void testSafeBrowsingUserOptOutOverridesManifest() throws Throwable { 1014 AwSafeBrowsingConfigHelper.setSafeBrowsingUserOptIn(false); 1015 loadGreenPage(); 1016 final String responseUrl = mTestServer.getURL(MALWARE_HTML_PATH); 1017 mActivityTestRule.loadUrlSync( 1018 mAwContents, mContentsClient.getOnPageFinishedHelper(), responseUrl); 1019 assertTargetPageHasLoaded(MALWARE_PAGE_BACKGROUND_COLOR); 1020 } 1021 1022 @Test 1023 @SmallTest 1024 @Feature({"AndroidWebView"}) testSafeBrowsingUserOptOutOverridesPerWebView()1025 public void testSafeBrowsingUserOptOutOverridesPerWebView() throws Throwable { 1026 AwSafeBrowsingConfigHelper.setSafeBrowsingUserOptIn(false); 1027 mActivityTestRule.getAwSettingsOnUiThread(mAwContents).setSafeBrowsingEnabled(true); 1028 loadGreenPage(); 1029 final String responseUrl = mTestServer.getURL(MALWARE_HTML_PATH); 1030 mActivityTestRule.loadUrlSync( 1031 mAwContents, mContentsClient.getOnPageFinishedHelper(), responseUrl); 1032 assertTargetPageHasLoaded(MALWARE_PAGE_BACKGROUND_COLOR); 1033 } 1034 1035 @Test 1036 @SmallTest 1037 @Feature({"AndroidWebView"}) testSafeBrowsingHardcodedMalwareUrl()1038 public void testSafeBrowsingHardcodedMalwareUrl() throws Throwable { 1039 loadGreenPage(); 1040 mActivityTestRule.loadUrlAsync(mAwContents, WEB_UI_MALWARE_URL); 1041 // Wait for the interstitial to actually render. 1042 mActivityTestRule.waitForVisualStateCallback(mAwContents); 1043 waitForInterstitialDomToLoad(); 1044 } 1045 1046 @Test 1047 @SmallTest 1048 @Feature({"AndroidWebView"}) testSafeBrowsingHardcodedPhishingUrl()1049 public void testSafeBrowsingHardcodedPhishingUrl() throws Throwable { 1050 loadGreenPage(); 1051 mActivityTestRule.loadUrlAsync(mAwContents, WEB_UI_PHISHING_URL); 1052 // Wait for the interstitial to actually render. 1053 mActivityTestRule.waitForVisualStateCallback(mAwContents); 1054 waitForInterstitialDomToLoad(); 1055 } 1056 1057 @Test 1058 @SmallTest 1059 @Feature({"AndroidWebView"}) testSafeBrowsingHardcodedUrlsIgnoreUserOptOut()1060 public void testSafeBrowsingHardcodedUrlsIgnoreUserOptOut() throws Throwable { 1061 AwSafeBrowsingConfigHelper.setSafeBrowsingUserOptIn(false); 1062 loadGreenPage(); 1063 mActivityTestRule.loadUrlAsync(mAwContents, WEB_UI_MALWARE_URL); 1064 // Wait for the interstitial to actually render. 1065 mActivityTestRule.waitForVisualStateCallback(mAwContents); 1066 waitForInterstitialDomToLoad(); 1067 } 1068 1069 @Test 1070 @SmallTest 1071 @Feature({"AndroidWebView"}) testSafeBrowsingHardcodedUrlsRespectPerWebviewToggle()1072 public void testSafeBrowsingHardcodedUrlsRespectPerWebviewToggle() throws Throwable { 1073 mActivityTestRule.getAwSettingsOnUiThread(mAwContents).setSafeBrowsingEnabled(false); 1074 mActivityTestRule.loadUrlSync( 1075 mAwContents, mContentsClient.getOnPageFinishedHelper(), WEB_UI_MALWARE_URL); 1076 // If we get here, it means the navigation was not blocked by an interstitial. 1077 } 1078 1079 @Test 1080 @SmallTest 1081 @Feature({"AndroidWebView"}) testSafeBrowsingClickLearnMoreLink()1082 public void testSafeBrowsingClickLearnMoreLink() throws Throwable { 1083 loadInterstitialAndClickLink(PHISHING_HTML_PATH, "learn-more-link", 1084 appendLocale("https://support.google.com/chrome/?p=cpn_safe_browsing_wv")); 1085 } 1086 1087 @Test 1088 @SmallTest 1089 @Feature({"AndroidWebView"}) testSafeBrowsingClickReportErrorLink()1090 public void testSafeBrowsingClickReportErrorLink() throws Throwable { 1091 // Only phishing interstitials have the report-error-link 1092 final String reportErrorUrl = 1093 Uri.parse("https://safebrowsing.google.com/safebrowsing/report_error/") 1094 .buildUpon() 1095 .appendQueryParameter( 1096 "url", mTestServer.getURL(PHISHING_HTML_PATH).toString()) 1097 .appendQueryParameter("hl", getSafeBrowsingLocaleOnUiThreadForTesting()) 1098 .toString(); 1099 loadInterstitialAndClickLink(PHISHING_HTML_PATH, "report-error-link", reportErrorUrl); 1100 } 1101 appendLocale(String url)1102 private String appendLocale(String url) throws Exception { 1103 return Uri.parse(url) 1104 .buildUpon() 1105 .appendQueryParameter("hl", getSafeBrowsingLocaleOnUiThreadForTesting()) 1106 .toString(); 1107 } 1108 getSafeBrowsingLocaleOnUiThreadForTesting()1109 private String getSafeBrowsingLocaleOnUiThreadForTesting() throws Exception { 1110 return TestThreadUtils.runOnUiThreadBlocking( 1111 () -> AwContents.getSafeBrowsingLocaleForTesting()); 1112 } 1113 1114 @Test 1115 @SmallTest 1116 @Feature({"AndroidWebView"}) testSafeBrowsingClickDiagnosticLink()1117 public void testSafeBrowsingClickDiagnosticLink() throws Throwable { 1118 final String responseUrl = mTestServer.getURL(MALWARE_HTML_PATH); 1119 final String diagnosticUrl = 1120 Uri.parse("https://transparencyreport.google.com/safe-browsing/search") 1121 .buildUpon() 1122 .appendQueryParameter("url", responseUrl) 1123 .appendQueryParameter("hl", getSafeBrowsingLocaleOnUiThreadForTesting()) 1124 .toString(); 1125 loadInterstitialAndClickLink(MALWARE_HTML_PATH, "diagnostic-link", diagnosticUrl); 1126 } 1127 1128 @Test 1129 @SmallTest 1130 @Feature({"AndroidWebView"}) testSafeBrowsingClickWhitePaperLink()1131 public void testSafeBrowsingClickWhitePaperLink() throws Throwable { 1132 final String whitepaperUrl = 1133 Uri.parse("https://www.google.com/chrome/browser/privacy/whitepaper.html") 1134 .buildUpon() 1135 .appendQueryParameter("hl", getSafeBrowsingLocaleOnUiThreadForTesting()) 1136 .fragment("extendedreport") 1137 .toString(); 1138 loadInterstitialAndClickLink(PHISHING_HTML_PATH, "whitepaper-link", whitepaperUrl); 1139 } 1140 1141 @Test 1142 @SmallTest 1143 @Feature({"AndroidWebView"}) testSafeBrowsingClickPrivacyPolicy()1144 public void testSafeBrowsingClickPrivacyPolicy() throws Throwable { 1145 final String privacyPolicyUrl = 1146 Uri.parse("https://www.google.com/chrome/browser/privacy/") 1147 .buildUpon() 1148 .appendQueryParameter("hl", getSafeBrowsingLocaleOnUiThreadForTesting()) 1149 .fragment("safe-browsing-policies") 1150 .toString(); 1151 loadInterstitialAndClickLink(PHISHING_HTML_PATH, "privacy-link", privacyPolicyUrl); 1152 } 1153 loadInterstitialAndClickLink(String path, String linkId, String linkUrl)1154 private void loadInterstitialAndClickLink(String path, String linkId, String linkUrl) 1155 throws Exception { 1156 loadPathAndWaitForInterstitial(path); 1157 waitForInterstitialDomToLoad(); 1158 int pageFinishedCount = mContentsClient.getOnPageFinishedHelper().getCallCount(); 1159 clickLinkById(linkId); 1160 mContentsClient.getOnPageFinishedHelper().waitForCallback(pageFinishedCount); 1161 // Some click tests involve URLs that redirect and mAwContents.getUrl() sometimes 1162 // returns the post-redirect URL, so we instead check with ShouldInterceptRequest. 1163 AwContentsClient.AwWebResourceRequest requestsForUrl = 1164 mContentsClient.getShouldInterceptRequestHelper().getRequestsForUrl(linkUrl); 1165 // Make sure the URL was seen for a main frame navigation. 1166 Assert.assertTrue(requestsForUrl.isMainFrame); 1167 } 1168 1169 @Test 1170 @SmallTest 1171 @Feature({"AndroidWebView"}) testInitSafeBrowsingCallbackOnUIThread()1172 public void testInitSafeBrowsingCallbackOnUIThread() throws Throwable { 1173 Context ctx = InstrumentationRegistry.getInstrumentation() 1174 .getTargetContext() 1175 .getApplicationContext(); 1176 CallbackHelper helper = new CallbackHelper(); 1177 int count = helper.getCallCount(); 1178 mOnUiThread = false; 1179 AwContentsStatics.initSafeBrowsing(ctx, b -> { 1180 mOnUiThread = ThreadUtils.runningOnUiThread(); 1181 helper.notifyCalled(); 1182 }); 1183 helper.waitForCallback(count); 1184 // Don't run the assert on the callback's thread, since the test runner loses the stack 1185 // trace unless on the instrumentation thread. 1186 Assert.assertTrue("Callback should run on UI Thread", mOnUiThread); 1187 } 1188 1189 @Test 1190 @SmallTest 1191 @Feature({"AndroidWebView"}) testInitSafeBrowsingUsesAppContext()1192 public void testInitSafeBrowsingUsesAppContext() throws Throwable { 1193 MockContext ctx = 1194 new MockContext(InstrumentationRegistry.getInstrumentation().getTargetContext()); 1195 CallbackHelper helper = new CallbackHelper(); 1196 int count = helper.getCallCount(); 1197 1198 AwContentsStatics.initSafeBrowsing(ctx, b -> helper.notifyCalled()); 1199 helper.waitForCallback(count); 1200 Assert.assertTrue( 1201 "Should only use application context", ctx.wasGetApplicationContextCalled()); 1202 } 1203 1204 private static class MockContext extends ContextWrapper { 1205 private boolean mGetApplicationContextWasCalled; 1206 MockContext(Context context)1207 public MockContext(Context context) { 1208 super(context); 1209 } 1210 1211 @Override getApplicationContext()1212 public Context getApplicationContext() { 1213 mGetApplicationContextWasCalled = true; 1214 return super.getApplicationContext(); 1215 } 1216 wasGetApplicationContextCalled()1217 public boolean wasGetApplicationContextCalled() { 1218 return mGetApplicationContextWasCalled; 1219 } 1220 } 1221 1222 @Test 1223 @SmallTest 1224 @Feature({"AndroidWebView"}) testGetSafeBrowsingPrivacyPolicyUrl()1225 public void testGetSafeBrowsingPrivacyPolicyUrl() throws Throwable { 1226 final Uri privacyPolicyUrl = 1227 Uri.parse("https://www.google.com/chrome/browser/privacy/") 1228 .buildUpon() 1229 .appendQueryParameter("hl", getSafeBrowsingLocaleOnUiThreadForTesting()) 1230 .fragment("safe-browsing-policies") 1231 .build(); 1232 TestThreadUtils.runOnUiThreadBlocking( 1233 () -> { mPrivacyPolicyUrl = AwContentsStatics.getSafeBrowsingPrivacyPolicyUrl(); }); 1234 Assert.assertEquals(privacyPolicyUrl, this.mPrivacyPolicyUrl); 1235 Assert.assertNotNull(this.mPrivacyPolicyUrl); 1236 } 1237 1238 @Test 1239 @SmallTest 1240 @Feature({"AndroidWebView"}) testDestroyWebViewWithInterstitialShowing()1241 public void testDestroyWebViewWithInterstitialShowing() throws Throwable { 1242 loadPathAndWaitForInterstitial(MALWARE_HTML_PATH); 1243 destroyOnMainSync(); 1244 // As long as we've reached this line without crashing, there should be no bug. 1245 } 1246 destroyOnMainSync()1247 private void destroyOnMainSync() { 1248 // The AwActivityTestRule method invokes AwContents#destroy() on the main thread, but 1249 // Awcontents#destroy() posts an asynchronous task itself to destroy natives. Therefore, we 1250 // still need to wait for the real work to actually finish. 1251 mActivityTestRule.destroyAwContentsOnMainSync(mAwContents); 1252 CriteriaHelper.pollUiThread(() -> { 1253 try { 1254 int awContentsCount = TestThreadUtils.runOnUiThreadBlocking( 1255 () -> AwContents.getNativeInstanceCount()); 1256 Criteria.checkThat(awContentsCount, Matchers.is(0)); 1257 } catch (Exception e) { 1258 throw new CriteriaNotSatisfiedException(e); 1259 } 1260 }); 1261 } 1262 } 1263