Bug 1518843 - Use isPrivileged instead of restrictScheme for addons permissions. r=kmag
authorAgi Sferro <agi@mozilla.com>
Tue, 16 Apr 2019 18:04:39 +0000
changeset 469763 48f975c56cc6
parent 469762 f5e5b65b6600
child 469764 4a6e7c0051cb
push id35882
push usercbrindusan@mozilla.com
push dateWed, 17 Apr 2019 15:54:01 +0000
treeherdermozilla-central@37185c0ae520 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskmag
bugs1518843
milestone68.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1518843 - Use isPrivileged instead of restrictScheme for addons permissions. r=kmag Differential Revision: https://phabricator.services.mozilla.com/D22620
toolkit/components/extensions/Extension.jsm
--- a/toolkit/components/extensions/Extension.jsm
+++ b/toolkit/components/extensions/Extension.jsm
@@ -116,43 +116,47 @@ const PRIVATE_ALLOWED_PERMISSION = "inte
 // The userContextID reserved for the extension storage (its purpose is ensuring that the IndexedDB
 // storage used by the browser.storage.local API is not directly accessible from the extension code,
 // it is defined and reserved as "userContextIdInternal.webextStorageLocal" in ContextualIdentityService.jsm).
 const WEBEXT_STORAGE_USER_CONTEXT_ID = -1 >>> 0;
 
 // The maximum time to wait for extension child shutdown blockers to complete.
 const CHILD_SHUTDOWN_TIMEOUT_MS = 8000;
 
+// Permissions that are only available to privileged extensions.
+const PRIVILEGED_PERMS = new Set(["mozillaAddons"]);
+
 /**
  * Classify an individual permission from a webextension manifest
  * as a host/origin permission, an api permission, or a regular permission.
  *
  * @param {string} perm  The permission string to classify
  * @param {boolean} restrictSchemes
+ * @param {boolean} isPrivileged whether or not the webextension is privileged
  *
  * @returns {object}
  *          An object with exactly one of the following properties:
  *          "origin" to indicate this is a host/origin permission.
  *          "api" to indicate this is an api permission
  *                (as used for webextensions experiments).
  *          "permission" to indicate this is a regular permission.
  *          "invalid" to indicate that the given permission cannot be used.
  */
-function classifyPermission(perm, restrictSchemes) {
+function classifyPermission(perm, restrictSchemes, isPrivileged) {
   let match = /^(\w+)(?:\.(\w+)(?:\.\w+)*)?$/.exec(perm);
   if (!match) {
     try {
       let {pattern} = new MatchPattern(perm, {restrictSchemes, ignorePath: true});
       return {origin: pattern};
     } catch (e) {
       return {invalid: perm};
     }
   } else if (match[1] == "experiments" && match[2]) {
     return {api: match[2]};
-  } else if (perm === "mozillaAddons" && restrictSchemes) {
+  } else if (!isPrivileged && PRIVILEGED_PERMS.has(match[1])) {
     return {invalid: perm};
   }
   return {permission: perm};
 }
 
 const LOGGER_ID_BASE = "addons.webextension.";
 const UUID_MAP_PREF = "extensions.webextensions.uuids";
 const LEAVE_STORAGE_PREF = "extensions.webextensions.keepStorageOnUninstall";
@@ -498,19 +502,19 @@ class ExtensionData {
    */
   get manifestPermissions() {
     if (this.type !== "extension") {
       return null;
     }
 
     let permissions = new Set();
     let origins = new Set();
-    let {restrictSchemes} = this;
+    let {restrictSchemes, isPrivileged} = this;
     for (let perm of this.manifest.permissions || []) {
-      let type = classifyPermission(perm, restrictSchemes);
+      let type = classifyPermission(perm, restrictSchemes, isPrivileged);
       if (type.origin) {
         origins.add(perm);
       } else if (type.permission) {
         permissions.add(perm);
       }
     }
 
     if (this.manifest.devtools_page) {
@@ -692,28 +696,29 @@ class ExtensionData {
       originPermissions,
       permissions,
       schemaURLs: null,
       type: this.type,
       webAccessibleResources,
     };
 
     if (this.type === "extension") {
-      let restrictSchemes = !(this.isPrivileged && manifest.permissions.includes("mozillaAddons"));
+      let {isPrivileged} = this;
+      let restrictSchemes = !(isPrivileged && manifest.permissions.includes("mozillaAddons"));
 
       for (let perm of manifest.permissions) {
-        if (perm === "geckoProfiler" && !this.isPrivileged) {
+        if (perm === "geckoProfiler" && !isPrivileged) {
           const acceptedExtensions = Services.prefs.getStringPref("extensions.geckoProfiler.acceptedExtensionIds", "");
           if (!acceptedExtensions.split(",").includes(id)) {
             this.manifestError("Only whitelisted extensions are allowed to access the geckoProfiler.");
             continue;
           }
         }
 
-        let type = classifyPermission(perm, restrictSchemes);
+        let type = classifyPermission(perm, restrictSchemes, isPrivileged);
         if (type.origin) {
           perm = type.origin;
           originPermissions.add(perm);
         } else if (type.api) {
           apiNames.add(type.api);
         } else if (type.invalid) {
           this.manifestWarning(`Invalid extension permission: ${perm}`);
           continue;
@@ -2116,18 +2121,19 @@ class Extension extends ExtensionData {
   }
 
   get name() {
     return this.manifest.name;
   }
 
   get optionalOrigins() {
     if (this._optionalOrigins == null) {
-      let {restrictSchemes} = this;
-      let origins = this.manifest.optional_permissions.filter(perm => classifyPermission(perm, restrictSchemes).origin);
+      let {restrictSchemes, isPrivileged} = this;
+      let origins = this.manifest.optional_permissions.filter(perm =>
+        classifyPermission(perm, restrictSchemes, isPrivileged).origin);
       this._optionalOrigins = new MatchPatternSet(origins, {restrictSchemes, ignorePath: true});
     }
     return this._optionalOrigins;
   }
 }
 
 class Dictionary extends ExtensionData {
   constructor(addonData, startupReason) {