High patch-now ~ plausible CVSS 8.8 chrome OOB 🔧 Commit mapped

Overview

High
Severity
8.8
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactOut of bounds write in ServiceWorker
DescriptionOut of bounds write in ServiceWorker
ComponentServiceWorker
Sub-componentServiceWorker
Bug ClassOOB
Action tierpatch-now — corruption primitive, web-reachable in default config (plausible) — upheld by independent review by AI
Computed by PatchGap's deterministic exploitability ruler from the fixing commit. patch-now = RCE-class & attacker-reachable in a default config, or exploited in the wild; watch = real security signal below that bar; noise = no security relevance.
Independent review by AIPlausible
Reviewer notes: Fix is a default-off feature flag that stops SW-served responses entering the code cache-a kill switch, not a direct OOB fix. The code-cache-mismatch to OOB-write mechanism is consistent but inferred, not shown in the diff.
A second, independent AI reviewer (not a human) re-derived this analysis from the fixing commit and the vendor advisory, looking for errors.
Tracker558342353
Fix commitc0b5e8174936 (chromium/src) +103/-1
CWECWE-787 (Out-of-bounds write)
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
CISA KEVNot listed
CreditedCristian Di Nicola (@crih.exe)
Reported2026-09-07 (per vendor advisory)
Fix landed2026-09-10 (public git)
Disclosed2026-09-15
Patch gap5 days — fix public in source before the advisory

Background

Code cache (cached metadata)
V8’s serialized bytecode/compilation artifact attached to a script resource, keyed by URL, so a subsequent load can skip recompilation and consume the stored bytes directly.
Service worker subresource interception
a service worker can intercept a page’s subresource fetches and answer them via respondWith(), returning content it fabricates, reads from Cache Storage, or fetches from a different URL, so the bytes delivered for a URL need not match what the network would serve.
Isolated (site-keyed) code cache
the per-site code cache used for scripts; ShouldUseIsolatedCodeCache and the CachedMetadataSender factory decide whether a response participates in it.
`RuntimeEnabledFeatures::ServiceWorkerCodeCacheEnabled()`
the new runtime flag added by this patch, disabled by default, that gates whether service-worker-served subresource responses may use code caching at all.

Root Cause Analysis

Verify before building a PoC

This analysis is consistent with the fixing commit but not fully provable from it: part of the vulnerable path lives in code the fix commit does not touch. Confirm the trigger and the sink against the source before relying on it for exploit development.
What the commit does not establish: Fix is a default-off feature flag that stops SW-served responses entering the code cache-a kill switch, not a direct OOB fix. The code-cache-mismatch to OOB-write mechanism is consistent but inferred, not shown in the diff.

Before the patch, a subresource script whose ResourceResponse::WasFetchedViaServiceWorker() was true could still participate in the site-isolated V8 code cache: ShouldUseIsolatedCodeCache and the CachedMetadataSender factory keyed caching decisions on the resource URL, and the streaming/ScriptResource paths attached the cached metadata handler for such responses. Because a service worker can substitute arbitrary content for a given URL (a passthrough or Cache Storage response can carry bytes unrelated to the code cache previously stored for that URL), the code cache and the actual source text could diverge while sharing the same key. Consuming a serialized code cache whose offsets no longer correspond to the delivered script leaves V8 operating on mismatched bytecode, the invariant behind the out-of-bounds write.

The fix introduces the ServiceWorkerCodeCache runtime flag (default off) and adds guards in script_streamer.cc, script_resource.cc, and cached_metadata_handler.cc that, when the flag is disabled and the response was fetched via a service worker, clear the cached metadata, skip creating the cache handler, return a NullCachedMetadataSender, and make ShouldUseIsolatedCodeCache return false. This removes service-worker-served subresources from the code-cache path entirely, so no stale or mismatched cache can be applied to substituted content.

Key insight
The core mistake was keying the subresource code cache purely on URL while a service worker can serve arbitrary content for that URL, allowing a code cache to be applied to source it was never generated from; the fix stops feeding service-worker-served responses into code caching at all (behind a default-off flag) rather than trying to re-validate the match.

Attack Path

  1. Install a controlling service worker An attacker page registers a same-origin service worker that intercepts subresource script fetches via fetch events and respondWith().
  2. Seed a code cache for a URL Cause a script URL to be compiled and its V8 code cache stored in the isolated, URL-keyed code cache.
  3. Serve mismatched content under the same key Have the service worker answer the same URL with different bytes (a passthrough/Cache Storage response the attacker controls) so WasFetchedViaServiceWorker() is true and the stored code cache no longer matches the delivered source.
  4. Trigger consumption of the mismatched cache The pre-patch code-cache path attaches the cached metadata and hands V8 a serialized code cache whose offsets do not correspond to the actual script, producing an out-of-bounds write during code-cache consumption (inferred; the patch removes the caching path rather than showing the V8 sink).
  5. Corrupt renderer memory The out-of-bounds write yields memory corruption in the renderer process, potentially leveraged toward further control (inferred).

Impact Assessment

An attacker who can register a service worker for an origin (e.g., via a page they control) gains an out-of-bounds write in the renderer process by forcing a V8 code cache to be applied to script content the cache was not generated from. The precondition is service-worker control over subresource responses for a URL that also has a stored code cache; no elevated privileges are needed beyond running script in a controlled origin. The advisory characterizes this only as an out-of-bounds write in ServiceWorker and does not state exploited-in-the-wild status; any escalation to code execution or sandbox escape is (inferred).

Changed Functions

FunctionChangeNotes
if
third_party/blink/renderer/bindings/core/v8/script_streamer.cc
modified
ServiceWorkerV8CodeCacheForCacheStorageTest
content/browser/service_worker/service_worker_browsertest.cc
modified
ServiceWorkerV8CodeCacheForCacheStorageTest
content/browser/service_worker/service_worker_browsertest.cc
modified
TEST
third_party/blink/renderer/core/loader/resource/script_resource_test.cc
modified

Files Changed

  • third_party/blink/renderer/platform/runtime_enabled_features.json5
  • third_party/blink/renderer/platform/loader/fetch/url_loader/cached_metadata_handler_test.cc
  • third_party/blink/renderer/bindings/core/v8/script_streamer.cc
  • third_party/blink/renderer/core/loader/resource/script_resource.cc
  • content/browser/service_worker/service_worker_browsertest.cc
  • third_party/blink/renderer/platform/loader/fetch/url_loader/cached_metadata_handler.cc
  • third_party/blink/renderer/core/loader/resource/script_resource_test.cc

Audit Directions

  • URL-keyed caches under content substitution
    Audit any cache keyed by URL (code cache, cached metadata, other persisted compilation artifacts) that can be populated or served by a service worker, since the URL key does not bind to the actual bytes delivered.
  • `WasFetchedViaServiceWorker()` gaps
    Look for code-cache and resource paths that branch on request context or URL but never consult WasFetchedViaServiceWorker(), where a service worker could inject content mismatched to cached state.
  • Cross-layer flag coverage
    When a mitigation gates behavior behind a runtime flag, verify every consumer of the affected data (streamer, resource loader, sender factory, and the isolated-cache decision) checks the same flag, so no single unguarded path reintroduces the mismatch.
From c0b5e81749361a9c8a79990f2c721a16d141882c Mon Sep 17 00:00:00 2001
From: Takashi Nakayama <tnak@chromium.org>
Date: Thu, 10 Sep 2026 00:32:02 -0700
Subject: [PATCH] ServiceWorker: Add feature flag to disable Code Cache

This CL adds a new runtime enabled feature, `ServiceWorkerCodeCache` to
disable Code Caching for subresource requests through Service Workers.
The new feature is disabled by default.

Bug: 558367547, 546413288, 547815507, 558342353
Change-Id: I2a3b7fbf8c489eec00bce668abb3a7cf438d9f44
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8377021
Reviewed-by: Yoshisato Yanagisawa <yyanagisawa@chromium.org>
Reviewed-by: Shunya Shishido <sisidovski@chromium.org>
Commit-Queue: Takashi Nakayama <tnak@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1695233}
---
diff --git a/third_party/blink/renderer/bindings/core/v8/script_streamer.cc b/third_party/blink/renderer/bindings/core/v8/script_streamer.cc
index aabdaa9e..615c445 100644
--- a/third_party/blink/renderer/bindings/core/v8/script_streamer.cc
+++ b/third_party/blink/renderer/bindings/core/v8/script_streamer.cc
@@ -58,6 +58,7 @@
 #include "third_party/blink/renderer/platform/loader/fetch/response_body_loader.h"
 #include "third_party/blink/renderer/platform/loader/fetch/script_cached_metadata_handler.h"
 #include "third_party/blink/renderer/platform/network/mime/mime_type_registry.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
 #include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
 #include "third_party/blink/renderer/platform/scheduler/public/worker_pool.h"
@@ -1874,6 +1875,11 @@
     return false;
   }
 
+  if (!RuntimeEnabledFeatures::ServiceWorkerCodeCacheEnabled() &&
+      head->was_fetched_via_service_worker) {
+    cached_metadata.reset();
+  }
+
   if (is_valid_wasm_mime_type) {
     // TODO(https://crbug.com/42204365): Add support for code caching.
     // For now, we clear cached_metadata to avoid unintended side-effects.
diff --git a/third_party/blink/renderer/core/loader/resource/script_resource.cc b/third_party/blink/renderer/core/loader/resource/script_resource.cc
index 39a58fe..8b9f1616 100644
--- a/third_party/blink/renderer/core/loader/resource/script_resource.cc
+++ b/third_party/blink/renderer/core/loader/resource/script_resource.cc
@@ -59,6 +59,7 @@
 #include "third_party/blink/renderer/platform/loader/fetch/webui_bundled_cached_metadata_handler.h"
 #include "third_party/blink/renderer/platform/loader/subresource_integrity.h"
 #include "third_party/blink/renderer/platform/network/mime/mime_type_registry.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/blink/renderer/platform/weborigin/scheme_registry.h"
 #include "third_party/blink/renderer/platform/wtf/functional.h"
 #include "third_party/blink/renderer/platform/wtf/shared_buffer.h"
@@ -411,6 +412,10 @@
   }
 
   cached_metadata_handler_ = nullptr;
+  if (response.WasFetchedViaServiceWorker() &&
+      !RuntimeEnabledFeatures::ServiceWorkerCodeCacheEnabled()) {
+    return;
+  }
   // Currently we support the metadata caching only for HTTP family and any
   // schemes defined by SchemeRegistry as requiring a hash check.
   bool http_family = GetResourceRequest().Url().ProtocolIsInHttpFamily() &&
diff --git a/third_party/blink/renderer/platform/loader/fetch/url_loader/cached_metadata_handler.cc b/third_party/blink/renderer/platform/loader/fetch/url_loader/cached_metadata_handler.cc
index 3707f3fc..5ef2f37d 100644
--- a/third_party/blink/renderer/platform/loader/fetch/url_loader/cached_metadata_handler.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/url_loader/cached_metadata_handler.cc
@@ -12,6 +12,7 @@
 #include "third_party/blink/public/platform/web_url.h"
 #include "third_party/blink/renderer/platform/loader/fetch/code_cache_host.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 
 namespace blink {
 
@@ -131,6 +132,11 @@
     const ResourceResponse& response,
     mojom::blink::CodeCacheType code_cache_type,
     scoped_refptr<const SecurityOrigin> requestor_origin) {
+  if (!RuntimeEnabledFeatures::ServiceWorkerCodeCacheEnabled() &&
+      response.WasFetchedViaServiceWorker()) {
+    return std::make_unique<NullCachedMetadataSender>();
+  }
+
   // Non-ServiceWorker scripts and passthrough SW responses use the site
   // isolated code cache.
   if (!response.WasFetchedViaServiceWorker() ||
@@ -171,6 +177,11 @@
 bool ShouldUseIsolatedCodeCache(
     mojom::blink::RequestContextType request_context,
     const ResourceResponse& response) {
+  if (!RuntimeEnabledFeatures::ServiceWorkerCodeCacheEnabled() &&
+      response.WasFetchedViaServiceWorker()) {
+    return false;
+  }
+
   // Service worker script has its own code cache.
   if (request_context == mojom::blink::RequestContextType::SERVICE_WORKER)
     return false;
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 626e074..85ea1fb 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -5711,6 +5711,10 @@
       status: "experimental",
     },
     {
+      // V8 Code Cache for resource requests passing through Service Worker.
+      name: "ServiceWorkerCodeCache",
+    },
+    {
       // Support for Service Worker APIs in Dedicated Workers.
       // See https://chromestatus.com/feature/6497653025931264
       // See https://issues.chromium.org/issues/40364838
diff --git a/content/browser/service_worker/service_worker_browsertest.cc b/content/browser/service_worker/service_worker_browsertest.cc
index 42aaa111..01c4800 100644
--- a/content/browser/service_worker/service_worker_browsertest.cc
+++ b/content/browser/service_worker/service_worker_browsertest.cc
@@ -3399,7 +3399,10 @@
 class ServiceWorkerV8CodeCacheForCacheStorageTest
     : public ServiceWorkerBrowserTest {
  public:
-  ServiceWorkerV8CodeCacheForCacheStorageTest() = default;
+  ServiceWorkerV8CodeCacheForCacheStorageTest() {
+    feature_list_.InitAndEnableFeature(
+        blink::features::kServiceWorkerCodeCache);
+  }
 
   ServiceWorkerV8CodeCacheForCacheStorageTest(
       const ServiceWorkerV8CodeCacheForCacheStorageTest&) = delete;
@@ -3474,6 +3477,8 @@
         partition->GetCacheStorageControl(), embedded_test_server()->base_url(),
         std::string("cache_name"), embedded_test_server()->GetURL(kScriptUrl));
   }
+
+  base::test::ScopedFeatureList feature_list_;
 };
 
 const char ServiceWorkerV8CodeCacheForCacheStorageTest::kPageUrl[] =
diff --git a/third_party/blink/renderer/core/loader/resource/script_resource_test.cc b/third_party/blink/renderer/core/loader/resource/script_resource_test.cc
index df582f1..4a1d54b 100644
--- a/third_party/blink/renderer/core/loader/resource/script_resource_test.cc
+++ b/third_party/blink/renderer/core/loader/resource/script_resource_test.cc
@@ -9,6 +9,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
 #include "third_party/blink/renderer/platform/loader/fetch/url_loader/cached_metadata_handler.h"
+#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
 #include "third_party/blink/renderer/platform/testing/task_environment.h"
 #include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
@@ -323,5 +324,35 @@
   enable_webui_bundled_code_caching(false);
 }
 
+TEST(ScriptResourceTest, ServiceWorkerCodeCacheFlag) {
+  test::TaskEnvironment task_environment;
+  V8TestingScope scope;
+  const KURL url("https://www.example.com/sw-script.js");
+
+  {
+    ScopedServiceWorkerCodeCacheForTest scoped_feature(false);
+    ScriptResource* resource =
+        ScriptResource::CreateForTest(scope.GetIsolate(), url, Utf8Encoding());
+    ResourceResponse response(url);
+    response.SetHttpStatusCode(200);
+    response.SetWasFetchedViaServiceWorker(true);
+
+    resource->ResponseReceived(response);
+    EXPECT_FALSE(resource->CacheHandler());
+  }
+
+  {
+    ScopedServiceWorkerCodeCacheForTest scoped_feature(true);
+    ScriptResource* resource =
+        ScriptResource::CreateForTest(scope.GetIsolate(), url, Utf8Encoding());
+    ResourceResponse response(url);
+    response.SetHttpStatusCode(200);
+    response.SetWasFetchedViaServiceWorker(true);
+
+    resource->ResponseReceived(response);
+    EXPECT_TRUE(resource->CacheHandler());
+  }
+}
+
 }  // namespace
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/loader/fetch/url_loader/cached_metadata_handler_test.cc b/third_party/blink/renderer/platform/loader/fetch/url_loader/cached_metadata_handler_test.cc
index ba13a0e..a4320bb 100644
--- a/third_party/blink/renderer/platform/loader/fetch/url_loader/cached_metadata_handler_test.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/url_loader/cached_metadata_handler_test.cc
@@ -9,12 +9,14 @@
 #include "components/persistent_cache/pending_backend.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
 #include "third_party/blink/public/mojom/loader/code_cache.mojom-blink.h"
 #include "third_party/blink/public/platform/url_conversion.h"
 #include "third_party/blink/public/platform/web_url.h"
 #include "third_party/blink/renderer/platform/loader/fetch/code_cache_host.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
+#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
 #include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
@@ -136,6 +138,7 @@
 TEST(
     CachedMetadataHandlerTest,
     SendsMetadataToPlatformWhenFetchedViaServiceWorkerWithPassThroughResponse) {
+  ScopedServiceWorkerCodeCacheForTest scoped_feature(true);
   MockGeneratedCodeCache mock_disk_cache;
 
   // Equivalent to service worker calling respondWith(fetch(evt.request.url));
@@ -151,6 +154,7 @@
 TEST(
     CachedMetadataHandlerTest,
     DoesNotSendMetadataToPlatformWhenFetchedViaServiceWorkerWithDifferentURLResponse) {
+  ScopedServiceWorkerCodeCacheForTest scoped_feature(true);
   MockGeneratedCodeCache mock_disk_cache;
 
   // Equivalent to service worker calling respondWith(fetch(some_different_url))
@@ -166,6 +170,7 @@
 
 TEST(CachedMetadataHandlerTest,
      SendsMetadataToPlatformWhenFetchedViaServiceWorkerWithCacheResponse) {
+  ScopedServiceWorkerCodeCacheForTest scoped_feature(true);
   MockGeneratedCodeCache mock_disk_cache;
 
   // Equivalent to service worker calling respondWith(cache.match(some_url));
@@ -179,9 +184,25 @@
   EXPECT_EQ(1u, mock_disk_cache.CacheStorageCachedURLs().size());
 }
 
+TEST(CachedMetadataHandlerTest,
+     DoesNotSendMetadataToPlatformWhenServiceWorkerCodeCacheDisabled) {
+  ScopedServiceWorkerCodeCacheForTest scoped_feature(false);
+  MockGeneratedCodeCache mock_disk_cache;
+
+  ResourceResponse response(CreateTestResourceResponse());
+  response.SetWasFetchedViaServiceWorker(true);
+  response.SetUrlListViaServiceWorker({response.CurrentRequestUrl()});
+  response.SetCacheStorageCacheName("dummy");
+
+  SendDataFor(response, &mock_disk_cache);
+  EXPECT_EQ(0u, mock_disk_cache.CachedURLs().size());
+  EXPECT_EQ(0u, mock_disk_cache.CacheStorageCachedURLs().size());
+}
+
 TEST(
     CachedMetadataHandlerTest,
     DoesNotSendMetadataToPlatformWhenFetchedViaServiceWorkerWithSyntheticCacheResponse) {
+  ScopedServiceWorkerCodeCacheForTest scoped_feature(true);
   MockGeneratedCodeCache mock_disk_cache;
 
   // Equivalent to service worker calling
@@ -198,6 +219,7 @@
 TEST(
     CachedMetadataHandlerTest,
     DoesNotSendMetadataToPlatformWhenFetchedViaServiceWorkerWithDifferentURLCacheResponse) {
+  ScopedServiceWorkerCodeCacheForTest scoped_feature(true);
   MockGeneratedCodeCache mock_disk_cache;
 
   // Equivalent to service worker calling
@@ -213,5 +235,23 @@
   EXPECT_EQ(0u, mock_disk_cache.CacheStorageCachedURLs().size());
 }
 
+TEST(CachedMetadataHandlerTest,
+     ShouldUseIsolatedCodeCacheRespectsServiceWorkerCodeCacheFlag) {
+  ResourceResponse response(CreateTestResourceResponse());
+  response.SetWasFetchedViaServiceWorker(true);
+  response.SetUrlListViaServiceWorker({response.CurrentRequestUrl()});
+
+  {
+    ScopedServiceWorkerCodeCacheForTest scoped_feature(false);
+    EXPECT_FALSE(ShouldUseIsolatedCodeCache(
+        mojom::blink::RequestContextType::SCRIPT, response));
+  }
+  {
+    ScopedServiceWorkerCodeCacheForTest scoped_feature(true);
+    EXPECT_TRUE(ShouldUseIsolatedCodeCache(
+        mojom::blink::RequestContextType::SCRIPT, response));
+  }
+}
+
 }  // namespace
 }  // namespace blink
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/content/browser/service_worker/service_worker_browsertest.cc b/content/browser/service_worker/service_worker_browsertest.cc
index 42aaa111..01c4800 100644
--- a/content/browser/service_worker/service_worker_browsertest.cc
+++ b/content/browser/service_worker/service_worker_browsertest.cc
@@ -3399,7 +3399,10 @@
 class ServiceWorkerV8CodeCacheForCacheStorageTest
     : public ServiceWorkerBrowserTest {
  public:
-  ServiceWorkerV8CodeCacheForCacheStorageTest() = default;
+  ServiceWorkerV8CodeCacheForCacheStorageTest() {
+    feature_list_.InitAndEnableFeature(
+        blink::features::kServiceWorkerCodeCache);
+  }
 
   ServiceWorkerV8CodeCacheForCacheStorageTest(
       const ServiceWorkerV8CodeCacheForCacheStorageTest&) = delete;
@@ -3474,6 +3477,8 @@
         partition->GetCacheStorageControl(), embedded_test_server()->base_url(),
         std::string("cache_name"), embedded_test_server()->GetURL(kScriptUrl));
   }
+
+  base::test::ScopedFeatureList feature_list_;
 };
 
 const char ServiceWorkerV8CodeCacheForCacheStorageTest::kPageUrl[] =
diff --git a/third_party/blink/renderer/core/loader/resource/script_resource_test.cc b/third_party/blink/renderer/core/loader/resource/script_resource_test.cc
index df582f1..4a1d54b 100644
--- a/third_party/blink/renderer/core/loader/resource/script_resource_test.cc
+++ b/third_party/blink/renderer/core/loader/resource/script_resource_test.cc
@@ -9,6 +9,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
 #include "third_party/blink/renderer/platform/loader/fetch/url_loader/cached_metadata_handler.h"
+#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
 #include "third_party/blink/renderer/platform/testing/task_environment.h"
 #include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
@@ -323,5 +324,35 @@
   enable_webui_bundled_code_caching(false);
 }
 
+TEST(ScriptResourceTest, ServiceWorkerCodeCacheFlag) {
+  test::TaskEnvironment task_environment;
+  V8TestingScope scope;
+  const KURL url("https://www.example.com/sw-script.js");
+
+  {
+    ScopedServiceWorkerCodeCacheForTest scoped_feature(false);
+    ScriptResource* resource =
+        ScriptResource::CreateForTest(scope.GetIsolate(), url, Utf8Encoding());
+    ResourceResponse response(url);
+    response.SetHttpStatusCode(200);
+    response.SetWasFetchedViaServiceWorker(true);
+
+    resource->ResponseReceived(response);
+    EXPECT_FALSE(resource->CacheHandler());
+  }
+
+  {
+    ScopedServiceWorkerCodeCacheForTest scoped_feature(true);
+    ScriptResource* resource =
+        ScriptResource::CreateForTest(scope.GetIsolate(), url, Utf8Encoding());
+    ResourceResponse response(url);
+    response.SetHttpStatusCode(200);
+    response.SetWasFetchedViaServiceWorker(true);
+
+    resource->ResponseReceived(response);
+    EXPECT_TRUE(resource->CacheHandler());
+  }
+}
+
 }  // namespace
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/loader/fetch/url_loader/cached_metadata_handler_test.cc b/third_party/blink/renderer/platform/loader/fetch/url_loader/cached_metadata_handler_test.cc
index ba13a0e..a4320bb 100644
--- a/third_party/blink/renderer/platform/loader/fetch/url_loader/cached_metadata_handler_test.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/url_loader/cached_metadata_handler_test.cc
@@ -9,12 +9,14 @@
 #include "components/persistent_cache/pending_backend.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
 #include "third_party/blink/public/mojom/loader/code_cache.mojom-blink.h"
 #include "third_party/blink/public/platform/url_conversion.h"
 #include "third_party/blink/public/platform/web_url.h"
 #include "third_party/blink/renderer/platform/loader/fetch/code_cache_host.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
+#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
 #include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
@@ -136,6 +138,7 @@
 TEST(
     CachedMetadataHandlerTest,
     SendsMetadataToPlatformWhenFetchedViaServiceWorkerWithPassThroughResponse) {
+  ScopedServiceWorkerCodeCacheForTest scoped_feature(true);
   MockGeneratedCodeCache mock_disk_cache;
 
   // Equivalent to service worker calling respondWith(fetch(evt.request.url));
@@ -151,6 +154,7 @@
 TEST(
     CachedMetadataHandlerTest,
     DoesNotSendMetadataToPlatformWhenFetchedViaServiceWorkerWithDifferentURLResponse) {
+  ScopedServiceWorkerCodeCacheForTest scoped_feature(true);
   MockGeneratedCodeCache mock_disk_cache;
 
   // Equivalent to service worker calling respondWith(fetch(some_different_url))
@@ -166,6 +170,7 @@
 
 TEST(CachedMetadataHandlerTest,
      SendsMetadataToPlatformWhenFetchedViaServiceWorkerWithCacheResponse) {
+  ScopedServiceWorkerCodeCacheForTest scoped_feature(true);
   MockGeneratedCodeCache mock_disk_cache;
 
   // Equivalent to service worker calling respondWith(cache.match(some_url));
@@ -179,9 +184,25 @@
   EXPECT_EQ(1u, mock_disk_cache.CacheStorageCachedURLs().size());
 }
 
+TEST(CachedMetadataHandlerTest,
+     DoesNotSendMetadataToPlatformWhenServiceWorkerCodeCacheDisabled) {
+  ScopedServiceWorkerCodeCacheForTest scoped_feature(false);
+  MockGeneratedCodeCache mock_disk_cache;
+
+  ResourceResponse response(CreateTestResourceResponse());
+  response.SetWasFetchedViaServiceWorker(true);
+  response.SetUrlListViaServiceWorker({response.CurrentRequestUrl()});
+  response.SetCacheStorageCacheName("dummy");
+
+  SendDataFor(response, &mock_disk_cache);
+  EXPECT_EQ(0u, mock_disk_cache.CachedURLs().size());
+  EXPECT_EQ(0u, mock_disk_cache.CacheStorageCachedURLs().size());
+}
+
 TEST(
     CachedMetadataHandlerTest,
     DoesNotSendMetadataToPlatformWhenFetchedViaServiceWorkerWithSyntheticCacheResponse) {
+  ScopedServiceWorkerCodeCacheForTest scoped_feature(true);
   MockGeneratedCodeCache mock_disk_cache;
 
   // Equivalent to service worker calling
@@ -198,6 +219,7 @@
 TEST(
     CachedMetadataHandlerTest,
     DoesNotSendMetadataToPlatformWhenFetchedViaServiceWorkerWithDifferentURLCacheResponse) {
+  ScopedServiceWorkerCodeCacheForTest scoped_feature(true);
   MockGeneratedCodeCache mock_disk_cache;
 
   // Equivalent to service worker calling
@@ -213,5 +235,23 @@
   EXPECT_EQ(0u, mock_disk_cache.CacheStorageCachedURLs().size());
 }
 
+TEST(CachedMetadataHandlerTest,
+     ShouldUseIsolatedCodeCacheRespectsServiceWorkerCodeCacheFlag) {
+  ResourceResponse response(CreateTestResourceResponse());
+  response.SetWasFetchedViaServiceWorker(true);
+  response.SetUrlListViaServiceWorker({response.CurrentRequestUrl()});
+
+  {
+    ScopedServiceWorkerCodeCacheForTest scoped_feature(false);
+    EXPECT_FALSE(ShouldUseIsolatedCodeCache(
+        mojom::blink::RequestContextType::SCRIPT, response));
+  }
+  {
+    ScopedServiceWorkerCodeCacheForTest scoped_feature(true);
+    EXPECT_TRUE(ShouldUseIsolatedCodeCache(
+        mojom::blink::RequestContextType::SCRIPT, response));
+  }
+}
+
 }  // namespace
 }  // namespace blink
Loading diff…

Original Bug Report

The reporter's bug is still restricted on the tracker. Chrome de-restricts security bugs ~30–90 days after the fix ships; a later run will backfill it here.