Index: android_webview/javatests/src/org/chromium/android_webview/test/SafeBrowsingTest.java |
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/SafeBrowsingTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/SafeBrowsingTest.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..d49a5d1547efeb75e5e9d57183f17c7a0875744c |
--- /dev/null |
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/SafeBrowsingTest.java |
@@ -0,0 +1,174 @@ |
+// Copyright 2017 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+package org.chromium.android_webview.test; |
+ |
+import android.content.Context; |
+import android.content.SharedPreferences; |
+import android.graphics.Bitmap; |
+import android.support.test.filters.SmallTest; |
+import android.util.AndroidRuntimeException; |
+ |
+import org.chromium.android_webview.AwBrowserContext; |
+import org.chromium.android_webview.AwContents; |
+import org.chromium.android_webview.test.util.GraphicsTestUtils; |
+import org.chromium.base.test.util.Feature; |
+import org.chromium.base.test.util.InMemorySharedPreferences; |
+import org.chromium.components.safe_browsing.SafeBrowsingApiBridge; |
+import org.chromium.components.safe_browsing.SafeBrowsingApiHandler; |
+import org.chromium.net.test.util.TestWebServer; |
+ |
+import java.util.regex.Pattern; |
+ |
+/** |
+ * Test suite for SafeBrowsing. |
+ * |
+ * Ensures that interstitials can be successfully created for malicous pages. |
+ */ |
+public class SafeBrowsingTest extends AwTestBase { |
+ private TestAwContentsClient mContentsClient; |
+ private AwTestContainerView mContainerView; |
+ private AwContents mAwContents; |
+ private MockAwBrowserContext mBrowserContext; |
+ |
+ // The background color of the safebrowsing interstitial HTML |
+ private static final int INTERSTITIAL_COLOR = 0xffce3426; |
+ |
+ private static final String SIMPLE_RESPONSE_DATA = "<html></html>"; |
+ private static final int WAIT_TIMEOUT_MS = 10000; |
+ private static final String SAFE_URL = "/safe"; |
+ private static final String MALWARE_URL = "/malware"; |
+ |
+ /** |
+ * A fake SafeBrowsingApiHandler which treats URLs ending in SAFE_URL as safe and those ending |
+ * in MALWARE_URL as malicious URLs that should be blocked. |
+ */ |
+ public static class MockSafeBrowsingApiHandler implements SafeBrowsingApiHandler { |
+ private Observer mObserver; |
+ private static final String SAFE_METADATA = "{}"; |
+ private static final String MALWARE_METADATA = "{\"matches\":[{\"threat_type\":\"5\"}]}"; |
+ |
+ @Override |
+ public boolean init(Context context, Observer result) { |
+ mObserver = result; |
+ return true; |
+ } |
+ |
+ @Override |
+ public void startUriLookup(long callbackId, String uri, int[] threatsOfInterest) { |
+ int resultStatus = STATUS_SUCCESS; |
+ String metadata = isMaliciousUrl(uri) ? MALWARE_METADATA : SAFE_METADATA; |
+ |
+ mObserver.onUrlCheckDone(callbackId, resultStatus, metadata); |
+ } |
+ |
+ private static boolean isMaliciousUrl(String uri) { |
+ return Pattern.compile(".*" + MALWARE_URL).matcher(uri).matches(); |
boliu
2017/02/14 02:34:32
endsWith good enough?
Nate Fischer
2017/02/14 17:17:55
That's better, thanks. Done
|
+ } |
+ } |
+ |
+ /** |
+ * A fake AwBrowserContext which loads the MockSafeBrowsingApiHandler instead of the real one. |
+ */ |
+ private static class MockAwBrowserContext extends AwBrowserContext { |
+ public MockAwBrowserContext( |
+ SharedPreferences sharedPreferences, Context applicationContext) { |
+ super(sharedPreferences, applicationContext); |
+ SafeBrowsingApiBridge.setSafeBrowsingHandlerType(MockSafeBrowsingApiHandler.class); |
+ } |
+ } |
+ |
+ @Override |
+ public void setUp() throws Exception { |
+ super.setUp(); |
+ mContentsClient = new TestAwContentsClient(); |
+ mContainerView = createAwTestContainerViewOnMainSync(mContentsClient); |
+ mAwContents = mContainerView.getAwContents(); |
+ } |
+ |
+ /** |
+ * Creates a special BrowserContext that has a safebrowsing api handler which always says |
+ * sites are malicious |
+ */ |
+ @Override |
+ protected void createAwBrowserContext() { |
+ if (mBrowserContext != null) { |
+ throw new AndroidRuntimeException("There should only be one browser context."); |
+ } |
+ getActivity(); // The Activity must be launched in order to load native code |
+ final InMemorySharedPreferences prefs = new InMemorySharedPreferences(); |
+ final Context appContext = getInstrumentation().getTargetContext().getApplicationContext(); |
boliu
2017/02/14 02:34:31
this is a lot of copied code, meaning AwTestBase i
Nate Fischer
2017/02/14 17:17:55
I broke it into 3 methods. The intermediate method
|
+ getInstrumentation().runOnMainSync(new Runnable() { |
+ @Override |
+ public void run() { |
+ mBrowserContext = new MockAwBrowserContext(prefs, appContext); |
+ } |
+ }); |
+ } |
+ |
+ private boolean isShowingInterstitial() { |
+ Bitmap bitmap = GraphicsTestUtils.drawAwContentsOnUiThread( |
+ mAwContents, mContainerView.getWidth(), mContainerView.getHeight()); |
+ return (bitmap.getPixel(0, 0) == INTERSTITIAL_COLOR); |
boliu
2017/02/14 02:34:31
where is this color from?
Nate Fischer
2017/02/14 17:17:55
It's the background color of the interstitial HTML
boliu
2017/02/14 18:31:40
It's not ok to depend on arbitrary values in far o
|
+ } |
+ |
+ @SmallTest |
+ @Feature({"AndroidWebView"}) |
+ public void testSafeBrowsingDoesNotBlockSafePages() throws Throwable { |
+ TestWebServer webServer = TestWebServer.start(); |
+ |
+ final String responseUrl = webServer.setResponse(SAFE_URL, SIMPLE_RESPONSE_DATA, null); |
+ try { |
+ loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(), responseUrl); |
+ } finally { |
+ webServer.shutdown(); |
+ } |
+ |
+ assertFalse("Should not create an interstitial", isShowingInterstitial()); |
+ } |
+ |
+ @SmallTest |
+ @Feature({"AndroidWebView"}) |
+ public void testSafeBrowsingShowsInterstitialForMalware() throws Throwable { |
+ TestWebServer webServer = TestWebServer.start(); |
+ |
+ final String responseUrl = webServer.setResponse(MALWARE_URL, SIMPLE_RESPONSE_DATA, null); |
+ try { |
+ // loadUrlSync never finishes if an interstitial gets shown, so we have to call the |
+ // async version |
+ loadUrlAsync(mAwContents, responseUrl); |
+ Thread.sleep(WAIT_TIMEOUT_MS); |
boliu
2017/02/14 02:34:32
no sleeps
you can wait for startUriLookup to be c
Nate Fischer
2017/02/14 17:17:55
I wasn't sure how to implement what you suggested,
|
+ } finally { |
+ webServer.shutdown(); |
+ } |
+ |
+ assertTrue("Should create an interstitial", isShowingInterstitial()); |
+ } |
+ |
+ @SmallTest |
+ @Feature({"AndroidWebView"}) |
+ public void testSafeBrowsingMaliciousSubresourceShowsInterstitial() throws Throwable { |
+ TestWebServer webServer = TestWebServer.start(); |
+ |
+ final String iframeUrl = webServer.setResponse(MALWARE_URL, SIMPLE_RESPONSE_DATA, null); |
+ |
+ final String responseData = "<html>" |
+ + " <body bgcolor=\"#E6E6FA\">" |
+ + " <p>This is outside the iframe</p>" |
+ + " <iframe src=\"" + iframeUrl + "\"></iframe>" |
+ + " </body>" |
+ + "</html>"; |
+ final String responseUrl = webServer.setResponse(SAFE_URL, responseData, null); |
+ try { |
+ // loadUrlSync never finishes if an interstitial gets shown, so we have to call the |
+ // async version |
+ loadUrlAsync(mAwContents, responseUrl); |
+ Thread.sleep(WAIT_TIMEOUT_MS); |
boliu
2017/02/14 02:34:32
ditto
Nate Fischer
2017/02/14 17:17:55
Done
|
+ } finally { |
+ webServer.shutdown(); |
+ } |
+ |
+ assertTrue("Should create an interstitial", isShowingInterstitial()); |
+ } |
+} |