CVE-2026-79238
Overview
Files Changed
chrome/browser/chrome_content_browser_client.ccchrome/browser/chrome_content_browser_client_unittest.cc
Patch
From e30c3969db337dad9ee43af0b85265e3afdb7b66 Mon Sep 17 00:00:00 2001 From: Shunya Shishido <[email protected]> Date: Fri, 03 Jul 2026 02:12:27 -0700 Subject: [PATCH] Restrict ServiceWorker synthetic response to the DSE origin IsServiceWorkerSyntheticResponseAllowed() gated on IsDefaultSearchEngine(), which matches the default search provider's alternate_urls in addition to its primary search URL. A search provider that lists an alternate_url on a different origin therefore made navigations to that origin eligible for the synthetic ServiceWorker registration, which is returned ahead of the storage lookup for the origin's own registration. Require the navigated URL to be same-origin with the default search provider's primary search URL so the synthetic registration is only used on the search provider's own origin. Update the existing unit test to use an https search URL so its positive expectation holds under the new origin check, and add coverage for alternate_urls on a different origin. Bug: 517774971 TAG=agy CONV=e3a74e33-5c1c-48e7-b139-14c8e9116f8f Change-Id: I5fb614e0f8a60c734c4ff4ed314f330457696e7f Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8028583 Reviewed-by: Yoshisato Yanagisawa <[email protected]> Commit-Queue: Shunya Shishido <[email protected]> Cr-Commit-Position: refs/heads/main@{#1656443} --- diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc index ee1657fe1..aeea3c7 100644 --- a/chrome/browser/chrome_content_browser_client.cc +++ b/chrome/browser/chrome_content_browser_client.cc @@ -3844,13 +3844,22 @@ return false; } - // Prewarm page can be treated as a DSE. As we don't want to enable synthetic - // response on the prewarm page, manually exclude it. auto* template_url_service = TemplateURLServiceFactory::GetForProfile(profile); CHECK(template_url_service); - if (IsPrewarmUrl(url, - template_url_service->GetDefaultSearchProviderOrigin())) { + const url::Origin dse_origin = + template_url_service->GetDefaultSearchProviderOrigin(); + + // The synthetic registration is created for `url`'s origin. Restrict it to + // the default search provider's own origin so that alternate URLs on other + // origins don't get a synthetic registration. + if (!dse_origin.IsSameOriginWith(url)) { + return false; + } + + // Prewarm page can be treated as a DSE. As we don't want to enable synthetic + // response on the prewarm page, manually exclude it. + if (IsPrewarmUrl(url, dse_origin)) { return false; } diff --git a/chrome/browser/chrome_content_browser_client_unittest.cc b/chrome/browser/chrome_content_browser_client_unittest.cc index e0d29c2..99ea1d9 100644 --- a/chrome/browser/chrome_content_browser_client_unittest.cc +++ b/chrome/browser/chrome_content_browser_client_unittest.cc @@ -651,7 +651,7 @@ search_test_utils::WaitForTemplateURLServiceToLoad(template_url_service); TemplateURLData data; data.SetShortName(u"example.com"); - data.SetURL("http://example.com/test?q={searchTerms}"); + data.SetURL("https://example.com/test?q={searchTerms}"); data.new_tab_url = chrome::kChromeUINewTabURL; TemplateURL* template_url = template_url_service->Add(std::make_unique<TemplateURL>(data)); @@ -669,6 +669,38 @@ profile(), GURL("https://example.com/test?q=test"))); } +TEST_F(ChromeContentBrowserClientTestWithWebContents, + IsServiceWorkerSyntheticResponseAllowedForAlternateUrls) { + ChromeContentBrowserClient browser_client; + + // Update the default search engine with an alternate URL on a different + // origin. + TemplateURLServiceFactory::GetInstance()->SetTestingFactoryAndUse( + profile(), + base::BindRepeating(&TemplateURLServiceFactory::BuildInstanceFor)); + TemplateURLService* template_url_service = + TemplateURLServiceFactory::GetForProfile(profile()); + search_test_utils::WaitForTemplateURLServiceToLoad(template_url_service); + TemplateURLData data; + data.SetShortName(u"example.com"); + data.SetURL("https://example.com/test?q={searchTerms}"); + data.alternate_urls.push_back("https://other.test/{searchTerms}"); + data.new_tab_url = chrome::kChromeUINewTabURL; + TemplateURL* template_url = + template_url_service->Add(std::make_unique<TemplateURL>(data)); + template_url_service->SetUserSelectedDefaultSearchProvider(template_url); + + // The synthetic response should only be allowed for navigations to the + // default search provider's own origin, even when an alternate URL on a + // different origin matches. + EXPECT_TRUE(browser_client.IsServiceWorkerSyntheticResponseAllowed( + profile(), GURL("https://example.com/test?q=test"))); + EXPECT_FALSE(browser_client.IsServiceWorkerSyntheticResponseAllowed( + profile(), GURL("https://other.test/page"))); + EXPECT_FALSE(browser_client.IsServiceWorkerSyntheticResponseAllowed( + profile(), GURL("http://example.com/test?q=test"))); +} + #endif // !BUILDFLAG(IS_ANDROID) // NOTE: Any updates to the expectations in these tests should also be done in
Regression Test / PoC
diff --git a/chrome/browser/chrome_content_browser_client_unittest.cc b/chrome/browser/chrome_content_browser_client_unittest.cc
index e0d29c2..99ea1d9 100644
--- a/chrome/browser/chrome_content_browser_client_unittest.cc
+++ b/chrome/browser/chrome_content_browser_client_unittest.cc
@@ -651,7 +651,7 @@
search_test_utils::WaitForTemplateURLServiceToLoad(template_url_service);
TemplateURLData data;
data.SetShortName(u"example.com");
- data.SetURL("http://example.com/test?q={searchTerms}");
+ data.SetURL("https://example.com/test?q={searchTerms}");
data.new_tab_url = chrome::kChromeUINewTabURL;
TemplateURL* template_url =
template_url_service->Add(std::make_unique<TemplateURL>(data));
@@ -669,6 +669,38 @@
profile(), GURL("https://example.com/test?q=test")));
}
+TEST_F(ChromeContentBrowserClientTestWithWebContents,
+ IsServiceWorkerSyntheticResponseAllowedForAlternateUrls) {
+ ChromeContentBrowserClient browser_client;
+
+ // Update the default search engine with an alternate URL on a different
+ // origin.
+ TemplateURLServiceFactory::GetInstance()->SetTestingFactoryAndUse(
+ profile(),
+ base::BindRepeating(&TemplateURLServiceFactory::BuildInstanceFor));
+ TemplateURLService* template_url_service =
+ TemplateURLServiceFactory::GetForProfile(profile());
+ search_test_utils::WaitForTemplateURLServiceToLoad(template_url_service);
+ TemplateURLData data;
+ data.SetShortName(u"example.com");
+ data.SetURL("https://example.com/test?q={searchTerms}");
+ data.alternate_urls.push_back("https://other.test/{searchTerms}");
+ data.new_tab_url = chrome::kChromeUINewTabURL;
+ TemplateURL* template_url =
+ template_url_service->Add(std::make_unique<TemplateURL>(data));
+ template_url_service->SetUserSelectedDefaultSearchProvider(template_url);
+
+ // The synthetic response should only be allowed for navigations to the
+ // default search provider's own origin, even when an alternate URL on a
+ // different origin matches.
+ EXPECT_TRUE(browser_client.IsServiceWorkerSyntheticResponseAllowed(
+ profile(), GURL("https://example.com/test?q=test")));
+ EXPECT_FALSE(browser_client.IsServiceWorkerSyntheticResponseAllowed(
+ profile(), GURL("https://other.test/page")));
+ EXPECT_FALSE(browser_client.IsServiceWorkerSyntheticResponseAllowed(
+ profile(), GURL("http://example.com/test?q=test")));
+}
+
#endif // !BUILDFLAG(IS_ANDROID)
// NOTE: Any updates to the expectations in these tests should also be done in
Original Bug Report
Extension Permission Bypass via alternate_urls Disabling Third-Party ServiceWorkers
Project Fortify, an experimental security project, has identified the following potential security issue. If you’re a feature owner CC-ed on this bug, please do your best to review these reports. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.
Overview: A logical flaw in Chrome’s Settings Overrides API allows a malicious extension with only the chrome_settings_overrides permission to register arbitrary third-party origins under alternate_urls. When a user navigates to those origins, the browser mistakenly identifies them as eligible for a synthetic ServiceWorker response. This completely shadows the victim origin’s real ServiceWorker, silently bypassing its fetch interception and security controls.
Affected files:
content/browser/service_worker/service_worker_registry.ccchrome/browser/chrome_content_browser_client.ccchrome/browser/extensions/api/settings_overrides/settings_overrides_api.ccchrome/common/extensions/manifest_handlers/settings_overrides_handler.cccontent/browser/service_worker/service_worker_loader_helpers.cccontent/browser/service_worker/service_worker_controllee_request_handler.cc
Estimated timestamp from git blame: 2025-05-26
Description
An extension with only the chrome_settings_overrides permission (and no host permissions) can register arbitrary third-party origins under search_provider.alternate_urls. When a user navigates to a listed URL, the browser’s synthetic-response matching flags it as a default search engine URL, prompting ServiceWorkerRegistry::FindRegistrationForClientUrl to early-return an in-memory synthetic ServiceWorker registration. This completely shadows the victim site’s real ServiceWorker, silently disabling its fetch interception, offline caching, and any client-side security policies or headers normally injected via the ServiceWorker.
Root Cause Analysis
-
Unchecked
alternate_urlsIngestion: Inchrome/browser/extensions/api/settings_overrides/settings_overrides_api.cc(lines 131-138) andchrome/common/extensions/manifest_handlers/settings_overrides_handler.cc(lines 70-112), thealternate_urlsprovided in thechrome_settings_overrides.search_providermanifest key are copied verbatim intoTemplateURLDatawith no host, origin, or pattern validation. -
Attacker-Controlled Synthetic Response Eligibility:
ChromeContentBrowserClient::IsServiceWorkerSyntheticResponseAllowed(defined inchrome/browser/chrome_content_browser_client.cc:3820) delegates its validation toIsDefaultSearchEngine, which resolves to true for any URL whose host and path matches any entry inalternate_urlsviaTemplateURL::IsSearchURLandTemplateURLRef::ExtractSearchTermsFromURL(defined incomponents/search_engines/template_url.cc). -
Registry Shadowing: In
ServiceWorkerRegistry::FindRegistrationForClientUrl(content/browser/service_worker/service_worker_registry.ccline 428), synthetic response eligibility is evaluated before performing the storage lookup for the site’s real ServiceWorker registration. If eligible, it returns a synthetic ServiceWorker registration configured withfetch_handler_type = kNoHandlerand router rules that route everything directly to the network. The database lookup for the real ServiceWorker is completely bypassed. -
Self-Reinforcing Cache Invalidation: When the synthetic branch runs with
scopes == std::nullopt,DidFindRegistrationForClientUrlinvokesClearInternalCacheForStorageKey(key), wiping the in-memory scope-cache for the victim origin. This ensures subsequent navigations continue to bypass the real ServiceWorker.
Potential Attack Scenario
Note: These are potential steps. Our security review was performed statically, and we have not run a live proof of concept.
- An extension is installed whose manifest declares a
chrome_settings_overridesblock targeting an attacker’s search provider, and includes"alternate_urls": ["https://victim.com/{searchTerms}"]with no host permissions. The user accepts the “Change your search settings” prompt. - The user navigates to
https://victim.com/anything(a top-level GET request). - Instead of running the victim’s legitimate registered ServiceWorker, Chrome serves a synthetic ServiceWorker configured with
kNoHandlerand network router rules. The page loads directly from the network, disabling any fetch interception, custom headers, or security policies (e.g. CSRF or CSP) deployed via the victim’s ServiceWorker.
Feature Flag Dependencies
This potential issue requires the ServiceWorkerSyntheticResponse feature to be enabled. It is currently disabled by default but active in field trials on platforms including Android, ChromeOS, Linux, macOS, and Windows via testing/variations/fieldtrial_testing_config.json.
Suggested Fix
Restrict alternate_urls parsing or registration in chrome/common/extensions/manifest_handlers/settings_overrides_handler.cc and chrome/browser/extensions/api/settings_overrides/settings_overrides_api.cc to ensure that an extension cannot specify alternate search URLs targeting domains for which it does not hold host permissions. Alternatively, restrict ChromeContentBrowserClient::IsServiceWorkerSyntheticResponseAllowed to exclude alternate search URLs from synthetic response matching unless the origin is validated.
Evaluated with Chrome root at commit: 5133b93d189b383c37805b1cf3a9d2dbfe8d7379
Results so far have been promising, but there can be wrong deductions. Feel free to adjust as follows:
- If you are familiar with the severity guidelines, you may adjust the severity.
- If this is a false positive, and there’s no work to be done, please close as WAI.
- If there is work to do here but not a vulnerability, please change the issue type to Task/Bug/FR.
Data from false positives will be used to improve accuracy over time. And please feel free to reach out to me directly if you have concerns or feedback on the project.