Moderate firefox Logic Error 🔧 Commit mapped

Overview

Moderate
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Impactmoderate
DescriptionPrivilege escalation in the WebExtensions component
ComponentNetworking
Bug ClassLogic Error
Tracker2049069
Fix commit4d5fb902acb3 (firefox) +31/-37
CISA KEVNot listed
CreditedTomoya Nakanishi
Disclosed2026-10-13

Changed Functions

FunctionChangeNotes
if
netwerk/protocol/viewsource/nsViewSourceHandler.cpp
modified
if
browser/components/extensions/test/browser/browser_ext_tabs_create_invalid_url.js
modified
add_task
browser/components/extensions/test/browser/browser_ext_tabs_create_invalid_url.js
modified
for
browser/components/extensions/test/browser/browser_ext_tabs_create_invalid_url.js
modified

Files Changed

  • browser/components/extensions/test/browser/browser_ext_tabs_create_invalid_url.js
  • netwerk/protocol/viewsource/nsViewSourceHandler.cpp
diff --git a/netwerk/protocol/viewsource/nsViewSourceHandler.cpp b/netwerk/protocol/viewsource/nsViewSourceHandler.cpp
index 1c5aca9a950..f97163115ae 100644
--- a/netwerk/protocol/viewsource/nsViewSourceHandler.cpp
+++ b/netwerk/protocol/viewsource/nsViewSourceHandler.cpp
@@ -39,11 +39,13 @@ nsViewSourceHandler::GetFlagsForURI(nsIURI* aURI, uint32_t* result) {
   }
 
   nsCOMPtr<nsIURI> innerURI;
-  nestedURI->GetInnerURI(getter_AddRefs(innerURI));
+  nsresult rv = nestedURI->GetInnermostURI(getter_AddRefs(innerURI));
+  if (NS_FAILED(rv)) {
+    return NS_OK;
+  }
   nsCOMPtr<nsINetUtil> netUtil = do_GetNetUtil();
   bool isLoadable = false;
-  nsresult rv =
-      netUtil->ProtocolHasFlags(innerURI, URI_LOADABLE_BY_ANYONE, &isLoadable);
+  rv = netUtil->ProtocolHasFlags(innerURI, URI_LOADABLE_BY_ANYONE, &isLoadable);
   NS_ENSURE_SUCCESS(rv, rv);
   if (isLoadable) {
     *result |= URI_LOADABLE_BY_EXTENSIONS;
diff --git a/browser/components/extensions/test/browser/browser_ext_tabs_create_invalid_url.js b/browser/components/extensions/test/browser/browser_ext_tabs_create_invalid_url.js
index c29ea03886e..26364fb12c3 100644
--- a/browser/components/extensions/test/browser/browser_ext_tabs_create_invalid_url.js
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_create_invalid_url.js
@@ -4,50 +4,37 @@ const FILE_URL = Services.io.newFileURI(
   new FileUtils.File(getTestFilePath("file_dummy.html"))
 ).spec;
 
-async function testTabsCreateInvalidURL(tabsCreateURL) {
-  let extension = ExtensionTestUtils.loadExtension({
+function loadTestExtension() {
+  return ExtensionTestUtils.loadExtension({
     manifest: {
       permissions: ["tabs"],
     },
 
     background: function () {
-      browser.test.sendMessage("ready");
-      browser.test.onMessage.addListener((msg, tabsCreateURL) => {
-        browser.tabs.create({ url: tabsCreateURL }, tab => {
-          browser.test.assertEq(
-            undefined,
-            tab,
-            "on error tab should be undefined"
-          );
-          browser.test.assertTrue(
-            /Illegal URL/.test(browser.runtime.lastError.message),
-            "runtime.lastError should report the expected error message"
-          );
-
+      async function testInvalidUrl(tabsCreateURL) {
+        let promise = browser.tabs.create({ url: tabsCreateURL });
+        promise.then(tab => {
           // Remove the opened tab is any.
-          if (tab) {
-            browser.tabs.remove(tab.id);
-          }
-          browser.test.sendMessage("done");
+          browser.tabs.remove(tab);
         });
+        await browser.test.assertRejects(
+          promise,
+          /Illegal URL/,
+          `Should get rejection for tabs.create with: ${tabsCreateURL}`
+        );
+        browser.test.sendMessage("created");
+      }
+      browser.test.onMessage.addListener((msg, tabsCreateURL) => {
+        browser.test.assertEq(msg, "create", `tabs.create: ${tabsCreateURL}`);
+        testInvalidUrl(tabsCreateURL);
       });
     },
   });
-
-  await extension.startup();
-
-  await extension.awaitMessage("ready");
-
-  info(`test tab.create on invalid URL "${tabsCreateURL}"`);
-
-  extension.sendMessage("start", tabsCreateURL);
-  await extension.awaitMessage("done");
-
-  await extension.unload();
 }
 
-add_task(async function () {
-  info("Start testing tabs.create on invalid URLs");
+add_task(async function test_invalid_url_in_tabs_create() {
+  let extension = loadTestExtension();
+  await extension.startup();
 
   let dataURLPage = `data:text/html,
     <!DOCTYPE html>
@@ -67,11 +54,16 @@ add_task(async function () {
     },
     { tabsCreateURL: dataURLPage },
     { tabsCreateURL: FILE_URL },
+    { tabsCreateURL: `view-source:${FILE_URL}` },
+    // FILE_URL is not a real jar archive, but for validation we only look at
+    // the URL structure, so it doesn't matter.
+    { tabsCreateURL: `view-source:jar:${FILE_URL}!/inside.txt` },
   ];
 
   for (let { tabsCreateURL } of testCases) {
-    await testTabsCreateInvalidURL(tabsCreateURL);
+    extension.sendMessage("create", tabsCreateURL);
+    await extension.awaitMessage("created");
   }
 
-  info("done");
+  await extension.unload();
 });
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/browser/components/extensions/test/browser/browser_ext_tabs_create_invalid_url.js b/browser/components/extensions/test/browser/browser_ext_tabs_create_invalid_url.js
index c29ea03886e..26364fb12c3 100644
--- a/browser/components/extensions/test/browser/browser_ext_tabs_create_invalid_url.js
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_create_invalid_url.js
@@ -4,50 +4,37 @@ const FILE_URL = Services.io.newFileURI(
   new FileUtils.File(getTestFilePath("file_dummy.html"))
 ).spec;
 
-async function testTabsCreateInvalidURL(tabsCreateURL) {
-  let extension = ExtensionTestUtils.loadExtension({
+function loadTestExtension() {
+  return ExtensionTestUtils.loadExtension({
     manifest: {
       permissions: ["tabs"],
     },
 
     background: function () {
-      browser.test.sendMessage("ready");
-      browser.test.onMessage.addListener((msg, tabsCreateURL) => {
-        browser.tabs.create({ url: tabsCreateURL }, tab => {
-          browser.test.assertEq(
-            undefined,
-            tab,
-            "on error tab should be undefined"
-          );
-          browser.test.assertTrue(
-            /Illegal URL/.test(browser.runtime.lastError.message),
-            "runtime.lastError should report the expected error message"
-          );
-
+      async function testInvalidUrl(tabsCreateURL) {
+        let promise = browser.tabs.create({ url: tabsCreateURL });
+        promise.then(tab => {
           // Remove the opened tab is any.
-          if (tab) {
-            browser.tabs.remove(tab.id);
-          }
-          browser.test.sendMessage("done");
+          browser.tabs.remove(tab);
         });
+        await browser.test.assertRejects(
+          promise,
+          /Illegal URL/,
+          `Should get rejection for tabs.create with: ${tabsCreateURL}`
+        );
+        browser.test.sendMessage("created");
+      }
+      browser.test.onMessage.addListener((msg, tabsCreateURL) => {
+        browser.test.assertEq(msg, "create", `tabs.create: ${tabsCreateURL}`);
+        testInvalidUrl(tabsCreateURL);
       });
     },
   });
-
-  await extension.startup();
-
-  await extension.awaitMessage("ready");
-
-  info(`test tab.create on invalid URL "${tabsCreateURL}"`);
-
-  extension.sendMessage("start", tabsCreateURL);
-  await extension.awaitMessage("done");
-
-  await extension.unload();
 }
 
-add_task(async function () {
-  info("Start testing tabs.create on invalid URLs");
+add_task(async function test_invalid_url_in_tabs_create() {
+  let extension = loadTestExtension();
+  await extension.startup();
 
   let dataURLPage = `data:text/html,
     <!DOCTYPE html>
@@ -67,11 +54,16 @@ add_task(async function () {
     },
     { tabsCreateURL: dataURLPage },
     { tabsCreateURL: FILE_URL },
+    { tabsCreateURL: `view-source:${FILE_URL}` },
+    // FILE_URL is not a real jar archive, but for validation we only look at
+    // the URL structure, so it doesn't matter.
+    { tabsCreateURL: `view-source:jar:${FILE_URL}!/inside.txt` },
   ];
 
   for (let { tabsCreateURL } of testCases) {
-    await testTabsCreateInvalidURL(tabsCreateURL);
+    extension.sendMessage("create", tabsCreateURL);
+    await extension.awaitMessage("created");
   }
 
-  info("done");
+  await extension.unload();
 });
Loading diff…