44
55package org .chromium .android_webview .test ;
66
7+ import android .webkit .JavascriptInterface ;
8+
79import androidx .test .filters .SmallTest ;
810
11+ import com .google .common .util .concurrent .Futures ;
12+ import com .google .common .util .concurrent .ListenableFuture ;
13+
914import org .junit .After ;
1015import org .junit .Assert ;
1116import org .junit .Before ;
1419import org .junit .runner .RunWith ;
1520
1621import org .chromium .android_webview .AwContents ;
22+ import org .chromium .android_webview .test .TestAwContentsClient .ShouldInterceptRequestHelper ;
1723import org .chromium .base .test .util .Batch ;
24+ import org .chromium .base .test .util .CallbackHelper ;
1825import org .chromium .base .test .util .CommandLineFlags ;
19- import org .chromium .content_public .common .ContentFeatures ;
26+ import org .chromium .blink_public .common .BlinkFeatures ;
27+ import org .chromium .components .embedder_support .util .WebResourceResponseInfo ;
28+ import org .chromium .components .environment_integrity .IntegrityServiceBridge ;
29+ import org .chromium .components .environment_integrity .IntegrityServiceBridgeDelegate ;
2030import org .chromium .net .test .util .TestWebServer ;
2131
32+ import java .io .ByteArrayInputStream ;
33+ import java .nio .charset .StandardCharsets ;
2234import java .util .Collections ;
35+ import java .util .Map ;
36+ import java .util .concurrent .TimeUnit ;
37+ import java .util .concurrent .TimeoutException ;
2338
2439/**
2540 * Tests for WebEnvironmentIntegrity in WebView.
2944 * and only supposed to test WebView-specific differences.
3045 */
3146@ RunWith (AwJUnit4ClassRunner .class )
32- @ CommandLineFlags .Add ({"enable-features=" + ContentFeatures .WEB_ENVIRONMENT_INTEGRITY })
3347@ Batch (Batch .PER_CLASS )
3448public class AwWebEnvironmentIntegrityTest {
3549 @ Rule
@@ -39,6 +53,17 @@ public class AwWebEnvironmentIntegrityTest {
3953 private AwContents mAwContents ;
4054
4155 private TestWebServer mWebServer ;
56+ private static final String ORIGIN_TRIAL_URL = "https://example.com/" ;
57+ private static final String ORIGIN_TRIAL_HEADER = "Origin-Trial" ;
58+ private static final String ORIGIN_TRIAL_TOKEN =
59+ "A1GBGCeaLBRlky1ITf9uRak5iluqLWnUdSTKVTO0Ce/I7a35nik6DKqPJNZSPd9KEAIuJKmi2dmL9HWThDWgdA"
60+ + "cAAABheyJvcmlnaW4iOiAiaHR0cHM6Ly9leGFtcGxlLmNvbTo0NDMiLCAiZmVhdHVyZSI6ICJXZWJFbnZpcm"
61+ + "9ubWVudEludGVncml0eSIsICJleHBpcnkiOiAyMDAwMDAwMDAwfQ==" ;
62+
63+ private static final long HANDLE = 123456789L ;
64+
65+ private static final byte [] TOKEN = {1 , 2 , 3 , 4 };
66+ private static final String TOKEN_BASE64 = "AQIDBA==" ;
4267
4368 @ Before
4469 public void setUp () throws Exception {
@@ -58,6 +83,24 @@ public void tearDown() throws Exception {
5883
5984 @ Test
6085 @ SmallTest
86+ public void testWebEnvironmentIntegrityApiNotAvailableByDefault () throws Throwable {
87+ // Load a web page from localhost to get a secure context
88+ mWebServer .setResponse ("/" , "<html>" , Collections .emptyList ());
89+ mActivityTestRule .loadUrlSync (
90+ mAwContents , mContentsClient .getOnPageFinishedHelper (), mWebServer .getBaseUrl ());
91+ // Check that the 'getEnvironmentIntegrity' method is available.
92+ final String script = "'getEnvironmentIntegrity' in navigator ? 'available': 'missing'" ;
93+ String result = mActivityTestRule .executeJavaScriptAndWaitForResult (
94+ mAwContents , mContentsClient , script );
95+ // The result is expected to have extra quotes as a JSON-encoded string.
96+ Assert .assertEquals ("This test is expected to fail if runtime_enabled_features.json5"
97+ + " is updated to mark the feature as 'stable'." ,
98+ "\" missing\" " , result );
99+ }
100+
101+ @ Test
102+ @ SmallTest
103+ @ CommandLineFlags .Add ({"enable-features=" + BlinkFeatures .WEB_ENVIRONMENT_INTEGRITY })
61104 public void testWebEnvironmentIntegrityApiAvailable () throws Throwable {
62105 // Load a web page from localhost to get a secure context
63106 mWebServer .setResponse ("/" , "<html>" , Collections .emptyList ());
@@ -70,4 +113,103 @@ public void testWebEnvironmentIntegrityApiAvailable() throws Throwable {
70113 // The result is expected to have extra quotes as a JSON-encoded string.
71114 Assert .assertEquals ("\" available\" " , result );
72115 }
116+
117+ @ Test
118+ @ SmallTest
119+ @ CommandLineFlags .Add ({"disable-features=" + BlinkFeatures .WEB_ENVIRONMENT_INTEGRITY })
120+ public void testWebEnvironmentIntegrityApiCanBeDisabled () throws Throwable {
121+ // Load a web page from localhost to get a secure context
122+ mWebServer .setResponse ("/" , "<html>" , Collections .emptyList ());
123+ mActivityTestRule .loadUrlSync (
124+ mAwContents , mContentsClient .getOnPageFinishedHelper (), mWebServer .getBaseUrl ());
125+ // Check that the 'getEnvironmentIntegrity' method is available.
126+ final String script = "'getEnvironmentIntegrity' in navigator ? 'available': 'missing'" ;
127+ String result = mActivityTestRule .executeJavaScriptAndWaitForResult (
128+ mAwContents , mContentsClient , script );
129+ // The result is expected to have extra quotes as a JSON-encoded string.
130+ Assert .assertEquals ("\" missing\" " , result );
131+ }
132+
133+ @ Test
134+ @ SmallTest
135+ @ CommandLineFlags .Add ({"origin-trial-public-key=dRCs+TocuKkocNKa0AtZ4awrt9XKH2SQCI6o4FY6BNA=" })
136+ public void testAppIdentityEnabledByOriginTrial () throws Throwable {
137+ // Set up a response with the origin trial header.
138+ // Since origin trial tokens are tied to the origin, we use an request intercept to load
139+ // the content when making a request to the origin trial URL, instead of relying on the
140+ // server, which serves from an unknown port.
141+ var body = new ByteArrayInputStream (
142+ "<!DOCTYPE html><html><body>Hello, World" .getBytes (StandardCharsets .UTF_8 ));
143+ var responseInfo = new WebResourceResponseInfo ("text/html" , "utf-8" , body , 200 , "OK" ,
144+ Map .of (ORIGIN_TRIAL_HEADER , ORIGIN_TRIAL_TOKEN ));
145+
146+ final ShouldInterceptRequestHelper requestInterceptHelper =
147+ mContentsClient .getShouldInterceptRequestHelper ();
148+ requestInterceptHelper .setReturnValueForUrl (ORIGIN_TRIAL_URL , responseInfo );
149+
150+ final TestIntegrityServiceBridgeDelegateImpl delegateForTesting =
151+ new TestIntegrityServiceBridgeDelegateImpl ();
152+ mActivityTestRule .runOnUiThread (
153+ () -> IntegrityServiceBridge .setDelegateForTesting (delegateForTesting ));
154+
155+ final ExecutionCallbackListener listener = new ExecutionCallbackListener ();
156+ AwActivityTestRule .addJavascriptInterfaceOnUiThread (mAwContents , listener , "testListener" );
157+
158+ mActivityTestRule .loadUrlSync (
159+ mAwContents , mContentsClient .getOnPageFinishedHelper (), ORIGIN_TRIAL_URL );
160+
161+ final String script = "(() => {"
162+ + "if ('getEnvironmentIntegrity' in navigator) {"
163+ + " navigator.getEnvironmentIntegrity('contentBinding')"
164+ + " .then(s => testListener.result(s.encode()))"
165+ + " .catch(e => testListener.result('error: ' + e));"
166+ + " return 'available';"
167+ + "} else {return 'unavailable';}"
168+ + "})();" ;
169+ String scriptResult = mActivityTestRule .executeJavaScriptAndWaitForResult (
170+ mAwContents , mContentsClient , script );
171+ // The result is expected to have extra quotes as a JSON-encoded string.
172+ Assert .assertEquals ("\" available\" " , scriptResult );
173+
174+ // Wait until the result callback has been triggered, to inspect the state of the delegate
175+ // The actual result should just be an error we don't care about.
176+ String result = listener .waitForResult ();
177+ Assert .assertEquals (TOKEN_BASE64 , result );
178+ }
179+
180+ static class ExecutionCallbackListener {
181+ private final CallbackHelper mCallbackHelper = new CallbackHelper ();
182+ private String mResult ;
183+
184+ @ JavascriptInterface
185+ public void result (String s ) {
186+ mResult = s ;
187+ mCallbackHelper .notifyCalled ();
188+ }
189+
190+ String waitForResult () throws TimeoutException {
191+ mCallbackHelper .waitForNext (5 , TimeUnit .SECONDS );
192+ return mResult ;
193+ }
194+ }
195+
196+ private static class TestIntegrityServiceBridgeDelegateImpl
197+ implements IntegrityServiceBridgeDelegate {
198+ @ Override
199+ public ListenableFuture <Long > createEnvironmentIntegrityHandle (
200+ boolean bindAppIdentity , int timeoutMilliseconds ) {
201+ return Futures .immediateFuture (HANDLE );
202+ }
203+
204+ @ Override
205+ public ListenableFuture <byte []> getEnvironmentIntegrityToken (
206+ long handle , byte [] requestHash , int timeoutMilliseconds ) {
207+ return Futures .immediateFuture (TOKEN );
208+ }
209+
210+ @ Override
211+ public boolean canUseGms () {
212+ return true ;
213+ }
214+ }
73215}
0 commit comments