High firefox Sandbox Escape 🔧 Commit mapped

Overview

High
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
Impacthigh
DescriptionSandbox escape in Firefox and Firefox Focus for Android
ComponentCore
Bug ClassSandbox Escape
Tracker2003171
Fix commit80b6d771b4a1 (firefox) +171/-175
CISA KEVNot listed
CreditedDaisuke Hatakeyama and Shota Matsuda
Disclosed2026-05-19

Changed Functions

FunctionChangeNotes
getMediaPermission
mobile/shared/actors/GeckoViewPermissionChild.sys.mjs
modified
addCameraPermission
mobile/shared/actors/GeckoViewPermissionChild.sys.mjs
modified
getAppPermissions
mobile/shared/actors/GeckoViewPermissionChild.sys.mjs
modified
mediaRecordingStatusChanged
mobile/shared/actors/GeckoViewPermissionChild.sys.mjs
modified
checkIfGrantedByExtensionPermissions
mobile/shared/actors/GeckoViewPermissionChild.sys.mjs
modified
if
mobile/shared/actors/GeckoViewPermissionChild.sys.mjs
modified
switch
mobile/shared/actors/GeckoViewPermissionParent.sys.mjs
modified

Files Changed

  • mobile/shared/actors/GeckoViewPermissionChild.sys.mjs
  • mobile/shared/actors/GeckoViewPermissionParent.sys.mjs
  • mobile/shared/components/geckoview/GeckoViewPermission.sys.mjs
diff --git a/mobile/shared/actors/GeckoViewPermissionChild.sys.mjs b/mobile/shared/actors/GeckoViewPermissionChild.sys.mjs
index d454250bb56..6ca281f084f 100644
--- a/mobile/shared/actors/GeckoViewPermissionChild.sys.mjs
+++ b/mobile/shared/actors/GeckoViewPermissionChild.sys.mjs
@@ -4,20 +4,6 @@
 
 import { GeckoViewActorChild } from "resource://gre/modules/GeckoViewActorChild.sys.mjs";
 
-const lazy = {};
-
-ChromeUtils.defineESModuleGetters(lazy, {
-  E10SUtils: "resource://gre/modules/E10SUtils.sys.mjs",
-});
-
-const PERM_ACCESS_FINE_LOCATION = "android.permission.ACCESS_FINE_LOCATION";
-
-const MAPPED_TO_EXTENSION_PERMISSIONS = [
-  "persistent-storage",
-  // TODO(Bug 1336194): support geolocation manifest permission
-  // (see https://bugzilla.mozilla.org/show_bug.cgi?id=1336194#c17)l
-];
-
 export class GeckoViewPermissionChild extends GeckoViewActorChild {
   getMediaPermission(aPermission) {
     return this.sendQuery("GeckoView:MediaPermission", {
@@ -26,11 +12,7 @@ export class GeckoViewPermissionChild extends GeckoViewActorChild {
   }
 
   addCameraPermission() {
-    return this.sendQuery("AddCameraPermission");
-  }
-
-  getAppPermissions(aPermissions) {
-    return this.sendQuery("GetAppPermissions", aPermissions);
+    return this.sendQuery("GeckoView:AddCameraPermission");
   }
 
   mediaRecordingStatusChanged(aDevices) {
@@ -38,146 +20,6 @@ export class GeckoViewPermissionChild extends GeckoViewActorChild {
       devices: aDevices,
     });
   }
-
-  // Some WebAPI permissions can be requested and granted to extensions through the
-  // the extension manifest.json, which the user have been already prompted for
-  // (e.g. at install time for the one listed in the manifest.json permissions property,
-  // or at runtime through the optional_permissions property and the permissions.request
-  // WebExtensions API method).
-  //
-  // WebAPI permission that are expected to be mapped to extensions permissions are listed
-  // in the MAPPED_TO_EXTENSION_PERMISSIONS array.
-  //
-  // @param {nsIContentPermissionType} perm
-  //        The WebAPI permission being requested
-  // @param {nsIContentPermissionRequest} aRequest
-  //        The nsIContentPermissionRequest as received by the promptPermission method.
-  //
-  // @returns {null | { allow: boolean, permission: Object }
-  //          Returns null if the request was not handled and should continue with the
-  //          regular permission prompting flow, otherwise it returns an object to
-  //          allow or disallow the permission request right away.
-  checkIfGrantedByExtensionPermissions(perm, aRequest) {
-    if (!aRequest.principal.addonPolicy) {
-      // Not an extension, continue with the regular permission prompting flow.
-      return null;
-    }
-
-    // Return earlier and continue with the regular permission prompting flow if the
-    // the permission is no one that can be requested from the extension manifest file.
-    if (!MAPPED_TO_EXTENSION_PERMISSIONS.includes(perm.type)) {
-      return null;
-    }
-
-    // Disallow if the extension is not active anymore.
-    if (!aRequest.principal.addonPolicy.active) {
-      return { allow: false };
-    }
-
-    // Check if the permission have been already granted to the extension, if it is allow it right away.
-    const isGranted =
-      Services.perms.testPermissionFromPrincipal(
-        aRequest.principal,
-        perm.type
-      ) === Services.perms.ALLOW_ACTION;
-    if (isGranted) {
-      return {
-        allow: true,
-        permission: { [perm.type]: Services.perms.ALLOW_ACTION },
-      };
-    }
-
-    // continue with the regular permission prompting flow otherwise.
-    return null;
-  }
-
-  async promptPermission(aRequest) {
-    // Only allow exactly one permission request here.
-    const types = aRequest.types.QueryInterface(Ci.nsIArray);
-    if (types.length !== 1) {
-      return { allow: false };
-    }
-
-    const perm = types.queryElementAt(0, Ci.nsIContentPermissionType);
-
-    // Check if the request principal is an extension principal and if the permission requested
-    // should be already granted based on the extension permissions (or disallowed right away
-    // because the extension is not enabled anymore.
-    const extensionResult = this.checkIfGrantedByExtensionPermissions(
-      perm,
-      aRequest
-    );
-    if (extensionResult) {
-      return extensionResult;
-    }
-
-    if (
-      perm.type === "desktop-notification" &&
-      !aRequest.hasValidTransientUserGestureActivation &&
-      Services.prefs.getBoolPref(
-        "dom.webnotifications.requireuserinteraction",
-        true
-      )
-    ) {
-      // We need user interaction and don't have it.
-      return { allow: false };
-    }
-
-    const principal =
-      perm.type === "storage-access"
-        ? aRequest.principal
-        : aRequest.topLevelPrincipal;
-
-    let allowOrDeny;
-    try {
-      allowOrDeny = await this.sendQuery("GeckoView:ContentPermission", {
-        uri: principal.URI.displaySpec,
-        thirdPartyOrigin: aRequest.principal.origin,
-        principal: lazy.E10SUtils.serializePrincipal(principal),
-        perm: perm.type,
-        value: perm.capability,
-        contextId: principal.originAttributes.geckoViewSessionContextId ?? null,
-        privateMode: principal.privateBrowsingId != 0,
-      });
-
-      if (allowOrDeny === Services.perms.ALLOW_ACTION) {
-        // Ask for app permission after asking for content permission.
-        if (perm.type === "geolocation") {
-          const granted = await this.getAppPermissions([
-            PERM_ACCESS_FINE_LOCATION,
-          ]);
-          allowOrDeny = granted
-            ? Services.perms.ALLOW_ACTION
-            : Services.perms.DENY_ACTION;
-        }
-      }
-    } catch (error) {
-      console.error("Permission error:", error);
-      allowOrDeny = Services.perms.DENY_ACTION;
-    }
-
-    // Manually release the target request here to facilitate garbage collection.
-    aRequest = undefined;
-
-    const allow = allowOrDeny === Services.perms.ALLOW_ACTION;
-
-    // The storage access code adds itself to the perm manager; no need for us to do it.
-    if (perm.type === "storage-access") {
-      if (allow) {
-        return { allow, permission: { "storage-access": "allow" } };
-      }
-      return { allow };
-    }
-
-    Services.perms.addFromPrincipal(
-      principal,
-      perm.type,
-      allowOrDeny,
-      Services.perms.EXPIRE_NEVER
-    );
-
-    return { allow };
-  }
 }
 
 const { debug, warn } = GeckoViewPermissionChild.initLogging(
diff --git a/mobile/shared/actors/GeckoViewPermissionParent.sys.mjs b/mobile/shared/actors/GeckoViewPermissionParent.sys.mjs
index 3735721bd17..efd6d1e8dd9 100644
--- a/mobile/shared/actors/GeckoViewPermissionParent.sys.mjs
+++ b/mobile/shared/actors/GeckoViewPermissionParent.sys.mjs
@@ -50,10 +50,7 @@ export class GeckoViewPermissionParent extends GeckoViewActorParent {
     debug`receiveMessage ${aMessage.name}`;
 
     switch (aMessage.name) {
-      case "GetAppPermissions": {
-        return this.getAppPermissions(aMessage.data);
-      }
-      case "AddCameraPermission": {
+      case "GeckoView:AddCameraPermission": {
         return this.addCameraPermission();
       }
Loading diff…