Low firefox Logic Error 🔧 Commit mapped

Overview

Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Impactlow
DescriptionSame-origin policy bypass in the DevTools component
ComponentCore
Bug ClassLogic Error
Tracker2066063
Fix commitd27eb6ae9969 (firefox) +41/-2
CISA KEVNot listed
CreditedFinn Westendorf
Disclosed2026-10-13

Files Changed

  • devtools/shared/network-observer/NetworkLocalMode.sys.mjs
  • devtools/shared/network-observer/test/browser/browser_networkobserver_localMode.js
diff --git a/devtools/shared/network-observer/NetworkLocalMode.sys.mjs b/devtools/shared/network-observer/NetworkLocalMode.sys.mjs
index 7d607a4f47d..5b7baad8af0 100644
--- a/devtools/shared/network-observer/NetworkLocalMode.sys.mjs
+++ b/devtools/shared/network-observer/NetworkLocalMode.sys.mjs
@@ -39,8 +39,21 @@ export const NetworkLocalMode = {
       systemPath = systemPath.replace(/\//g, "\\");
     }
 
-    const overridePath = PathUtils.joinRelative(localFolderPath, systemPath);
-    let file = new lazy.FileUtils.File(overridePath);
+    let file;
+    try {
+      const overridePath = PathUtils.joinRelative(localFolderPath, systemPath);
+      file = new lazy.FileUtils.File(overridePath);
+    } catch (e) {
+      console.error(
+        "Exception while processing local mode request path",
+        channel.URI.spec,
+        localFolderPath,
+        systemPath,
+        e
+      );
+      overrideChannelInto404(channel);
+      return;
+    }
 
     if (!file.exists()) {
       // Create a 404 response to avoid leaving any request matching the host
diff --git a/devtools/shared/network-observer/test/browser/browser_networkobserver_localMode.js b/devtools/shared/network-observer/test/browser/browser_networkobserver_localMode.js
index b8ba07bea02..ad29d0c0449 100644
--- a/devtools/shared/network-observer/test/browser/browser_networkobserver_localMode.js
+++ b/devtools/shared/network-observer/test/browser/browser_networkobserver_localMode.js
@@ -13,6 +13,8 @@ const TEST_URL_PORT_9999 = `https://${TEST_ORIGIN}:9999/`;
 const TEST_FOLDER_URL = `${TEST_URL}folder/`;
 const TEST_FOLDER_PAGE_URL = `${TEST_FOLDER_URL}test.html`;
 const TEST_404_URL = `${TEST_URL}404`;
+// It's important that the URL ends up with two `/`:
+const TEST_URL_INVALID_PATH = `https://${TEST_ORIGIN}//`;
 
 const TEST_UNICODE_ORIGIN = "ʂ.com";
 const TEST_UNICODE_URL = `https://${TEST_UNICODE_ORIGIN}/`;
@@ -158,6 +160,30 @@ add_task(async function testLocalMode() {
     }
   );
 
+  info("Assert that URL whose path is invalid are generating a 404");
+  await loadURL(gBrowser.selectedBrowser, TEST_URL_INVALID_PATH);
+  await SpecialPowers.spawn(
+    gBrowser.selectedBrowser,
+    [TEST_URL_INVALID_PATH],
+    async pageUrl => {
+      is(
+        content.browsingContext.docShell.currentDocumentChannel.responseStatus,
+        404,
+        "The page has a 404 HTTP Response code"
+      );
+      is(
+        content.document.querySelector("p").textContent,
+        "No local file for: //",
+        "The content of the HTML is the 404 error page"
+      );
+      is(
+        content.location.href,
+        pageUrl,
+        "The location of the page is the 404 url"
+      );
+    }
+  );
+
   info("Assert that we can also load local mapping via a unicode origin");
   await loadURL(gBrowser.selectedBrowser, TEST_UNICODE_URL);
   await SpecialPowers.spawn(
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/devtools/shared/network-observer/test/browser/browser_networkobserver_localMode.js b/devtools/shared/network-observer/test/browser/browser_networkobserver_localMode.js
index b8ba07bea02..ad29d0c0449 100644
--- a/devtools/shared/network-observer/test/browser/browser_networkobserver_localMode.js
+++ b/devtools/shared/network-observer/test/browser/browser_networkobserver_localMode.js
@@ -13,6 +13,8 @@ const TEST_URL_PORT_9999 = `https://${TEST_ORIGIN}:9999/`;
 const TEST_FOLDER_URL = `${TEST_URL}folder/`;
 const TEST_FOLDER_PAGE_URL = `${TEST_FOLDER_URL}test.html`;
 const TEST_404_URL = `${TEST_URL}404`;
+// It's important that the URL ends up with two `/`:
+const TEST_URL_INVALID_PATH = `https://${TEST_ORIGIN}//`;
 
 const TEST_UNICODE_ORIGIN = "ʂ.com";
 const TEST_UNICODE_URL = `https://${TEST_UNICODE_ORIGIN}/`;
@@ -158,6 +160,30 @@ add_task(async function testLocalMode() {
     }
   );
 
+  info("Assert that URL whose path is invalid are generating a 404");
+  await loadURL(gBrowser.selectedBrowser, TEST_URL_INVALID_PATH);
+  await SpecialPowers.spawn(
+    gBrowser.selectedBrowser,
+    [TEST_URL_INVALID_PATH],
+    async pageUrl => {
+      is(
+        content.browsingContext.docShell.currentDocumentChannel.responseStatus,
+        404,
+        "The page has a 404 HTTP Response code"
+      );
+      is(
+        content.document.querySelector("p").textContent,
+        "No local file for: //",
+        "The content of the HTML is the 404 error page"
+      );
+      is(
+        content.location.href,
+        pageUrl,
+        "The location of the page is the 404 url"
+      );
+    }
+  );
+
   info("Assert that we can also load local mapping via a unicode origin");
   await loadURL(gBrowser.selectedBrowser, TEST_UNICODE_URL);
   await SpecialPowers.spawn(
Loading diff…