Medium firefox Logic Error 🔧 Commit mapped

Overview

Medium
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
Impactmoderate
DescriptionPrivilege escalation in the WebDriver BiDi component
ComponentCore
Bug ClassLogic Error
Tracker2044280
Fix commit941d886f2645 (firefox) +41/-3
CISA KEVNot listed
CreditedTomoya Nakanishi
Disclosed2026-09-01

Changed Functions

FunctionChangeNotes
if
remote/shared/messagehandler/transports/RootTransport.sys.mjs
modified

Files Changed

  • remote/shared/messagehandler/MessageHandler.sys.mjs
  • remote/shared/messagehandler/transports/RootTransport.sys.mjs
  • remote/webdriver-bidi/modules/root/browsingContext.sys.mjs
diff --git a/remote/shared/messagehandler/MessageHandler.sys.mjs b/remote/shared/messagehandler/MessageHandler.sys.mjs
index bb4d0df1b57..02eee37e0d0 100644
--- a/remote/shared/messagehandler/MessageHandler.sys.mjs
+++ b/remote/shared/messagehandler/MessageHandler.sys.mjs
@@ -243,6 +243,12 @@ export class MessageHandler extends EventEmitter {
    *     If not explicitly set, the framework will automatically retry if the
    *     destination is likely to be replaced (e.g. browsingContext on the
    *     initial document or loading a document).
+   * @property {boolean=} skipPrivilegeCheck
+   *     Optional. When true, the command is allowed to be forwarded to a
+   *     privileged browsing context even without system access. Defaults to
+   *     false, which prevents the command from reaching a browsing context
+   *     that became privileged after it was dispatched. Should only be set for
+   *     commands that are safe regardless of the context's privilege level.
    */
 
   /**
diff --git a/remote/shared/messagehandler/transports/RootTransport.sys.mjs b/remote/shared/messagehandler/transports/RootTransport.sys.mjs
index bf8c67a2bb7..52346f4459b 100644
--- a/remote/shared/messagehandler/transports/RootTransport.sys.mjs
+++ b/remote/shared/messagehandler/transports/RootTransport.sys.mjs
@@ -12,9 +12,12 @@ ChromeUtils.defineESModuleGetters(lazy, {
     "chrome://remote/content/shared/BrowsingContextUtils.sys.mjs",
   isInitialDocument:
     "chrome://remote/content/shared/BrowsingContextUtils.sys.mjs",
+  isPrivilegedContext:
+    "chrome://remote/content/shared/BrowsingContextUtils.sys.mjs",
   Log: "chrome://remote/content/shared/Log.sys.mjs",
   MessageHandlerFrameActor:
     "chrome://remote/content/shared/messagehandler/transports/js-window-actors/MessageHandlerFrameActor.sys.mjs",
+  RemoteAgent: "chrome://remote/content/components/RemoteAgent.sys.mjs",
   TabManager: "chrome://remote/content/shared/TabManager.sys.mjs",
   waitForCurrentWindowGlobal:
     "chrome://remote/content/shared/BrowsingContextUtils.sys.mjs",
@@ -149,6 +152,23 @@ export class RootTransport {
       try {
         if (browsingContext.isContent) {
           browsingContext = webProgress.browsingContext;
+
+          // The browsing context is (re-)resolved from the web progress on
+          // every attempt and may have navigated to a privileged page since
+          // the command was initially dispatched. Without system access no
+          // command may run in a privileged context, so refuse to forward it
+          // there instead of executing with elevated privileges. Commands that
+          // are always allowed regardless of privilege (e.g. the base URL
+          // lookup for a navigation) opt out via `skipPrivilegeCheck`.
+          if (
+            !command.skipPrivilegeCheck &&
+            !lazy.RemoteAgent.allowSystemAccess &&
+            lazy.isPrivilegedContext(browsingContext)
+          ) {
+            throw new lazy.error.MessageHandlerError(
+              `Cannot forward command ${name} to a privileged browsing context`
+            );
+          }
         }
 
         if (!browsingContext.currentWindowGlobal) {
diff --git a/remote/webdriver-bidi/modules/root/browsingContext.sys.mjs b/remote/webdriver-bidi/modules/root/browsingContext.sys.mjs
index 6f7a9df1c49..974f2285976 100644
--- a/remote/webdriver-bidi/modules/root/browsingContext.sys.mjs
+++ b/remote/webdriver-bidi/modules/root/browsingContext.sys.mjs
@@ -583,7 +583,7 @@ class BrowsingContextModule extends RootBiDiModule {
     );
 
     const context = this._getNavigable(contextId, {
-      supportsPrivilegedScope: true,
+      skipPrivilegeCheck: true,
     });
     lazy.assert.topLevel(
       context,
@@ -1403,6 +1403,8 @@ class BrowsingContextModule extends RootBiDiModule {
         id: context.id,
       },
       retryOnAbort: true,
+      // Reading the base URL is safe and must work while navigating a privileged page.
+      skipPrivilegeCheck: true,
     });
 
     let targetURI;
@@ -2918,7 +2920,12 @@ class BrowsingContextModule extends RootBiDiModule {
       "_awaitVisibilityState",
       browsingContext.id,
       { value: expectedState, timeout },
-      { retryOnAbort: true }
+      {
+        retryOnAbort: true,
+        // Awaiting the visibility state is safe and can target a context
+        // (e.g. a previously selected tab) regardless of its privilege level.
+        skipPrivilegeCheck: true,
+      }
     );
   }
 
@@ -3030,7 +3037,12 @@ class BrowsingContextModule extends RootBiDiModule {
           height: targetHeight,
           width: targetWidth,
         },
-        { retryOnAbort: true }
+        {
+          retryOnAbort: true,
+          // Awaiting the resized viewport dimensions is safe
+          // regardless of the context's privilege level.
+          skipPrivilegeCheck: true,
+        }
       );
     }
   }
Loading diff…