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 <[email protected]> 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 <[email protected]> Reviewed-by: Yoshisato Yanagisawa <[email protected]> Commit-Queue: Takashi Nakayama <[email protected]> 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