CVE-2026-79238
Overview
Background
- ServiceWorker synthetic response
- a browser-side optimization that returns a synthetic Service Worker registration for the default search engine (DSE) so a navigation can be served ahead of the normal storage lookup for the origin’s own registration.
- `IsServiceWorkerSyntheticResponseAllowed`
- the
ChromeContentBrowserClientgate inchrome_content_browser_client.ccthat decides whether the synthetic registration is eligible for a given navigation URL. - `alternate_urls`
- a
TemplateURLDatafield listing additional URL patterns (possibly on other origins) that a search provider also treats as its own search endpoints. - `GetDefaultSearchProviderOrigin` / `IsSameOriginWith`
- the DSE’s primary search-URL origin and the
url::Originsame-origin comparison used to constrain the gate.
Root Cause Analysis
The eligibility gate ultimately depended on default-search-provider matching, which in the TemplateURLService matches not only the provider’s primary search URL but also every entry in its alternate_urls. Because alternate_urls can name a completely different origin, a navigation to that foreign origin was treated as a DSE navigation and made eligible for the synthetic Service Worker registration. That synthetic registration is created for url’s own origin and is returned ahead of the storage lookup for that origin’s real registration, so navigations to an unrelated origin could be served the search provider’s synthetic worker context — an authorization boundary violation across origins.
The fix computes dse_origin from GetDefaultSearchProviderOrigin() and adds an explicit if (!dse_origin.IsSameOriginWith(url)) return false; guard, placed before the existing IsPrewarmUrl check, so the synthetic registration is only used on the search provider’s own origin. This tightens the check by requiring exact same-origin equality with the primary search URL rather than any alternate-URL match.
alternate_urls) with “is the default search engine’s own origin”; the fix restores the intended invariant by requiring the navigated url to be same-origin with the DSE’s primary search URL before allowing a synthetic registration.Attack Path
- Configure an alternate-origin search entry
A default search provider (or a mechanism that sets one) lists an
alternate_urlon a different origin, e.g.https://other.test/{searchTerms}, while its primary search URL is onexample.com. - Navigate to the foreign origin
The victim navigates to a URL on
other.test, which the pre-patch gate accepts because default-search matching honored the alternate URL. - Synthetic registration is returned
IsServiceWorkerSyntheticResponseAllowedreturnstrue, so a synthetic Service Worker registration is created forother.testand returned ahead of the storage lookup forother.test’s own registration. - Cross-origin authorization violation The foreign origin is served the search provider’s synthetic worker context instead of (or before) its legitimate registration, causing incorrect authorization in ServiceWorker (inferred as to concrete downstream leakage, which the diff does not demonstrate).
Impact Assessment
alternate_urls can cause navigations to an unrelated origin to be served a synthetic Service Worker registration intended only for the search provider, i.e. incorrect authorization in ServiceWorker within the browser process’s navigation path. The precondition is a DSE configuration whose alternate URLs span a different origin than the primary search URL. The vendor advisory rates this medium severity and does not report in-the-wild exploitation; NVD scores it CVSS 4.3. Any further escalation is (inferred).Files Changed
chrome/browser/chrome_content_browser_client.ccchrome/browser/chrome_content_browser_client_unittest.cc
Audit Directions
- Alternate-URL / provider matchingAudit every use of default-search-provider matching (
IsDefaultSearchEngineand related helpers) that gates a security decision, since these matchalternate_urlson arbitrary origins rather than the primary search origin. - Origin-vs-match confusionLook for gates that treat “matches a configured pattern set” as equivalent to “is the trusted origin,” and verify they perform an explicit
IsSameOriginWithagainst a single canonical origin. - Registration returned before storage lookupReview synthetic/precomputed registration shortcuts that are returned ahead of the real storage lookup, ensuring their eligibility is bounded to the exact origin they claim to represent.
Patch
From e30c3969db337dad9ee43af0b85265e3afdb7b66 Mon Sep 17 00:00:00 2001
From: Shunya Shishido <sisidovski@chromium.org>
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 <yyanagisawa@chromium.org>
Commit-Queue: Shunya Shishido <sisidovski@chromium.org>
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.