Low firefox Logic Error 🔧 Commit mapped

Overview

Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Impactlow
DescriptionPrivilege escalation in the WebExtensions component
ComponentToolkit
Bug ClassLogic Error
Tracker2070547
Fix commitded1ba42e21c (firefox) +53/-2
CISA KEVNot listed
CreditedMozilla
Disclosed2026-10-13

Changed Functions

FunctionChangeNotes
localizeCallback
toolkit/components/extensions/test/xpcshell/test_WebExtensionContentScript.js
modified
add_task
toolkit/components/extensions/test/xpcshell/test_WebExtensionContentScript.js
modified

Files Changed

  • toolkit/components/extensions/WebExtensionPolicy.cpp
  • toolkit/components/extensions/test/xpcshell/test_WebExtensionContentScript.js
diff --git a/toolkit/components/extensions/WebExtensionPolicy.cpp b/toolkit/components/extensions/WebExtensionPolicy.cpp
index 5860d509d94..cf1a0af778a 100644
--- a/toolkit/components/extensions/WebExtensionPolicy.cpp
+++ b/toolkit/components/extensions/WebExtensionPolicy.cpp
@@ -984,12 +984,12 @@ bool MozDocumentMatcher::Matches(const DocInfo& aDoc,
     // In MV3, activeTab only allows access to same-origin iframes.
     if (mHasActiveTabPermission && aDoc.IsSameOriginWithTop() &&
         MatchPattern::MatchesAllURLs(urlinfo)) {
-      return true;
+      return mExtension->CheckGuarded(urlinfo).IsNull();
     }
   } else {
     if (mHasActiveTabPermission && aDoc.ShouldMatchActiveTabPermission() &&
         MatchPattern::MatchesAllURLs(urlinfo)) {
-      return true;
+      return !mExtension || mExtension->CheckGuarded(urlinfo).IsNull();
     }
   }
 
diff --git a/toolkit/components/extensions/test/xpcshell/test_WebExtensionContentScript.js b/toolkit/components/extensions/test/xpcshell/test_WebExtensionContentScript.js
index 5b1a8b30f94..24a841a7dd2 100644
--- a/toolkit/components/extensions/test/xpcshell/test_WebExtensionContentScript.js
+++ b/toolkit/components/extensions/test/xpcshell/test_WebExtensionContentScript.js
@@ -508,3 +508,54 @@ add_task(function test_WebExtensionContentScript_frames_mv3_specific() {
     expectMatches: true,
   });
 });
+
+async function test_activeTab_guarded(manifestVersion) {
+  let contentPage = await ExtensionTestUtils.loadContentPage(
+    "http://example.com/data/file_toplevel.html"
+  );
+
+  await contentPage.spawn([manifestVersion], manifestVersion => {
+    const { ExtensionGuardSet, WebExtensionContentScript, WebExtensionPolicy } =
+      Cu.getGlobalForObject(Services);
+    let policy = new WebExtensionPolicy({
+      id: "foo@bar.baz",
+      mozExtensionHostname: "88fb51cd-159f-4859-83db-7065485bc9b2",
+      baseURL: "file:///foo",
+
+      manifestVersion,
+      allowedOrigins: [],
+      localizeCallback() {},
+    });
+    let script = new WebExtensionContentScript(policy, {
+      matches: [],
+      hasActiveTabPermission: true,
+    });
+    let wgc = this.content.windowGlobalChild;
+
+    Assert.ok(
+      script.matchesWindowGlobal(wgc),
+      "activeTab matches the top-level document without guards"
+    );
+
+    policy.guardSets = [
+      new ExtensionGuardSet({
+        deny: ["http://example.com/*"],
+        source: "enterprise-global",
+      }),
+    ];
+    Assert.ok(
+      !script.matchesWindowGlobal(wgc),
+      "activeTab does not match a guarded host"
+    );
+  });
+
+  await contentPage.close();
+}
+
+add_task(function test_WebExtensionContentScript_activeTab_guarded_mv2() {
+  return test_activeTab_guarded(2);
+});
+
+add_task(function test_WebExtensionContentScript_activeTab_guarded_mv3() {
+  return test_activeTab_guarded(3);
+});
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/toolkit/components/extensions/test/xpcshell/test_WebExtensionContentScript.js b/toolkit/components/extensions/test/xpcshell/test_WebExtensionContentScript.js
index 5b1a8b30f94..24a841a7dd2 100644
--- a/toolkit/components/extensions/test/xpcshell/test_WebExtensionContentScript.js
+++ b/toolkit/components/extensions/test/xpcshell/test_WebExtensionContentScript.js
@@ -508,3 +508,54 @@ add_task(function test_WebExtensionContentScript_frames_mv3_specific() {
     expectMatches: true,
   });
 });
+
+async function test_activeTab_guarded(manifestVersion) {
+  let contentPage = await ExtensionTestUtils.loadContentPage(
+    "http://example.com/data/file_toplevel.html"
+  );
+
+  await contentPage.spawn([manifestVersion], manifestVersion => {
+    const { ExtensionGuardSet, WebExtensionContentScript, WebExtensionPolicy } =
+      Cu.getGlobalForObject(Services);
+    let policy = new WebExtensionPolicy({
+      id: "foo@bar.baz",
+      mozExtensionHostname: "88fb51cd-159f-4859-83db-7065485bc9b2",
+      baseURL: "file:///foo",
+
+      manifestVersion,
+      allowedOrigins: [],
+      localizeCallback() {},
+    });
+    let script = new WebExtensionContentScript(policy, {
+      matches: [],
+      hasActiveTabPermission: true,
+    });
+    let wgc = this.content.windowGlobalChild;
+
+    Assert.ok(
+      script.matchesWindowGlobal(wgc),
+      "activeTab matches the top-level document without guards"
+    );
+
+    policy.guardSets = [
+      new ExtensionGuardSet({
+        deny: ["http://example.com/*"],
+        source: "enterprise-global",
+      }),
+    ];
+    Assert.ok(
+      !script.matchesWindowGlobal(wgc),
+      "activeTab does not match a guarded host"
+    );
+  });
+
+  await contentPage.close();
+}
+
+add_task(function test_WebExtensionContentScript_activeTab_guarded_mv2() {
+  return test_activeTab_guarded(2);
+});
+
+add_task(function test_WebExtensionContentScript_activeTab_guarded_mv3() {
+  return test_activeTab_guarded(3);
+});
Loading diff…