High firefox Sandbox Escape 🔧 Commit mapped

Overview

High
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
Impacthigh
DescriptionA process isolation vulnerability in Firefox stemmed from improper handling of javascript: URIs, which could allow content to execute in the top-level document's process instead of the intended frame, potentially enabling a sandbox escape.
ComponentCore
Bug ClassSandbox Escape
Tracker1958350
Fix commit0e460c11eca8 (firefox) +34/-4
CISA KEVNot listed
CreditedNika Layzell
Disclosed2025-04-29

Changed Functions

FunctionChangeNotes
if
browser/components/tabbrowser/content/tabbrowser.js
modified

Files Changed

  • browser/base/content/nsContextMenu.sys.mjs
  • browser/components/tabbrowser/content/tabbrowser.js
diff --git a/browser/base/content/nsContextMenu.sys.mjs b/browser/base/content/nsContextMenu.sys.mjs
index dfcbb381792..3bd0ee8af8b 100644
--- a/browser/base/content/nsContextMenu.sys.mjs
+++ b/browser/base/content/nsContextMenu.sys.mjs
@@ -176,8 +176,6 @@ export class nsContextMenu {
       nsContextMenu.contentData = null;
     }
 
-    this.remoteType = this.actor?.domProcess?.remoteType;
-
     const { gBrowser } = this.window;
 
     this.shouldDisplay = context.shouldDisplay;
@@ -276,6 +274,8 @@ export class nsContextMenu {
         );
     }
 
+    this.remoteType = this.actor.manager.domProcess.remoteType;
+
     this.selectedText = this.selectionInfo.text;
     this.isTextSelected = !!this.selectedText.length;
     this.webExtBrowserType = this.browser.getAttribute(
diff --git a/browser/components/tabbrowser/content/tabbrowser.js b/browser/components/tabbrowser/content/tabbrowser.js
index 3a1d02bca2c..08895d9c30d 100644
--- a/browser/components/tabbrowser/content/tabbrowser.js
+++ b/browser/components/tabbrowser/content/tabbrowser.js
@@ -457,6 +457,21 @@
         openWindowInfo = window.arguments[11];
       }
 
+      let extraOptions;
+      if (window.arguments?.[1] instanceof Ci.nsIPropertyBag2) {
+        extraOptions = window.arguments[1];
+      }
+
+      // If our opener provided a remoteType which was responsible for creating
+      // this pop-up window, we'll fall back to using that remote type when no
+      // other remote type is available.
+      let triggeringRemoteType;
+      if (extraOptions?.hasKey("triggeringRemoteType")) {
+        triggeringRemoteType = extraOptions.getPropertyAsACString(
+          "triggeringRemoteType"
+        );
+      }
+
       let tabArgument = gBrowserInit.getTabToAdopt();
 
       // If we have a tab argument with browser, we use its remoteType. Otherwise,
@@ -484,7 +499,7 @@
       } else if (openWindowInfo) {
         userContextId = openWindowInfo.originAttributes.userContextId;
         if (openWindowInfo.isRemote) {
-          remoteType = E10SUtils.DEFAULT_REMOTE_TYPE;
+          remoteType = triggeringRemoteType ?? E10SUtils.DEFAULT_REMOTE_TYPE;
         } else {
           remoteType = E10SUtils.NOT_REMOTE;
         }
@@ -503,7 +518,7 @@
             uriToLoad,
             gMultiProcessBrowser,
             gFissionBrowser,
-            E10SUtils.DEFAULT_REMOTE_TYPE,
+            triggeringRemoteType ?? E10SUtils.DEFAULT_REMOTE_TYPE,
             null,
             oa
           );
@@ -514,6 +529,13 @@
           // would mean that `gBrowser.selectedBrowser` might not always exist,
           // which is the current assumption.
 
+          if (Cu.isInAutomation) {
+            ChromeUtils.releaseAssert(
+              !triggeringRemoteType,
+              "Unexpected triggeringRemoteType with no uriToLoad"
+            );
+          }
+
           // In this case we default to the privileged about process as that's
           // the best guess we can make, and we'll likely need it eventually.
           remoteType = E10SUtils.PRIVILEGEDABOUT_REMOTE_TYPE;
@@ -2756,6 +2778,7 @@
           initialBrowsingContextGroupId,
           openWindowInfo,
           skipLoad,
+          triggeringRemoteType,
         }));
 
         if (focusUrlBar) {
@@ -3318,8 +3341,15 @@
         initialBrowsingContextGroupId,
         openWindowInfo,
         skipLoad,
+        triggeringRemoteType,
       }
     ) {
+      // If we don't have a preferred remote type (or it is `NOT_REMOTE`), and
+      // we have a remote triggering remote type, use that instead.
+      if (!preferredRemoteType && triggeringRemoteType) {
+        preferredRemoteType = triggeringRemoteType;
+      }
+
       // If we don't have a preferred remote type, and we have a remote
       // opener, use the opener's remote type.
       if (!preferredRemoteType && openerBrowser) {
Loading diff…