Bug 1506950 - Properly block mozAddonManager based on xpinstall.enabled r=aswan
authorMichael Kaply <mozilla@kaply.com>
Thu, 17 Jan 2019 23:04:18 +0000
changeset 511467 bc44f16fc690df91c599eb2a37a5d0c73ea65a0f
parent 511466 4fe0910cfb679969e615841077a055955c290b70
child 511468 f57051371bf06a8755aa69a5403b2011d63e40f1
push id10547
push userffxbld-merge
push dateMon, 21 Jan 2019 13:03:58 +0000
treeherdermozilla-beta@24ec1916bffe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersaswan
bugs1506950
milestone66.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 1506950 - Properly block mozAddonManager based on xpinstall.enabled r=aswan Differential Revision: https://phabricator.services.mozilla.com/D11806
toolkit/mozapps/extensions/AddonManagerWebAPI.cpp
toolkit/mozapps/extensions/amWebAPI.js
toolkit/mozapps/extensions/test/browser/browser.ini
toolkit/mozapps/extensions/test/browser/browser_webapi_install_disabled.js
--- a/toolkit/mozapps/extensions/AddonManagerWebAPI.cpp
+++ b/toolkit/mozapps/extensions/AddonManagerWebAPI.cpp
@@ -24,34 +24,16 @@ static bool IsValidHost(const nsACString
   // This hidden pref allows users to disable mozAddonManager entirely if they
   // want for fingerprinting resistance. Someone like Tor browser will use this
   // pref.
   if (Preferences::GetBool(
           "privacy.resistFingerprinting.block_mozAddonManager")) {
     return false;
   }
 
-  // This is ugly, but Preferences.h doesn't have support
-  // for default prefs or locked prefs
-  nsCOMPtr<nsIPrefService> prefService(
-      do_GetService(NS_PREFSERVICE_CONTRACTID));
-  nsCOMPtr<nsIPrefBranch> prefs;
-  if (prefService) {
-    prefService->GetDefaultBranch(nullptr, getter_AddRefs(prefs));
-    bool isEnabled;
-    if (NS_SUCCEEDED(prefs->GetBoolPref("xpinstall.enabled", &isEnabled)) &&
-        !isEnabled) {
-      bool isLocked;
-      prefs->PrefIsLocked("xpinstall.enabled", &isLocked);
-      if (isLocked) {
-        return false;
-      }
-    }
-  }
-
   if (host.EqualsLiteral("addons.mozilla.org") ||
       host.EqualsLiteral("discovery.addons.mozilla.org") ||
       host.EqualsLiteral("testpilot.firefox.com")) {
     return true;
   }
 
   // When testing allow access to the developer sites.
   if (Preferences::GetBool("extensions.webapi.testing", false)) {
--- a/toolkit/mozapps/extensions/amWebAPI.js
+++ b/toolkit/mozapps/extensions/amWebAPI.js
@@ -1,15 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+ChromeUtils.defineModuleGetter(this, "Services",
+  "resource://gre/modules/Services.jsm");
 
 XPCOMUtils.defineLazyPreferenceGetter(this, "WEBEXT_PERMISSION_PROMPTS",
                                       "extensions.webextPermissionPrompts", false);
 
 const MSG_PROMISE_REQUEST  = "WebAPIPromiseRequest";
 const MSG_PROMISE_RESULT   = "WebAPIPromiseResult";
 const MSG_INSTALL_EVENT    = "WebAPIInstallEvent";
 const MSG_INSTALL_CLEANUP  = "WebAPICleanup";
@@ -219,16 +221,19 @@ class WebAPI extends APIObject {
         return null;
       }
       let addon = new Addon(this.window, this.broker, addonInfo);
       return this.window.Addon._create(this.window, addon);
     });
   }
 
   createInstall(options) {
+    if (!Services.prefs.getBoolPref("xpinstall.enabled", true)) {
+      throw new this.window.Error("Software installation is disabled.");
+    }
     let installOptions = {
       ...options,
       // Provide the host from which the amWebAPI is being called
       // (so that we can detect if the API is being used from the disco pane,
       // AMO, testpilot or another unknown webpage).
       sourceHost: this.window.document.nodePrincipal.URI &&
         this.window.document.nodePrincipal.URI.host,
     };
--- a/toolkit/mozapps/extensions/test/browser/browser.ini
+++ b/toolkit/mozapps/extensions/test/browser/browser.ini
@@ -97,16 +97,17 @@ skip-if = verify
 [browser_uninstalling.js]
 [browser_updateid.js]
 [browser_updatessl.js]
 [browser_webapi.js]
 [browser_webapi_access.js]
 [browser_webapi_addon_listener.js]
 [browser_webapi_enable.js]
 [browser_webapi_install.js]
+[browser_webapi_install_disabled.js]
 [browser_webapi_theme.js]
 [browser_webapi_uninstall.js]
 [browser_webext_icon.js]
 [browser_webext_options.js]
 tags = webextensions
 skip-if = os == 'linux' || (os == 'mac' && debug) # bug 1483347
 [browser_webext_options_addon_reload.js]
 tags = webextensions
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_webapi_install_disabled.js
@@ -0,0 +1,54 @@
+const TESTPAGE = `${SECURE_TESTROOT}webapi_checkavailable.html`;
+const XPI_URL = `${SECURE_TESTROOT}../xpinstall/amosigned.xpi`;
+
+function waitForClear() {
+  const MSG = "WebAPICleanup";
+  return new Promise(resolve => {
+    let listener = {
+      receiveMessage(msg) {
+        if (msg.name == MSG) {
+          Services.mm.removeMessageListener(MSG, listener);
+          resolve();
+        }
+      },
+    };
+
+    Services.mm.addMessageListener(MSG, listener, true);
+  });
+}
+
+add_task(async function setup() {
+  await SpecialPowers.pushPrefEnv({
+    set: [["extensions.webapi.testing", true],
+          ["xpinstall.enabled", false],
+          ["extensions.install.requireBuiltInCerts", false]],
+  });
+  info("added preferences");
+});
+
+async function testInstall(browser, args) {
+  let success = await ContentTask.spawn(browser, {args}, async function(opts) {
+    let { args } = opts;
+    let install;
+    try {
+      install = await content.navigator.mozAddonManager.createInstall(args);
+    } catch (e) {}
+    return !!install;
+  });
+  is(success, false, "Install was blocked");
+}
+
+add_task(async function() {
+    // withNewTab() will close the test tab before returning, at which point
+    // the cleanup event will come from the content process.  We need to see
+    // that event but don't want to race to install a listener for it after
+    // the tab is closed.  So set up the listener now but don't yield the
+    // listening promise until below.
+    let clearPromise = waitForClear();
+
+    await BrowserTestUtils.withNewTab(TESTPAGE, async function(browser) {
+      await testInstall(browser, {url: XPI_URL});
+    });
+
+    await clearPromise;
+});