author | Andrew Swan <aswan@mozilla.com> |
Fri, 15 Jul 2016 11:40:45 -0700 | |
changeset 306056 | e25f1a122d9dc433d907ec2d1a6c9108cb3fbec0 |
parent 306055 | 8d4f43219dcfeb6b102f974e2617a31c1745a989 |
child 306057 | 70a2074086327156d960bdcb84d5606ca9323ad8 |
push id | 79765 |
push user | cbook@mozilla.com |
push date | Thu, 21 Jul 2016 14:26:34 +0000 |
treeherder | mozilla-inbound@ab54bfc55266 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | rhelmer |
bugs | 1287125 |
milestone | 50.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
|
--- a/toolkit/mozapps/extensions/AddonManager.jsm +++ b/toolkit/mozapps/extensions/AddonManager.jsm @@ -41,16 +41,17 @@ const PREF_EM_HOTFIX_LASTVERSION = const PREF_EM_HOTFIX_URL = "extensions.hotfix.url"; const PREF_EM_CERT_CHECKATTRIBUTES = "extensions.hotfix.cert.checkAttributes"; const PREF_EM_HOTFIX_CERTS = "extensions.hotfix.certs."; const PREF_MATCH_OS_LOCALE = "intl.locale.matchOS"; const PREF_SELECTED_LOCALE = "general.useragent.locale"; const UNKNOWN_XPCOM_ABI = "unknownABI"; const PREF_MIN_WEBEXT_PLATFORM_VERSION = "extensions.webExtensionsMinPlatformVersion"; +const PREF_WEBAPI_TESTING = "extensions.webapi.testing"; const UPDATE_REQUEST_VERSION = 2; const CATEGORY_UPDATE_PARAMS = "extension-update-params"; const XMLURI_BLOCKLIST = "http://www.mozilla.org/2006/addons-blocklist"; const KEY_PROFILEDIR = "ProfD"; const KEY_APPDIR = "XCurProcD"; @@ -61,16 +62,23 @@ const PREF_EM_CHECK_COMPATIBILITY_BASE = var PREF_EM_CHECK_COMPATIBILITY = MOZ_COMPATIBILITY_NIGHTLY ? PREF_EM_CHECK_COMPATIBILITY_BASE + ".nightly" : undefined; const TOOLKIT_ID = "toolkit@mozilla.org"; const VALID_TYPES_REGEXP = /^[\w\-]+$/; +const WEBAPI_INSTALL_HOSTS = ["addons.mozilla.org", "addons.cdn.mozilla.net"]; +const WEBAPI_TEST_INSTALL_HOSTS = [ + "addons.allizom.org", "addons-stage-cdn.allizom.org", + "addons-dev.allizom.org", "addons-dev-cdn-allizom.org", + "example.com", +]; + Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/AsyncShutdown.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "Task", "resource://gre/modules/Task.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "Promise", "resource://gre/modules/Promise.jsm"); @@ -2891,17 +2899,39 @@ var AddonManagerInternal = { if (!info) { throw new Error(`forgetInstall cannot find ${id}`); } info.install.removeListener(info.listener); this.installs.delete(id); }, createInstall(target, options) { - return new Promise((resolve) => { + // Throw an appropriate error if the given URL is not valid + // as an installation source. Return silently if it is okay. + function checkInstallUrl(url) { + let host = Services.io.newURI(options.url, null, null).host; + if (WEBAPI_INSTALL_HOSTS.includes(host)) { + return; + } + if (Services.prefs.getBoolPref(PREF_WEBAPI_TESTING) + && WEBAPI_TEST_INSTALL_HOSTS.includes(host)) { + return; + } + + throw new Error(`Install from ${host} not permitted`); + } + + return new Promise((resolve, reject) => { + try { + checkInstallUrl(options.url); + } catch (err) { + reject({message: err.message}); + return; + } + let newInstall = install => { let id = this.nextInstall++; let listener = this.makeListener(id, target); install.addListener(listener); this.installs.set(id, {install, target, listener}); let result = {id};
--- a/toolkit/mozapps/extensions/addonManager.js +++ b/toolkit/mozapps/extensions/addonManager.js @@ -243,17 +243,19 @@ amManager.prototype = { onInstalled: (addon) => handler("onInstalled", addon.id, false), onUninstalling: (addon, needsRestart) => handler("onUninstalling", addon.id, needsRestart), onUninstalled: (addon) => handler("onUninstalled", addon.id, false), onOperationCancelled: (addon) => handler("onOperationCancelled", addon.id, false), }; } AddonManager.addAddonListener(this.addonListener); } else { - AddonManager.removeAddonListener(this.addonListener); + if (this.addonListener) { + AddonManager.removeAddonListener(this.addonListener); + } } } } return undefined; }, sendEvent(target, data) { target.sendAsyncMessage(MSG_INSTALL_EVENT, data);
--- a/toolkit/mozapps/extensions/test/browser/browser_webapi_install.js +++ b/toolkit/mozapps/extensions/test/browser/browser_webapi_install.js @@ -1,38 +1,39 @@ const TESTPAGE = `${SECURE_TESTROOT}webapi_checkavailable.html`; const XPI_URL = `${SECURE_TESTROOT}addons/browser_webapi_install.xpi`; const ID = "webapi_install@tests.mozilla.org"; // eh, would be good to just stat the real file instead of this... const XPI_LEN = 4782; -Services.prefs.setBoolPref("extensions.webapi.testing", true); -Services.prefs.setBoolPref("extensions.install.requireBuiltInCerts", false); -registerCleanupFunction(() => { - Services.prefs.clearUserPref("extensions.webapi.testing"); - Services.prefs.clearUserPref("extensions.install.requireBuiltInCerts"); -}); - function waitForClear() { const MSG = "WebAPICleanup"; return new Promise(resolve => { let listener = { receiveMessage: function(msg) { if (msg.name == MSG) { Services.ppmm.removeMessageListener(MSG, listener); resolve(); } } }; Services.ppmm.addMessageListener(MSG, listener); }); } +add_task(function* setup() { + yield SpecialPowers.pushPrefEnv({ + set: [["extensions.webapi.testing", true], + ["extensions.install.requireBuiltInCerts", false]], + }); + info("added preferences"); +}); + // Wrapper around a common task to run in the content process to test // the mozAddonManager API. Takes a URL for the XPI to install and an // array of steps, each of which can either be an action to take // (i.e., start or cancel the install) or an install event to wait for. // Steps that look for a specific event may also include a "props" property // with properties that the AddonInstall object is expected to have when // that event is triggered. function* testInstall(browser, url, steps, description) { @@ -235,8 +236,35 @@ add_task(makeInstallTest(function* (brow yield testInstall(browser, XPI_URL + "bogus", steps, "install of a bad url fails"); let addons = yield promiseAddonsByIDs([ID]); is(addons[0], null, "The addon was not installed"); ok(AddonManager.webAPI.installs.size > 0, "webAPI is tracking the AddonInstall"); })); +add_task(function* test_permissions() { + function testBadUrl(url, pattern, successMessage) { + return BrowserTestUtils.withNewTab(TESTPAGE, function* (browser) { + let result = yield ContentTask.spawn(browser, {url, pattern}, function (opts) { + return new Promise(resolve => { + content.navigator.mozAddonManager.createInstall({url: opts.url}) + .then(() => { + resolve({success: false, message: "createInstall should not have succeeded"}); + }, err => { + if (err.message.match(new RegExp(opts.pattern))) { + resolve({success: true}); + } + resolve({success: false, message: `Wrong error message: ${err.message}`}); + }); + }); + }); + is(result.success, true, result.message || successMessage); + }); + } + + yield testBadUrl("i am not a url", "NS_ERROR_MALFORMED_URI", + "Installing from an unparseable URL fails"); + + yield testBadUrl("https://addons.not-really-mozilla.org/impostor.xpi", + "not permitted", + "Installing from non-approved URL fails"); +});