Chrome · Workers › ServiceWorker
CVE-2026-110914
Memory Corruption in Workers (ServiceWorker)
Overview
Medium
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifcontent/browser/worker_host/shared_worker_host.cc |
modified | |
ifcontent/browser/worker_host/worker_script_fetcher.cc |
modified | |
DedicatedWorkerServiceImplTestcontent/browser/worker_host/dedicated_worker_service_impl_unittest.cc |
modified | |
ifcontent/browser/worker_host/dedicated_worker_service_impl_unittest.cc |
modified |
Files Changed
content/browser/worker_host/worker_script_fetcher_unittest.cccontent/browser/worker_host/dedicated_worker_service_impl_unittest.ccthird_party/blink/web_tests/external/wpt/html/cross-origin-embedder-policy/reflection-require-corp.tentative.https.any.worker-expected.txtcontent/browser/worker_host/worker_script_fetcher.hcontent/browser/worker_host/dedicated_worker_host.cccontent/common/features.hcontent/browser/worker_host/shared_worker_host.cccontent/browser/worker_host/dedicated_worker_host.hcontent/browser/worker_host/shared_worker_host_unittest.cccontent/browser/worker_host/worker_script_fetcher.cccontent/common/features.ccthird_party/blink/web_tests/external/wpt/html/cross-origin-embedder-policy/reflection-credentialless.tentative.https.any.worker-expected.txt
Patch
From 08f529c57faad9e8bf7cb3881576d0c5fa4b984e Mon Sep 17 00:00:00 2001
From: Takashi Nakayama <tnak@chromium.org>
Date: Tue, 06 Oct 2026 00:02:02 -0700
Subject: [PATCH] ServiceWorker: Drop ServiceWorkerMainResourceHandle for CSP-sandboxed workers
When shared and dedicated worker scripts are received and started, its
PolicyContainerPolicies must be computed and checked to prevent invalid
ServiceWorker from running for the shared/dedicated worker.
This CL ensures the calculation of PolicyContainerPolicies in
DidCreateScriptLoader() and drops ServiceWorkerHandle if the worker is
sandboxed by CSP.
See the bug for details.
Bug: 41478971, 563194341
Change-Id: I8e1df63ea670b10a3bfec73dedd47036ae62dd5e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8461264
Reviewed-by: Rakina Zata Amni <rakina@chromium.org>
Reviewed-by: Yoshisato Yanagisawa <yyanagisawa@chromium.org>
Commit-Queue: Takashi Nakayama <tnak@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1712040}
---
diff --git a/content/browser/worker_host/dedicated_worker_host.cc b/content/browser/worker_host/dedicated_worker_host.cc
index cf8c8f63..b206e6eb3 100644
--- a/content/browser/worker_host/dedicated_worker_host.cc
+++ b/content/browser/worker_host/dedicated_worker_host.cc
@@ -42,6 +42,7 @@
#include "content/browser/worker_host/dedicated_worker_service_impl.h"
#include "content/browser/worker_host/worker_script_fetcher.h"
#include "content/browser/worker_host/worker_util.h"
+#include "content/common/features.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/network_service_util.h"
@@ -227,8 +228,8 @@
mojo::PendingReceiver<blink::mojom::LockManager> receiver) {
GetStoragePartitionImpl()->BindLockManager(
GetWorkerStorageKey(), GetToken().value(), std::move(receiver));
- GetStoragePartitionImpl()->GetLockManager()->AddLockObserver(GetToken().value(),
- this);
+ GetStoragePartitionImpl()->GetLockManager()->AddLockObserver(
+ GetToken().value(), this);
}
bool DedicatedWorkerHost::OnLockContention() {
@@ -546,7 +547,6 @@
worker_client_security_state_->cross_origin_embedder_policy =
result->main_script_load_params->response_head->parsed_headers
->cross_origin_embedder_policy;
-
}
// The worker global scope's document isolation policy is the same as its
@@ -609,6 +609,18 @@
auto policy_container_host = base::MakeRefCounted<PolicyContainerHost>(
std::move(result->policy_container_policies));
+ // A worker sandboxed without `allow-same-origin` has an opaque origin and
+ // is not eligible to use service workers (mirroring
+ // `NavigationRequest::CommitNavigation()`).
+ if (base::FeatureList::IsEnabled(
+ features::kServiceWorkerDropHandleForCSPSandboxedWorker) &&
+ service_worker_handle_ &&
+ (policy_container_host->policies().sandbox_flags &
+ network::mojom::WebSandboxFlags::kOrigin) ==
+ network::mojom::WebSandboxFlags::kOrigin) {
+ service_worker_handle_.reset();
+ }
+
blink::mojom::ServiceWorkerContainerInfoForClientPtr container_info;
blink::mojom::ControllerServiceWorkerInfoPtr controller;
if (GetServiceWorkerClient()) {
diff --git a/content/browser/worker_host/dedicated_worker_host.h b/content/browser/worker_host/dedicated_worker_host.h
index 21a3f05..5bb43195 100644
--- a/content/browser/worker_host/dedicated_worker_host.h
+++ b/content/browser/worker_host/dedicated_worker_host.h
@@ -301,6 +301,8 @@
}
private:
+ friend class DedicatedWorkerServiceImplTest;
+
// RenderProcessHostObserver:
void RenderProcessExited(RenderProcessHost* render_process_host,
const ChildProcessTerminationInfo& info) override;
diff --git a/content/browser/worker_host/shared_worker_host.cc b/content/browser/worker_host/shared_worker_host.cc
index 4d2e7f7..dcbf9f96 100644
--- a/content/browser/worker_host/shared_worker_host.cc
+++ b/content/browser/worker_host/shared_worker_host.cc
@@ -317,12 +317,18 @@
/*frame_tree_node=*/std::nullopt);
}
- // https://html.spec.whatwg.org/C/#creating-a-policy-container-from-a-fetch-response
- // This does not parse the referrer policy, which will be
- // updated in `SharedWorkerGlobalScope::Initialize()`.
- PolicyContainerPolicies policies(
- result.final_response_url,
- result.main_script_load_params->response_head.get(), nullptr);
+ PolicyContainerPolicies policies;
+ if (base::FeatureList::IsEnabled(
+ features::kServiceWorkerDropHandleForCSPSandboxedWorker)) {
+ policies = std::move(result.policy_container_policies);
+ } else {
+ // https://html.spec.whatwg.org/C/#creating-a-policy-container-from-a-fetch-response
+ // This does not parse the referrer policy, which will be updated in
+ // `SharedWorkerGlobalScope::Initialize()`.
+ policies = PolicyContainerPolicies(
+ result.final_response_url,
+ result.main_script_load_params->response_head.get(), nullptr);
+ }
// A worker context can only be secure if its creator also is.
if (!creator_policy_container_host_->policies().is_web_secure_context) {
@@ -433,9 +439,22 @@
result.subresource_loader_factories->set_bypass_redirect_checks(
bypass_redirect_checks);
+ // A worker sandboxed without `allow-same-origin` has an opaque origin and
+ // is not eligible to use service workers (mirroring
+ // `NavigationRequest::CommitNavigation()`).
+ if (base::FeatureList::IsEnabled(
+ features::kServiceWorkerDropHandleForCSPSandboxedWorker) &&
+ service_worker_handle_ &&
+ (policy_container_host->policies().sandbox_flags &
+ network::mojom::WebSandboxFlags::kOrigin) ==
+ network::mojom::WebSandboxFlags::kOrigin) {
+ service_worker_handle_.reset();
+ }
+
blink::mojom::ServiceWorkerContainerInfoForClientPtr container_info;
blink::mojom::ControllerServiceWorkerInfoPtr controller;
- if (service_worker_handle_->service_worker_client()) {
+ if (service_worker_handle_ &&
+ service_worker_handle_->service_worker_client()) {
mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter>
coep_reporter;
if (coep_reporter_) {
@@ -450,7 +469,10 @@
service_worker_handle_->scoped_service_worker_client()
->CommitResponseAndRelease(
/*rfh_id=*/std::nullopt,
- std::move(result.policy_container_policies),
+ base::FeatureList::IsEnabled(
+ features::kServiceWorkerDropHandleForCSPSandboxedWorker)
+ ? policy_container_host->policies()
+ : result.policy_container_policies,
std::move(coep_reporter), std::move(dip_reporter),
ukm_source_id());
}
@@ -502,7 +524,8 @@
instance_.DoesRequireCrossSiteRequestForCookies(),
std::move(coep_reporting_observer), std::move(dip_reporting_observer),
cross_origin_isolated);
- if (service_worker_handle_->service_worker_client()) {
+ if (service_worker_handle_ &&
+ service_worker_handle_->service_worker_client()) {
service_worker_handle_->service_worker_client()->SetContainerReady();
}
diff --git a/content/browser/worker_host/worker_script_fetcher.cc b/content/browser/worker_host/worker_script_fetcher.cc
index 522d8c1..77f5160 100644
--- a/content/browser/worker_host/worker_script_fetcher.cc
+++ b/content/browser/worker_host/worker_script_fetcher.cc
@@ -31,6 +31,7 @@
#include "content/browser/worker_host/network_restrictions_worker_throttle.h"
#include "content/browser/worker_host/worker_script_loader.h"
#include "content/browser/worker_host/worker_script_loader_factory.h"
+#include "content/common/features.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/child_process_host.h"
@@ -127,6 +128,28 @@
/*is_for_worker_script=*/true);
}
+// Build `PolicyContainerPolicies` from the worker script response headers or
+// inherit from the creator for local schemes.
+PolicyContainerPolicies ComputePolicyContainerPoliciesForWorker(
+ const GURL& final_response_url,
+ network::mojom::URLResponseHead* response_head,
+ const PolicyContainerPolicies* creator_policies) {
+ PolicyContainerPolicies policies;
+ if (base::FeatureList::IsEnabled(
+ features::kServiceWorkerDropHandleForCSPSandboxedWorker)) {
+ if (final_response_url.SchemeIsLocal() && creator_policies) {
+ policies = creator_policies->Clone();
+ } else if (response_head && response_head->parsed_headers) {
+ policies =
+ PolicyContainerPolicies(final_response_url, response_head, nullptr);
+ }
+ }
+ policies.connection_allowlists = GetConnectionAllowlistsForWorker(
+ final_response_url, response_head, creator_policies,
+ final_response_url.SchemeIsLocal());
+ return policies;
+}
+
void DidCreateScriptLoader(
WorkerScriptFetcher::CompletionCallback callback,
std::unique_ptr<blink::PendingURLLoaderFactoryBundle>
@@ -176,14 +199,12 @@
// The load succeeded iff `main_script_load_params` is not nullptr.
if (main_script_load_params) {
- // TODO(crbug.com/41478971): Pass the PolicyContainerPolicies. It can
- // be built from the
- // `main_script_load_params.response_head->parsed_headers`.
- PolicyContainerPolicies policies;
- policies.connection_allowlists = GetConnectionAllowlistsForWorker(
+ // Note: Unlike in the spec, the referrer policy will be lazily calculated
+ // by the renderer in `(Shared/Dedicated)WorkerGlobalScope::Initialize()`.
+ // https://html.spec.whatwg.org/C/#creating-a-policy-container-from-a-fetch-response
+ PolicyContainerPolicies policies = ComputePolicyContainerPoliciesForWorker(
final_response_url, main_script_load_params->response_head.get(),
- base::OptionalToPtr(creator_policies),
- final_response_url.SchemeIsLocal());
+ base::OptionalToPtr(creator_policies));
std::move(callback).Run(std::make_optional<WorkerScriptFetcherResult>(
std::move(subresource_loader_factories),
@@ -713,6 +734,16 @@
return initial_request_url;
}
+// static
+PolicyContainerPolicies
+WorkerScriptFetcher::ComputePolicyContainerPoliciesForTesting(
+ const GURL& final_response_url,
+ network::mojom::URLResponseHead* response_head,
+ const PolicyContainerPolicies* creator_policies) {
+ return ComputePolicyContainerPoliciesForWorker(
+ final_response_url, response_head, creator_policies);
+}
+
WorkerScriptFetcher::WorkerScriptFetcher(
std::unique_ptr<WorkerScriptLoaderFactory> script_loader_factory,
std::unique_ptr<network::ResourceRequest> resource_request,
diff --git a/content/browser/worker_host/worker_script_fetcher.h b/content/browser/worker_host/worker_script_fetcher.h
index 0c20572..de982a15 100644
--- a/content/browser/worker_host/worker_script_fetcher.h
+++ b/content/browser/worker_host/worker_script_fetcher.h
@@ -176,6 +176,12 @@
const GURL& initial_request_url,
blink::mojom::WorkerMainScriptLoadParams* main_script_load_params);
+ CONTENT_EXPORT static PolicyContainerPolicies
+ ComputePolicyContainerPoliciesForTesting(
+ const GURL& final_response_url,
+ network::mojom::URLResponseHead* response_head,
+ const PolicyContainerPolicies* creator_policies);
+
private:
// Callback invoked by this instance when the load ends, successfully or not.
//
diff --git a/content/common/features.cc b/content/common/features.cc
index 8d0431b0..0a49092b0 100644
--- a/content/common/features.cc
+++ b/content/common/features.cc
@@ -755,6 +755,10 @@
&kServiceWorkerBypassFetchHandlerHashStrings,
"script_checksum_to_bypass", ""};
+// Killswitch for crbug.com/563194341
+BASE_FEATURE(kServiceWorkerDropHandleForCSPSandboxedWorker,
+ base::FEATURE_ENABLED_BY_DEFAULT);
+
// (crbug.com/41411856): When enabled, the srcdoc iframes are controlled by the
// same service worker that controls their parent.
BASE_FEATURE(kServiceWorkerSrcdocSupport, base::FEATURE_ENABLED_BY_DEFAULT);
diff --git a/content/common/features.h b/content/common/features.h
index 8cddb975..76487c0d 100644
--- a/content/common/features.h
+++ b/content/common/features.h
@@ -233,6 +233,8 @@
kServiceWorkerBypassFetchHandlerHashStrings);
CONTENT_EXPORT extern const base::FeatureParam<std::string>
kServiceWorkerBypassFetchHandlerBypassedHashStrings;
+CONTENT_EXPORT BASE_DECLARE_FEATURE(
+ kServiceWorkerDropHandleForCSPSandboxedWorker);
CONTENT_EXPORT BASE_DECLARE_FEATURE(kServiceWorkerSrcdocSupport);
CONTENT_EXPORT BASE_DECLARE_FEATURE(kServiceWorkerStaticRouterCORPCheck);
CONTENT_EXPORT BASE_DECLARE_FEATURE(kServiceWorkerStaticRouterOpaqueCheck);
diff --git a/content/browser/worker_host/dedicated_worker_service_impl_unittest.cc b/content/browser/worker_host/dedicated_worker_service_impl_unittest.cc
index d4e3f17e..6bafa74 100644
--- a/content/browser/worker_host/dedicated_worker_service_impl_unittest.cc
+++ b/content/browser/worker_host/dedicated_worker_service_impl_unittest.cc
@@ -19,6 +19,7 @@
#include "content/browser/storage_partition_impl.h"
#include "content/browser/worker_host/dedicated_worker_host.h"
#include "content/browser/worker_host/dedicated_worker_host_factory_impl.h"
+#include "content/browser/worker_host/worker_script_fetcher.h"
#include "content/common/content_navigation_policy.h"
#include "content/public/browser/back_forward_cache.h"
#include "content/public/browser/storage_partition.h"
@@ -36,6 +37,8 @@
#include "net/storage_access_api/status.h"
#include "services/network/public/cpp/constants.h"
#include "services/network/public/mojom/client_security_state.mojom.h"
+#include "services/network/public/mojom/parsed_headers.mojom.h"
+#include "services/network/public/mojom/url_response_head.mojom.h"
#include "third_party/blink/public/common/storage_key/storage_key.h"
#include "third_party/blink/public/common/tokens/tokens.h"
#include "third_party/blink/public/common/tokens/tokens_mojom_traits.h"
@@ -71,7 +74,7 @@
// ignored.
auto* ancestor_rfh =
RenderFrameHostImpl::FromID(ancestor_render_frame_host_id);
- auto coep_reporter = std::make_unique<CrossOriginEmbedderPolicyReporter>(
+ coep_reporter_ = std::make_unique<CrossOriginEmbedderPolicyReporter>(
ancestor_rfh->GetStoragePartition()->GetWeakPtr(), GURL(), std::nullopt,
std::nullopt, base::UnguessableToken::Create(),
net::NetworkAnonymizationKey());
@@ -82,7 +85,7 @@
blink::StorageKey::CreateFirstParty(origin),
net::IsolationInfo::CreateTransient(/*nonce=*/std::nullopt),
network::mojom::ClientSecurityState::New(), PolicyContainerPolicies(),
- coep_reporter->GetWeakPtr(), network::GetTestNetworkRestrictionsId());
+ coep_reporter_->GetWeakPtr(), network::GetTestNetworkRestrictionsId());
auto fetch_client_settings_object =
blink::mojom::FetchClientSettingsObject::New();
@@ -131,6 +134,7 @@
void OnScriptLoadStartFailed() override {}
private:
+ std::unique_ptr<CrossOriginEmbedderPolicyReporter> coep_reporter_;
mojo::Receiver<blink::mojom::DedicatedWorkerHostFactoryClient> receiver_{
this};
@@ -141,8 +145,7 @@
mojo::Remote<blink::mojom::DedicatedWorkerHost> remote_host_;
};
-class DedicatedWorkerServiceImplTest
- : public RenderViewHostImplTestHarness {
+class DedicatedWorkerServiceImplTest : public RenderViewHostImplTestHarness {
public:
DedicatedWorkerServiceImplTest() = default;
~DedicatedWorkerServiceImplTest() override = default;
@@ -182,6 +185,40 @@
->GetDedicatedWorkerService());
}
+ void DidStartScriptLoad(DedicatedWorkerHost* host,
+ const GURL& final_response_url,
+ network::mojom::URLResponseHeadPtr response_head) {
+ auto main_script_load_params =
+ blink::mojom::WorkerMainScriptLoadParams::New();
+ if (!response_head) {
+ response_head = network::mojom::URLResponseHead::New();
+ }
+ if (!response_head->parsed_headers) {
+ response_head->parsed_headers = network::mojom::ParsedHeaders::New();
+ }
+ main_script_load_params->response_head = std::move(response_head);
+ mojo::ScopedDataPipeProducerHandle producer_handle;
+ mojo::ScopedDataPipeConsumerHandle consumer_handle;
+ ASSERT_EQ(MOJO_RESULT_OK,
+ mojo::CreateDataPipe(nullptr, producer_handle, consumer_handle));
+ main_script_load_params->response_body = std::move(consumer_handle);
+ auto subresource_loader_factories =
+ std::make_unique<blink::PendingURLLoaderFactoryBundle>();
+
+ PolicyContainerPolicies policies =
+ WorkerScriptFetcher::ComputePolicyContainerPoliciesForTesting(
+ final_response_url, main_script_load_params->response_head.get(),
+ /*creator_policies=*/nullptr);
+ host->DidStartScriptLoad(
+ WorkerScriptFetcherResult(std::move(subresource_loader_factories),
+ std::move(main_script_load_params),
+ std::move(policies), final_response_url));
+ }
+
+ bool HasServiceWorkerHandle(DedicatedWorkerHost* host) const {
+ return host->service_worker_handle_ != nullptr;
+ }
+
private:
std::unique_ptr<TestBrowserContext> browser_context_;
};
@@ -219,16 +256,18 @@
.second;
DCHECK(inserted);
- if (on_worker_event_callback_)
+ if (on_worker_event_callback_) {
std::move(on_worker_event_callback_).Run();
+ }
}
void OnBeforeWorkerDestroyed(const blink::DedicatedWorkerToken& token,
DedicatedWorkerCreator creator) override {
size_t removed = dedicated_worker_infos_.erase(token);
DCHECK_EQ(removed, 1u);
- if (on_worker_event_callback_)
+ if (on_worker_event_callback_) {
std::move(on_worker_event_callback_).Run();
+ }
... (file diff truncated)
Loading diff…
Regression Test / PoC
shipped with the fix
diff --git a/content/browser/worker_host/dedicated_worker_service_impl_unittest.cc b/content/browser/worker_host/dedicated_worker_service_impl_unittest.cc
index d4e3f17e..6bafa74 100644
--- a/content/browser/worker_host/dedicated_worker_service_impl_unittest.cc
+++ b/content/browser/worker_host/dedicated_worker_service_impl_unittest.cc
@@ -19,6 +19,7 @@
#include "content/browser/storage_partition_impl.h"
#include "content/browser/worker_host/dedicated_worker_host.h"
#include "content/browser/worker_host/dedicated_worker_host_factory_impl.h"
+#include "content/browser/worker_host/worker_script_fetcher.h"
#include "content/common/content_navigation_policy.h"
#include "content/public/browser/back_forward_cache.h"
#include "content/public/browser/storage_partition.h"
@@ -36,6 +37,8 @@
#include "net/storage_access_api/status.h"
#include "services/network/public/cpp/constants.h"
#include "services/network/public/mojom/client_security_state.mojom.h"
+#include "services/network/public/mojom/parsed_headers.mojom.h"
+#include "services/network/public/mojom/url_response_head.mojom.h"
#include "third_party/blink/public/common/storage_key/storage_key.h"
#include "third_party/blink/public/common/tokens/tokens.h"
#include "third_party/blink/public/common/tokens/tokens_mojom_traits.h"
@@ -71,7 +74,7 @@
// ignored.
auto* ancestor_rfh =
RenderFrameHostImpl::FromID(ancestor_render_frame_host_id);
- auto coep_reporter = std::make_unique<CrossOriginEmbedderPolicyReporter>(
+ coep_reporter_ = std::make_unique<CrossOriginEmbedderPolicyReporter>(
ancestor_rfh->GetStoragePartition()->GetWeakPtr(), GURL(), std::nullopt,
std::nullopt, base::UnguessableToken::Create(),
net::NetworkAnonymizationKey());
@@ -82,7 +85,7 @@
blink::StorageKey::CreateFirstParty(origin),
net::IsolationInfo::CreateTransient(/*nonce=*/std::nullopt),
network::mojom::ClientSecurityState::New(), PolicyContainerPolicies(),
- coep_reporter->GetWeakPtr(), network::GetTestNetworkRestrictionsId());
+ coep_reporter_->GetWeakPtr(), network::GetTestNetworkRestrictionsId());
auto fetch_client_settings_object =
blink::mojom::FetchClientSettingsObject::New();
@@ -131,6 +134,7 @@
void OnScriptLoadStartFailed() override {}
private:
+ std::unique_ptr<CrossOriginEmbedderPolicyReporter> coep_reporter_;
mojo::Receiver<blink::mojom::DedicatedWorkerHostFactoryClient> receiver_{
this};
@@ -141,8 +145,7 @@
mojo::Remote<blink::mojom::DedicatedWorkerHost> remote_host_;
};
-class DedicatedWorkerServiceImplTest
- : public RenderViewHostImplTestHarness {
+class DedicatedWorkerServiceImplTest : public RenderViewHostImplTestHarness {
public:
DedicatedWorkerServiceImplTest() = default;
~DedicatedWorkerServiceImplTest() override = default;
@@ -182,6 +185,40 @@
->GetDedicatedWorkerService());
}
+ void DidStartScriptLoad(DedicatedWorkerHost* host,
+ const GURL& final_response_url,
+ network::mojom::URLResponseHeadPtr response_head) {
+ auto main_script_load_params =
+ blink::mojom::WorkerMainScriptLoadParams::New();
+ if (!response_head) {
+ response_head = network::mojom::URLResponseHead::New();
+ }
+ if (!response_head->parsed_headers) {
+ response_head->parsed_headers = network::mojom::ParsedHeaders::New();
+ }
+ main_script_load_params->response_head = std::move(response_head);
+ mojo::ScopedDataPipeProducerHandle producer_handle;
+ mojo::ScopedDataPipeConsumerHandle consumer_handle;
+ ASSERT_EQ(MOJO_RESULT_OK,
+ mojo::CreateDataPipe(nullptr, producer_handle, consumer_handle));
+ main_script_load_params->response_body = std::move(consumer_handle);
+ auto subresource_loader_factories =
+ std::make_unique<blink::PendingURLLoaderFactoryBundle>();
+
+ PolicyContainerPolicies policies =
+ WorkerScriptFetcher::ComputePolicyContainerPoliciesForTesting(
+ final_response_url, main_script_load_params->response_head.get(),
+ /*creator_policies=*/nullptr);
+ host->DidStartScriptLoad(
+ WorkerScriptFetcherResult(std::move(subresource_loader_factories),
+ std::move(main_script_load_params),
+ std::move(policies), final_response_url));
+ }
+
+ bool HasServiceWorkerHandle(DedicatedWorkerHost* host) const {
+ return host->service_worker_handle_ != nullptr;
+ }
+
private:
std::unique_ptr<TestBrowserContext> browser_context_;
};
@@ -219,16 +256,18 @@
.second;
DCHECK(inserted);
- if (on_worker_event_callback_)
+ if (on_worker_event_callback_) {
std::move(on_worker_event_callback_).Run();
+ }
}
void OnBeforeWorkerDestroyed(const blink::DedicatedWorkerToken& token,
DedicatedWorkerCreator creator) override {
size_t removed = dedicated_worker_infos_.erase(token);
DCHECK_EQ(removed, 1u);
- if (on_worker_event_callback_)
+ if (on_worker_event_callback_) {
std::move(on_worker_event_callback_).Run();
+ }
}
void OnFinalResponseURLDetermined(const blink::DedicatedWorkerToken& token,
const GURL& url) override {}
@@ -852,4 +891,52 @@
host->CreateBlobUrlStoreProvider(remote.BindNewPipeAndPassReceiver());
EXPECT_TRUE(remote.is_bound());
}
+
+// Tests that DedicatedWorkerHost::DidStartScriptLoad drops the
+// ServiceWorkerMainResourceHandle when the worker script response has a CSP
+// sandbox directive without allow-same-origin.
+TEST_F(DedicatedWorkerServiceImplTest,
+ ServiceWorkerHandleDroppedWhenCSPSandboxed) {
+ TestDedicatedWorkerServiceObserver observer;
+ base::ScopedObservation<DedicatedWorkerService,
+ DedicatedWorkerService::Observer>
+ scoped_observation(&observer);
+ scoped_observation.Observe(GetDedicatedWorkerService());
+
+ const GURL kUrl("https://example.com/");
+ std::unique_ptr<TestWebContents> web_contents = CreateWebContents(kUrl);
+ TestRenderFrameHost* render_frame_host = web_contents->GetPrimaryMainFrame();
+ const ChildProcessId render_process_host_id =
+ render_frame_host->GetProcess()->GetID();
+ const auto origin = url::Origin::Create(kUrl);
+ auto mock_dedicated_worker = std::make_unique<MockDedicatedWorker>(
+ render_process_host_id, render_frame_host->GetGlobalId(), origin);
+ observer.RunUntilWorkerEvent();
+ ASSERT_EQ(observer.dedicated_worker_infos().size(), 1u);
+ const blink::DedicatedWorkerToken worker_token =
+ observer.dedicated_worker_infos().begin()->first;
+ DedicatedWorkerHost* host =
+ GetDedicatedWorkerServiceImpl()->GetDedicatedWorkerHostFromToken(
+ worker_token);
+ ASSERT_TRUE(host);
+ EXPECT_TRUE(HasServiceWorkerHandle(host));
+
+ // Simulate receiving a worker script response with `Content-Security-Policy:
+ // sandbox` (which sets `WebSandboxFlags::kOrigin`).
+ auto response_head = network::mojom::URLResponseHead::New();
+ response_head->parsed_headers = network::mojom::ParsedHeaders::New();
+ auto csp = network::mojom::ContentSecurityPolicy::New();
+ csp->self_origin = network::mojom::CSPSource::New();
+ csp->header = network::mojom::ContentSecurityPolicyHeader::New();
+ csp->sandbox = network::mojom::WebSandboxFlags::kOrigin;
+ response_head->parsed_headers->content_security_policy.push_back(
+ std::move(csp));
+
+ // Verify that the service worker handle is dropped when script load starts.
+ DidStartScriptLoad(host, GURL("https://example.com/worker.js"),
+ std::move(response_head));
+ EXPECT_FALSE(HasServiceWorkerHandle(host));
+ EXPECT_FALSE(host->GetServiceWorkerClient());
+}
+
} // namespace content
diff --git a/content/browser/worker_host/shared_worker_host_unittest.cc b/content/browser/worker_host/shared_worker_host_unittest.cc
index f3b491c..687761e 100644
--- a/content/browser/worker_host/shared_worker_host_unittest.cc
+++ b/content/browser/worker_host/shared_worker_host_unittest.cc
@@ -169,22 +169,25 @@
host->SetServiceWorkerHandle(std::move(service_worker_handle));
TestContentBrowserClient client;
- host->Start(std::move(factory),
- blink::mojom::FetchClientSettingsObject::New(
- []() {
- auto policies =
- blink::mojom::PolicyContainerPolicies::New();
- policies->referrer_policy =
- network::mojom::ReferrerPolicy::kDefault;
- return policies;
- }(),
- /*outgoing_referrer=*/GURL(),
- blink::mojom::InsecureRequestsPolicy::kDoNotUpgrade),
- &client,
- WorkerScriptFetcherResult(
- std::move(subresource_loader_factories),
- std::move(main_script_load_params),
- PolicyContainerPolicies(), final_response_url));
+ PolicyContainerPolicies policies =
+ WorkerScriptFetcher::ComputePolicyContainerPoliciesForTesting(
+ final_response_url, main_script_load_params->response_head.get(),
+ /*creator_policies=*/nullptr);
+ host->Start(
+ std::move(factory),
+ blink::mojom::FetchClientSettingsObject::New(
+ []() {
+ auto policies = blink::mojom::PolicyContainerPolicies::New();
+ policies->referrer_policy =
+ network::mojom::ReferrerPolicy::kDefault;
+ return policies;
+ }(),
+ /*outgoing_referrer=*/GURL(),
+ blink::mojom::InsecureRequestsPolicy::kDoNotUpgrade),
+ &client,
+ WorkerScriptFetcherResult(std::move(subresource_loader_factories),
+ std::move(main_script_load_params),
+ std::move(policies), final_response_url));
}
MessagePortChannel AddClient(
@@ -201,6 +204,10 @@
return local_port;
}
+ bool HasServiceWorkerHandle(SharedWorkerHost* host) const {
+ return host->service_worker_handle_ != nullptr;
+ }
+
void RunNormalTest(bool extended_lifetime) {
base::WeakPtr<SharedWorkerHost> host = CreateHostWithExtendedLifetime(
/*extended_lifetime=*/extended_lifetime);
@@ -771,4 +778,24 @@
host_none->GetNetworkAnonymizationKey());
}
+TEST_F(SharedWorkerHostTest, ServiceWorkerHandleDroppedWhenCSPSandboxed) {
+ base::WeakPtr<SharedWorkerHost> host = CreateHost();
+ mojo::PendingRemote<blink::mojom::SharedWorkerFactory> factory;
+ MockSharedWorkerFactory factory_impl(
+ factory.InitWithNewPipeAndPassReceiver());
+
+ auto response_head = network::mojom::URLResponseHead::New();
+ response_head->parsed_headers = network::mojom::ParsedHeaders::New();
+ auto csp = network::mojom::ContentSecurityPolicy::New();
+ csp->self_origin = network::mojom::CSPSource::New();
+ csp->header = network::mojom::ContentSecurityPolicyHeader::New();
+ csp->sandbox = network::mojom::WebSandboxFlags::kOrigin;
+ response_head->parsed_headers->content_security_policy.push_back(
+ std::move(csp));
+
+ StartWorker(host.get(), std::move(factory),
+ GURL("https://www.example.com/w.js"), std::move(response_head));
+ EXPECT_FALSE(HasServiceWorkerHandle(host.get()));
+}
+
} // namespace content
diff --git a/content/browser/worker_host/worker_script_fetcher_unittest.cc b/content/browser/worker_host/worker_script_fetcher_unittest.cc
index b32dc0b..c86dcb8 100644
--- a/content/browser/worker_host/worker_script_fetcher_unittest.cc
+++ b/content/browser/worker_host/worker_script_fetcher_unittest.cc
@@ -6,6 +6,8 @@
#include <vector>
+#include "base/test/scoped_feature_list.h"
+#include "content/common/features.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/mojom/worker/worker_main_script_load_params.mojom.h"
#include "url/gurl.h"
@@ -94,4 +96,58 @@
}
}
+TEST(WorkerScriptFetcherTest, ComputePolicyContainerPolicies_NetworkResponse) {
+ auto response_head = network::mojom::URLResponseHead::New();
+ response_head->parsed_headers = network::mojom::ParsedHeaders::New();
+ auto csp = network::mojom::ContentSecurityPolicy::New();
+ csp->self_origin = network::mojom::CSPSource::New();
+ csp->header = network::mojom::ContentSecurityPolicyHeader::New();
+ csp->sandbox = network::mojom::WebSandboxFlags::kOrigin;
+ response_head->parsed_headers->content_security_policy.push_back(
+ std::move(csp));
+
+ PolicyContainerPolicies policies =
+ WorkerScriptFetcher::ComputePolicyContainerPoliciesForTesting(
+ GURL("https://example.com/worker.js"), response_head.get(),
+ /*creator_policies=*/nullptr);
+ EXPECT_EQ(network::mojom::WebSandboxFlags::kOrigin,
+ policies.sandbox_flags & network::mojom::WebSandboxFlags::kOrigin);
+}
+
+TEST(WorkerScriptFetcherTest, ComputePolicyContainerPolicies_LocalScheme) {
+ auto response_head = network::mojom::URLResponseHead::New();
+ response_head->parsed_headers = network::mojom::ParsedHeaders::New();
+
+ PolicyContainerPolicies creator_policies;
+ creator_policies.sandbox_flags = network::mojom::WebSandboxFlags::kOrigin;
+
+ PolicyContainerPolicies policies =
+ WorkerScriptFetcher::ComputePolicyContainerPoliciesForTesting(
+ GURL("blob:https://example.com/uuid"), response_head.get(),
+ &creator_policies);
... (truncated)
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.
References
On This Page