Medium chrome Logic Error 🔧 Commit mapped

Overview

Medium
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInappropriate implementation in Workers
DescriptionInappropriate implementation in Workers
ComponentWorkers
Bug ClassLogic Error
Tracker40051700
Fix commit606713645ace (chromium/src) +354/-153
CISA KEVNot listed
CreditedJayateertha Guruprasad
Disclosed2026-06-02

Changed Functions

FunctionChangeNotes
if
content/browser/BUILD.gn
modified
GetStorageKey
content/browser/devtools/dedicated_worker_devtools_agent_host.cc
modified

Files Changed

  • content/browser/BUILD.gn
  • content/browser/browser_interface_binders.cc
  • content/browser/compute_pressure/pressure_service_for_worker_unittest.cc
  • content/browser/devtools/dedicated_worker_devtools_agent_host.cc
  • content/browser/devtools/shared_worker_devtools_agent_host.cc
  • content/browser/direct_sockets/direct_sockets_service_impl.cc
  • content/browser/service_worker/service_worker_client.cc
From 606713645ace47728a8e2a56dd1fc6a8b44e13a7 Mon Sep 17 00:00:00 2001
From: Yoshisto Yanagisawa <yyanagisawa@chromium.org>
Date: Mon, 06 Apr 2026 08:23:23 -0700
Subject: [PATCH] Create opaque origins for data: URL workers behind a flag

When enabled, the kDataUrlWorkerOpaqueOrigin feature flag ensures that
dedicated and shared workers created from a data: URL have an opaque
origin (and thus an opaque StorageKey) rather than inheriting their
creator's origin, aligning with the HTML spec.

This CL implements the browser-process logic for this feature by:
1. Updating DedicatedWorkerHost to receive the appropriate StorageKey.
2. Generating an opaque StorageKey in DedicatedWorkerHostFactoryImpl
   if the worker script is a data: URL and the flag is on.
3. Updating SharedWorkerInstance to use an opaque StorageKey if the
   worker script is a data: URL and the flag is on.
4. Bypassing the CanAccessSharedWorkers() check in WebSharedWorkerImpl
   for data: URL scripts when the feature is enabled, as opaque
   origins do not normally allow access to shared workers.

Bug: 40051700
Change-Id: If8db773b61c1ff9e5ef00db6548f618b2e8f12c0
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7714382
Reviewed-by: Rakina Zata Amni <rakina@chromium.org>
Reviewed-by: Hidehiko Abe <hidehiko@chromium.org>
Commit-Queue: Yoshisato Yanagisawa <yyanagisawa@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1610304}
---

diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 1742ffc7..a560c5f 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -2569,6 +2569,8 @@
     "worker_host/worker_script_loader.h",
     "worker_host/worker_script_loader_factory.cc",
     "worker_host/worker_script_loader_factory.h",
+    "worker_host/worker_util.cc",
+    "worker_host/worker_util.h",
   ]
 
   if (is_android) {
diff --git a/content/browser/browser_interface_binders.cc b/content/browser/browser_interface_binders.cc
index 8b9989d..baa5bd1 100644
--- a/content/browser/browser_interface_binders.cc
+++ b/content/browser/browser_interface_binders.cc
@@ -420,8 +420,9 @@
         auto* process_host =
             static_cast<RenderProcessHostImpl*>(host->GetProcessHost());
         CHECK(process_host);
-        process_host->CreateNotificationService(
-            rfh_id, creator_type, host->GetStorageKey(), std::move(receiver));
+        process_host->CreateNotificationService(rfh_id, creator_type,
+                                                host->GetWorkerStorageKey(),
+                                                std::move(receiver));
       },
       base::Unretained(host), rfh_id, creator_type);
 }
@@ -505,7 +506,8 @@
         auto* process_host =
             static_cast<RenderProcessHostImpl*>(host->GetProcessHost());
         if (process_host)
-          (process_host->*method)(host->GetStorageKey(), std::move(receiver));
+          (process_host->*method)(host->GetWorkerStorageKey(),
+                                  std::move(receiver));
       },
       base::Unretained(host), method);
 }
@@ -527,7 +529,7 @@
         auto* process_host =
             static_cast<RenderProcessHostImpl*>(host->GetProcessHost());
         if (process_host)
-          (process_host->*method)(host->GetStorageKey(), *host,
+          (process_host->*method)(host->GetWorkerStorageKey(), *host,
                                   std::move(receiver));
       },
       base::Unretained(host), method);
@@ -1297,7 +1299,7 @@
 
 // Dedicated workers
 const url::Origin& GetContextForHost(DedicatedWorkerHost* host) {
-  return host->GetStorageKey().origin();
+  return host->GetWorkerStorageKey().origin();
 }
 
 void PopulateDedicatedWorkerBinders(DedicatedWorkerHost* host,
@@ -1420,7 +1422,7 @@
         auto* process_host = host->GetProcessHost();
         GetContentClient()->browser()->BindTranslationManager(
             process_host, process_host->GetBrowserContext(), host,
-            host->GetStorageKey().origin(), std::move(receiver));
+            host->GetWorkerStorageKey().origin(), std::move(receiver));
       },
       base::Unretained(host)));
   map->Add<language_detection::mojom::ContentLanguageDetectionDriver>(
@@ -1540,7 +1542,7 @@
         auto* process_host = host->GetProcessHost();
         GetContentClient()->browser()->BindTranslationManager(
             process_host, process_host->GetBrowserContext(), host,
-            host->GetStorageKey().origin(), std::move(receiver));
+            host->GetWorkerStorageKey().origin(), std::move(receiver));
       },
       base::Unretained(host)));
   map->Add<language_detection::mojom::ContentLanguageDetectionDriver>(
diff --git a/content/browser/compute_pressure/pressure_service_for_worker_unittest.cc b/content/browser/compute_pressure/pressure_service_for_worker_unittest.cc
index 0c1b518..a2be8b8 100644
--- a/content/browser/compute_pressure/pressure_service_for_worker_unittest.cc
+++ b/content/browser/compute_pressure/pressure_service_for_worker_unittest.cc
@@ -18,6 +18,7 @@
 #include "content/browser/worker_host/dedicated_worker_service_impl.h"
 #include "content/browser/worker_host/shared_worker_host.h"
 #include "content/browser/worker_host/shared_worker_service_impl.h"
+#include "content/browser/worker_host/worker_util.h"
 #include "content/public/browser/shared_worker_instance.h"
 #include "content/public/test/navigation_simulator.h"
 #include "content/test/test_render_frame_host.h"
@@ -146,8 +147,8 @@
     worker_host_ = std::make_unique<DedicatedWorkerHost>(
         &worker_service_, blink::DedicatedWorkerToken(), rfh->GetProcess(),
         rfh->GetGlobalId(), rfh->GetGlobalId(), rfh->GetStorageKey(),
-        rfh->GetStorageKey().origin(), rfh->GetIsolationInfoForSubresources(),
-        rfh->BuildClientSecurityState(),
+        rfh->GetStorageKey(), rfh->GetStorageKey().origin(),
+        rfh->GetIsolationInfoForSubresources(), rfh->BuildClientSecurityState(),
         rfh->policy_container_host()->policies(),
         /*creator_coep_reporter=*/nullptr,
         /*network_restrictions_id=*/std::nullopt,
@@ -270,10 +271,15 @@
     pressure_manager_.reset();
 
     auto* rfh = contents()->GetPrimaryMainFrame();
+    blink::StorageKey worker_storage_key =
+        CalculateWorkerStorageKey(kWorkerUrl, rfh->GetStorageKey());
+    url::Origin renderer_origin =
+        CalculateWorkerRendererOrigin(kWorkerUrl, worker_storage_key);
+
     SharedWorkerInstance instance(
         kWorkerUrl, blink::mojom::ScriptType::kClassic,
         network::mojom::CredentialsMode::kSameOrigin, "name",
-        rfh->GetStorageKey(),
+        rfh->GetStorageKey(), worker_storage_key, renderer_origin,
         blink::mojom::SharedWorkerCreationContextType::kSecure,
         rfh->GetStorageKey().IsFirstPartyContext()
             ? blink::mojom::SharedWorkerSameSiteCookies::kAll
diff --git a/content/browser/devtools/dedicated_worker_devtools_agent_host.cc b/content/browser/devtools/dedicated_worker_devtools_agent_host.cc
index 811b327..f911bffa 100644
--- a/content/browser/devtools/dedicated_worker_devtools_agent_host.cc
+++ b/content/browser/devtools/dedicated_worker_devtools_agent_host.cc
@@ -50,7 +50,7 @@
 std::optional<blink::StorageKey>
 DedicatedWorkerDevToolsAgentHost::GetStorageKey() {
   DedicatedWorkerHost* const host = GetDedicatedWorkerHost();
-  return host ? std::make_optional(host->GetStorageKey()) : std::nullopt;
+  return host ? std::make_optional(host->GetWorkerStorageKey()) : std::nullopt;
 }
 
 std::string DedicatedWorkerDevToolsAgentHost::GetType() {
diff --git a/content/browser/devtools/shared_worker_devtools_agent_host.cc b/content/browser/devtools/shared_worker_devtools_agent_host.cc
index 28e3430..bbf038c 100644
--- a/content/browser/devtools/shared_worker_devtools_agent_host.cc
+++ b/content/browser/devtools/shared_worker_devtools_agent_host.cc
@@ -81,7 +81,7 @@
 }
 
 blink::StorageKey SharedWorkerDevToolsAgentHost::GetStorageKey() const {
-  return instance_.storage_key();
+  return instance_.worker_storage_key();
 }
 
 bool SharedWorkerDevToolsAgentHost::Activate() {
@@ -125,7 +125,7 @@
 bool SharedWorkerDevToolsAgentHost::Matches(SharedWorkerHost* worker_host) {
   return instance_.Matches(worker_host->instance().url(),
                            worker_host->instance().name(),
-                           worker_host->instance().storage_key(),
+                           worker_host->instance().creator_storage_key(),
                            worker_host->instance().same_site_cookies());
 }
 
diff --git a/content/browser/direct_sockets/direct_sockets_service_impl.cc b/content/browser/direct_sockets/direct_sockets_service_impl.cc
index 151c5546..f605ce0 100644
--- a/content/browser/direct_sockets/direct_sockets_service_impl.cc
+++ b/content/browser/direct_sockets/direct_sockets_service_impl.cc
@@ -294,7 +294,10 @@
                   /*access_allowed=*/shared_worker &&
                   ArePermissionTypesAllowedForWorker(
                       shared_worker->GetProcessHost(),
-                      shared_worker->instance().storage_key().origin(),
+                      // Use the worker's own origin for permission checks. This
+                      // ensures that data: URL workers, which have opaque
+                      // origins, are denied sensitive permissions.
+                      shared_worker->instance().worker_storage_key().origin(),
                       std::move(required_permissions)));
             } else {
               std::move(callback)
diff --git a/content/browser/service_worker/service_worker_client.cc b/content/browser/service_worker/service_worker_client.cc
index 5da2325..58523e5f 100644
--- a/content/browser/service_worker/service_worker_client.cc
+++ b/content/browser/service_worker/service_worker_client.cc
@@ -608,7 +608,7 @@
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/content/browser/compute_pressure/pressure_service_for_worker_unittest.cc b/content/browser/compute_pressure/pressure_service_for_worker_unittest.cc
index 0c1b518..a2be8b8 100644
--- a/content/browser/compute_pressure/pressure_service_for_worker_unittest.cc
+++ b/content/browser/compute_pressure/pressure_service_for_worker_unittest.cc
@@ -18,6 +18,7 @@
 #include "content/browser/worker_host/dedicated_worker_service_impl.h"
 #include "content/browser/worker_host/shared_worker_host.h"
 #include "content/browser/worker_host/shared_worker_service_impl.h"
+#include "content/browser/worker_host/worker_util.h"
 #include "content/public/browser/shared_worker_instance.h"
 #include "content/public/test/navigation_simulator.h"
 #include "content/test/test_render_frame_host.h"
@@ -146,8 +147,8 @@
     worker_host_ = std::make_unique<DedicatedWorkerHost>(
         &worker_service_, blink::DedicatedWorkerToken(), rfh->GetProcess(),
         rfh->GetGlobalId(), rfh->GetGlobalId(), rfh->GetStorageKey(),
-        rfh->GetStorageKey().origin(), rfh->GetIsolationInfoForSubresources(),
-        rfh->BuildClientSecurityState(),
+        rfh->GetStorageKey(), rfh->GetStorageKey().origin(),
+        rfh->GetIsolationInfoForSubresources(), rfh->BuildClientSecurityState(),
         rfh->policy_container_host()->policies(),
         /*creator_coep_reporter=*/nullptr,
         /*network_restrictions_id=*/std::nullopt,
@@ -270,10 +271,15 @@
     pressure_manager_.reset();
 
     auto* rfh = contents()->GetPrimaryMainFrame();
+    blink::StorageKey worker_storage_key =
+        CalculateWorkerStorageKey(kWorkerUrl, rfh->GetStorageKey());
+    url::Origin renderer_origin =
+        CalculateWorkerRendererOrigin(kWorkerUrl, worker_storage_key);
+
     SharedWorkerInstance instance(
         kWorkerUrl, blink::mojom::ScriptType::kClassic,
         network::mojom::CredentialsMode::kSameOrigin, "name",
-        rfh->GetStorageKey(),
+        rfh->GetStorageKey(), worker_storage_key, renderer_origin,
         blink::mojom::SharedWorkerCreationContextType::kSecure,
         rfh->GetStorageKey().IsFirstPartyContext()
             ? blink::mojom::SharedWorkerSameSiteCookies::kAll
diff --git a/content/browser/worker_host/shared_worker_host_unittest.cc b/content/browser/worker_host/shared_worker_host_unittest.cc
index 231dde9..7a718e3 100644
--- a/content/browser/worker_host/shared_worker_host_unittest.cc
+++ b/content/browser/worker_host/shared_worker_host_unittest.cc
@@ -24,6 +24,7 @@
 #include "content/browser/worker_host/shared_worker_connector_impl.h"
 #include "content/browser/worker_host/shared_worker_service_impl.h"
 #include "content/browser/worker_host/worker_script_fetcher.h"
+#include "content/browser/worker_host/worker_util.h"
 #include "content/public/browser/shared_worker_instance.h"
 #include "content/public/common/content_features.h"
 #include "content/public/test/browser_task_environment.h"
@@ -84,10 +85,16 @@
 
   base::WeakPtr<SharedWorkerHost> CreateHostWithExtendedLifetime(
       bool extended_lifetime) {
+    blink::StorageKey creator_storage_key =
+        blink::StorageKey::CreateFirstParty(url::Origin::Create(kWorkerUrl));
+    blink::StorageKey worker_storage_key =
+        CalculateWorkerStorageKey(kWorkerUrl, creator_storage_key);
+    url::Origin renderer_origin =
+        CalculateWorkerRendererOrigin(kWorkerUrl, worker_storage_key);
     SharedWorkerInstance instance(
         kWorkerUrl, blink::mojom::ScriptType::kClassic,
         network::mojom::CredentialsMode::kSameOrigin, "name",
-        blink::StorageKey::CreateFirstParty(url::Origin::Create(kWorkerUrl)),
+        creator_storage_key, worker_storage_key, renderer_origin,
         blink::mojom::SharedWorkerCreationContextType::kSecure,
         blink::mojom::SharedWorkerSameSiteCookies::kAll, extended_lifetime);
     auto host = std::make_unique<SharedWorkerHost>(
@@ -391,7 +398,7 @@
 
   network::mojom::URLLoaderFactoryParamsPtr params =
       host->CreateNetworkFactoryParamsForSubresources();
-  EXPECT_EQ(host->GetStorageKey().origin(),
+  EXPECT_EQ(host->GetWorkerStorageKey().origin(),
             params->isolation_info.frame_origin());
   EXPECT_FALSE(params->isolation_info.nonce().has_value());
 }
@@ -399,11 +406,17 @@
 TEST_F(SharedWorkerHostTest,
        CreateNetworkFactoryParamsForSubresourcesWithNonce) {
   base::UnguessableToken nonce = base::UnguessableToken::Create();
+  blink::StorageKey creator_storage_key = blink::StorageKey::CreateWithNonce(
+      url::Origin::Create(kWorkerUrl), nonce);
+  blink::StorageKey worker_storage_key =
+      CalculateWorkerStorageKey(kWorkerUrl, creator_storage_key);
+  url::Origin renderer_origin =
+      CalculateWorkerRendererOrigin(kWorkerUrl, worker_storage_key);
+
   SharedWorkerInstance instance(
       kWorkerUrl, blink::mojom::ScriptType::kClassic,
-      network::mojom::CredentialsMode::kSameOrigin, "name",
-      blink::StorageKey::CreateWithNonce(url::Origin::Create(kWorkerUrl),
-                                         nonce),
+      network::mojom::CredentialsMode::kSameOrigin, "name", creator_storage_key,
+      worker_storage_key, renderer_origin,
       blink::mojom::SharedWorkerCreationContextType::kSecure,
       blink::mojom::SharedWorkerSameSiteCookies::kNone,
       /*extended_lifetime=*/false);
@@ -443,10 +456,17 @@
 
 TEST_F(SharedWorkerHostTestWithLNAEnabled,
        CreateNetworkFactoryParamsForSubresources) {
+  blink::StorageKey creator_storage_key =
+      blink::StorageKey::CreateFirstParty(url::Origin::Create(kWorkerUrl));
+  blink::StorageKey worker_storage_key =
+      CalculateWorkerStorageKey(kWorkerUrl, creator_storage_key);
+  url::Origin renderer_origin =
+      CalculateWorkerRendererOrigin(kWorkerUrl, worker_storage_key);
+
   SharedWorkerInstance instance(
       kWorkerUrl, blink::mojom::ScriptType::kClassic,
-      network::mojom::CredentialsMode::kSameOrigin, "name",
-      blink::StorageKey::CreateFirstParty(url::Origin::Create(kWorkerUrl)),
+      network::mojom::CredentialsMode::kSameOrigin, "name", creator_storage_key,
+      worker_storage_key, renderer_origin,
       blink::mojom::SharedWorkerCreationContextType::kSecure,
       blink::mojom::SharedWorkerSameSiteCookies::kAll,
       /*extended_lifetime=*/false);
diff --git a/content/browser/worker_host/shared_worker_instance_unittest.cc b/content/browser/worker_host/shared_worker_instance_unittest.cc
index 9d319a0..59c34fc 100644
--- a/content/browser/worker_host/shared_worker_instance_unittest.cc
+++ b/content/browser/worker_host/shared_worker_instance_unittest.cc
@@ -8,9 +8,12 @@
 #include <string>
 #include <string_view>
 
+#include "base/feature_list.h"
 #include "base/strings/utf_string_conversions.h"
+#include "content/browser/worker_host/worker_util.h"
 #include "services/network/public/mojom/content_security_policy.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/common/storage_key/storage_key.h"
 
 namespace content {
@@ -25,9 +28,14 @@
   SharedWorkerInstance CreateInstance(const GURL& script_url,
                                       const std::string& name,
                                       const blink::StorageKey& storage_key) {
+    blink::StorageKey worker_storage_key =
+        CalculateWorkerStorageKey(script_url, storage_key);
+    url::Origin renderer_origin =
+        CalculateWorkerRendererOrigin(script_url, worker_storage_key);
     return SharedWorkerInstance(
         script_url, blink::mojom::ScriptType::kClassic,
         network::mojom::CredentialsMode::kSameOrigin, name, storage_key,
+        worker_storage_key, renderer_origin,
         blink::mojom::SharedWorkerCreationContextType::kNonsecure,
         storage_key.IsFirstPartyContext()
             ? blink::mojom::SharedWorkerSameSiteCookies::kAll
diff --git a/third_party/blink/web_tests/external/wpt/webmessaging/broadcastchannel/opaque-origin-expected.txt b/third_party/blink/web_tests/external/wpt/webmessaging/broadcastchannel/opaque-origin-expected.txt
deleted file mode 100644
index a1d3fec..0000000
--- a/third_party/blink/web_tests/external/wpt/webmessaging/broadcastchannel/opaque-origin-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] BroadcastChannel messages from data URL dedicated workers should be self-contained
-  promise_test: Unhandled rejection with value: "Received message from an opaque origin"
-[FAIL] BroadcastChannel messages from data URL shared workers should be self-contained
-  promise_test: Unhandled rejection with value: "Received message from an opaque origin"
-Harness: the test ran to completion.
-
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.