Low firefox Logic Error 🔧 Commit mapped

Overview

Low
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
Impactlow
DescriptionMitigation bypass in the DOM: Service Workers component
ComponentDOM
Bug ClassLogic Error
Tracker2044063
Fix commit4f7060aa7884 (firefox) +40/-2
CISA KEVNot listed
Creditedbug2own
Disclosed2026-07-21

Changed Functions

FunctionChangeNotes
for
testing/web-platform/tests/service-workers/service-worker/resources/registration-tests-mime-types.js
modified
promise_test
testing/web-platform/tests/service-workers/service-worker/resources/registration-tests-mime-types.js
modified

Files Changed

  • dom/serviceworkers/ServiceWorkerScriptCache.cpp
  • dom/workers/loader/NetworkLoadHandler.cpp
  • testing/web-platform/tests/service-workers/service-worker/resources/import-scripts-mime-types-worker.js
  • testing/web-platform/tests/service-workers/service-worker/resources/registration-tests-mime-types.js
diff --git a/dom/serviceworkers/ServiceWorkerScriptCache.cpp b/dom/serviceworkers/ServiceWorkerScriptCache.cpp
index 6a1211f96de..2e80b226620 100644
--- a/dom/serviceworkers/ServiceWorkerScriptCache.cpp
+++ b/dom/serviceworkers/ServiceWorkerScriptCache.cpp
@@ -1087,9 +1087,12 @@ CompareNetwork::OnStreamComplete(nsIStreamLoader* aLoader,
     }
 
     auto mimeTypeUTF16 = NS_ConvertUTF8toUTF16(mimeType);
+    // The top-level service worker script must be served with a JavaScript
+    // MIME type. JSON is only permitted for non-top-level (imported) modules,
+    // such as `import data from "./x.json" with { type: "json" }`.
     if (mimeTypeUTF16.IsEmpty() ||
         !(nsContentUtils::IsJavascriptMIMEType(mimeTypeUTF16) ||
-          nsContentUtils::IsJsonMimeType(mimeTypeUTF16))) {
+          (!mIsMainScript && nsContentUtils::IsJsonMimeType(mimeTypeUTF16)))) {
       ServiceWorkerManager::LocalizeAndReportToAllClients(
           mRegistration->Scope(), "ServiceWorkerRegisterMimeTypeError2",
           nsTArray<nsString>{NS_ConvertUTF8toUTF16(mRegistration->Scope()),
diff --git a/dom/workers/loader/NetworkLoadHandler.cpp b/dom/workers/loader/NetworkLoadHandler.cpp
index 75a46add9dd..7d384eb01d9 100644
--- a/dom/workers/loader/NetworkLoadHandler.cpp
+++ b/dom/workers/loader/NetworkLoadHandler.cpp
@@ -353,8 +353,12 @@ nsresult NetworkLoadHandler::PrepareForRequest(nsIRequest* aRequest) {
 
     auto mimeTypeUTF16 = NS_ConvertUTF8toUTF16(mimeType);
     if (!nsContentUtils::IsJavascriptMIMEType(mimeTypeUTF16)) {
-      // JSON is allowed as a non-toplevel.
+      // JSON is only allowed for non-toplevel JSON module imports, not for
+      // classic importScripts() or the top-level worker script.
       if (!((!loadContext->IsTopLevel() &&
+             loadContext->mRequest->IsModuleRequest() &&
+             loadContext->mRequest->AsModuleRequest()->mModuleType ==
+                 JS::ModuleType::JSON &&
              nsContentUtils::IsJsonMimeType(mimeTypeUTF16))
 #ifdef NIGHTLY_BUILD
             // Allow wasm modules.
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/import-scripts-mime-types-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/import-scripts-mime-types-worker.js
index d4f1f3e26d8..7658eeace69 100644
--- a/testing/web-platform/tests/service-workers/service-worker/resources/import-scripts-mime-types-worker.js
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/import-scripts-mime-types-worker.js
@@ -1,6 +1,11 @@
 const badMimeTypes = [
   null,  // no MIME type
   'text/plain',
+  // JSON is only valid for JSON module imports, never for classic
+  // importScripts(), even when the body is valid JavaScript.
+  'application/json',
+  'text/json',
+  'application/manifest+json',
 ];
 
 const validMimeTypes = [
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/registration-tests-mime-types.js b/testing/web-platform/tests/service-workers/service-worker/resources/registration-tests-mime-types.js
index 037e6c0fde2..7a64cd91e41 100644
--- a/testing/web-platform/tests/service-workers/service-worker/resources/registration-tests-mime-types.js
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/registration-tests-mime-types.js
@@ -21,6 +21,32 @@ function registration_tests_mime_types(register_method) {
           'Registration of plain text script should fail.');
     }, 'Registering script with bad MIME type');
 
+  // The top-level service worker script must have a JavaScript MIME type. A
+  // JSON MIME type is only valid for imported (non-top-level) modules, so
+  // registering a top-level script served as JSON must fail even when the body
+  // happens to be valid JavaScript.
+  const jsonMimeTypes = [
+    'application/json',
+    'text/json',
+    'application/manifest+json',
+  ];
+
+  for (const jsonMimeType of jsonMimeTypes) {
+    promise_test(function(t) {
+        // Encode the MIME type so characters such as '+' survive the query
+        // string instead of being decoded to a space by the server.
+        var script =
+            `resources/mime-type-worker.py?mime=${encodeURIComponent(jsonMimeType)}`;
+        // Use a scope unique to each MIME type so the registrations don't
+        // interfere with one another.
+        var scope = `resources/scope/json-mime-type-worker/${jsonMimeType}`;
+        return promise_rejects_dom(t,
+            'SecurityError',
+            register_method(script, {scope: scope}),
+            'Registration of JSON MIME type script should fail.');
+      }, `Registering script with JSON MIME type ${jsonMimeType}`);
+  }
+
   /**
    * ServiceWorkerContainer.register() should throw a TypeError, according to
    * step 17.1 of https://w3c.github.io/ServiceWorker/#importscripts
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/import-scripts-mime-types-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/import-scripts-mime-types-worker.js
index d4f1f3e26d8..7658eeace69 100644
--- a/testing/web-platform/tests/service-workers/service-worker/resources/import-scripts-mime-types-worker.js
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/import-scripts-mime-types-worker.js
@@ -1,6 +1,11 @@
 const badMimeTypes = [
   null,  // no MIME type
   'text/plain',
+  // JSON is only valid for JSON module imports, never for classic
+  // importScripts(), even when the body is valid JavaScript.
+  'application/json',
+  'text/json',
+  'application/manifest+json',
 ];
 
 const validMimeTypes = [
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/registration-tests-mime-types.js b/testing/web-platform/tests/service-workers/service-worker/resources/registration-tests-mime-types.js
index 037e6c0fde2..7a64cd91e41 100644
--- a/testing/web-platform/tests/service-workers/service-worker/resources/registration-tests-mime-types.js
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/registration-tests-mime-types.js
@@ -21,6 +21,32 @@ function registration_tests_mime_types(register_method) {
           'Registration of plain text script should fail.');
     }, 'Registering script with bad MIME type');
 
+  // The top-level service worker script must have a JavaScript MIME type. A
+  // JSON MIME type is only valid for imported (non-top-level) modules, so
+  // registering a top-level script served as JSON must fail even when the body
+  // happens to be valid JavaScript.
+  const jsonMimeTypes = [
+    'application/json',
+    'text/json',
+    'application/manifest+json',
+  ];
+
+  for (const jsonMimeType of jsonMimeTypes) {
+    promise_test(function(t) {
+        // Encode the MIME type so characters such as '+' survive the query
+        // string instead of being decoded to a space by the server.
+        var script =
+            `resources/mime-type-worker.py?mime=${encodeURIComponent(jsonMimeType)}`;
+        // Use a scope unique to each MIME type so the registrations don't
+        // interfere with one another.
+        var scope = `resources/scope/json-mime-type-worker/${jsonMimeType}`;
+        return promise_rejects_dom(t,
+            'SecurityError',
+            register_method(script, {scope: scope}),
+            'Registration of JSON MIME type script should fail.');
+      }, `Registering script with JSON MIME type ${jsonMimeType}`);
+  }
+
   /**
    * ServiceWorkerContainer.register() should throw a TypeError, according to
    * step 17.1 of https://w3c.github.io/ServiceWorker/#importscripts
Loading diff…