зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1734987 - Part 3: Add isPrivileged to ExtensionData + test r=rpl
See https://bugzilla.mozilla.org/show_bug.cgi?id=1734987#c0 for the description of the why and how of this patch. Since isPrivileged is now supported on ExtensionData, this patch also removes the work-arounds from bug 1675858. Differential Revision: https://phabricator.services.mozilla.com/D128232
This commit is contained in:
Родитель
fdcd5ac4ad
Коммит
c02964f87e
|
@ -530,9 +530,10 @@ const manifestTypes = new Map([
|
|||
* `loadManifest` has been called, and completed.
|
||||
*/
|
||||
class ExtensionData {
|
||||
constructor(rootURI) {
|
||||
constructor(rootURI, isPrivileged = false) {
|
||||
this.rootURI = rootURI;
|
||||
this.resourceURL = rootURI.spec;
|
||||
this.isPrivileged = isPrivileged;
|
||||
|
||||
this.manifest = null;
|
||||
this.type = null;
|
||||
|
@ -553,6 +554,38 @@ class ExtensionData {
|
|||
this.eventPagesEnabled = eventPagesEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* A factory function that allows the construction of ExtensionData, with
|
||||
* the isPrivileged flag computed asynchronously.
|
||||
*
|
||||
* @param {nsIURI} rootURI
|
||||
* The URI pointing to the extension root.
|
||||
* @param {function(type, id)} checkPrivileged
|
||||
* An (async) function that takes the addon type and addon ID and returns
|
||||
* whether the given add-on is privileged.
|
||||
* @returns {ExtensionData}
|
||||
*/
|
||||
static async constructAsync({ rootURI, checkPrivileged }) {
|
||||
let extension = new ExtensionData(rootURI);
|
||||
// checkPrivileged depends on the extension type and id.
|
||||
await extension.initializeAddonTypeAndID();
|
||||
let { type, id } = extension;
|
||||
// Map the extension type to the type name used by the add-on manager.
|
||||
// TODO bug 1757084: Remove this.
|
||||
type = type == "langpack" ? "locale" : type;
|
||||
extension.isPrivileged = await checkPrivileged(type, id);
|
||||
return extension;
|
||||
}
|
||||
|
||||
static getIsPrivileged({ signedState, builtIn, temporarilyInstalled }) {
|
||||
return (
|
||||
signedState === AddonManager.SIGNEDSTATE_PRIVILEGED ||
|
||||
signedState === AddonManager.SIGNEDSTATE_SYSTEM ||
|
||||
builtIn ||
|
||||
(AddonSettings.EXPERIMENTS_ENABLED && temporarilyInstalled)
|
||||
);
|
||||
}
|
||||
|
||||
get builtinMessages() {
|
||||
return null;
|
||||
}
|
||||
|
@ -725,25 +758,8 @@ class ExtensionData {
|
|||
});
|
||||
}
|
||||
|
||||
canCheckSignature() {
|
||||
// ExtensionData instances can't check the signature because it is not yet
|
||||
// available when XPIProvider does use it to load the extension manifest.
|
||||
//
|
||||
// This method will return true for the ExtensionData subclasses (like
|
||||
// the Extension class) to enable the additional validation that would require
|
||||
// the signature to be available (e.g. to check if the extension is allowed to
|
||||
// use a privileged permission).
|
||||
return this.constructor != ExtensionData;
|
||||
}
|
||||
|
||||
get restrictSchemes() {
|
||||
// mozillaAddons permission is only allowed for privileged addons and
|
||||
// filtered out if the extension isn't privileged.
|
||||
// When the manifest is loaded by an explicit ExtensionData class
|
||||
// instance, the signature data isn't available yet and this helper
|
||||
// would always return false, but it will return true when appropriate
|
||||
// (based on the isPrivileged boolean property) for the Extension class.
|
||||
return !this.hasPermission("mozillaAddons");
|
||||
return !(this.isPrivileged && this.hasPermission("mozillaAddons"));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1027,15 +1043,48 @@ class ExtensionData {
|
|||
return Schemas.normalize(this.rawManifest, manifestType, context);
|
||||
}
|
||||
|
||||
async initializeAddonTypeAndID() {
|
||||
if (this.type) {
|
||||
// Already initialized.
|
||||
return;
|
||||
}
|
||||
this.rawManifest = await this.readJSON("manifest.json");
|
||||
let manifest = this.rawManifest;
|
||||
|
||||
if (manifest.theme) {
|
||||
this.type = "theme";
|
||||
} else if (manifest.langpack_id) {
|
||||
// TODO bug 1757084: This should be "locale".
|
||||
this.type = "langpack";
|
||||
} else if (manifest.dictionaries) {
|
||||
this.type = "dictionary";
|
||||
} else if (manifest.site_permissions) {
|
||||
this.type = "sitepermission";
|
||||
} else {
|
||||
this.type = "extension";
|
||||
}
|
||||
|
||||
if (!this.id) {
|
||||
let bss =
|
||||
manifest.browser_specific_settings?.gecko ||
|
||||
manifest.applications?.gecko;
|
||||
let id = bss?.id;
|
||||
// This is a basic type check.
|
||||
// When parseManifest is called, the ID is validated more thoroughly
|
||||
// because the id is defined to be an ExtensionID type in
|
||||
// toolkit/components/extensions/schemas/manifest.json
|
||||
if (typeof id == "string") {
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line complexity
|
||||
async parseManifest() {
|
||||
let [manifest] = await Promise.all([
|
||||
this.readJSON("manifest.json"),
|
||||
Management.lazyInit(),
|
||||
]);
|
||||
await Promise.all([this.initializeAddonTypeAndID(), Management.lazyInit()]);
|
||||
|
||||
let manifest = this.rawManifest;
|
||||
this.manifest = manifest;
|
||||
this.rawManifest = manifest;
|
||||
|
||||
if (manifest.default_locale) {
|
||||
await this.initLocale();
|
||||
|
@ -1054,18 +1103,6 @@ class ExtensionData {
|
|||
}
|
||||
}
|
||||
|
||||
if (this.manifest.theme) {
|
||||
this.type = "theme";
|
||||
} else if (this.manifest.langpack_id) {
|
||||
this.type = "langpack";
|
||||
} else if (this.manifest.dictionaries) {
|
||||
this.type = "dictionary";
|
||||
} else if (this.manifest.site_permissions) {
|
||||
this.type = "sitepermission";
|
||||
} else {
|
||||
this.type = "extension";
|
||||
}
|
||||
|
||||
let normalized = await this._getNormalizedManifest();
|
||||
if (normalized.error) {
|
||||
this.manifestError(normalized.error);
|
||||
|
@ -1096,8 +1133,6 @@ class ExtensionData {
|
|||
this.logWarning("Event pages are not currently supported.");
|
||||
}
|
||||
|
||||
this.id ??= manifest.applications?.gecko?.id;
|
||||
|
||||
let apiNames = new Set();
|
||||
let dependencies = new Set();
|
||||
let originPermissions = new Set();
|
||||
|
@ -1106,6 +1141,9 @@ class ExtensionData {
|
|||
|
||||
let schemaPromises = new Map();
|
||||
|
||||
// Note: this.id and this.type were computed in initializeAddonTypeAndID.
|
||||
// The format of `this.id` was confirmed to be a valid extensionID by the
|
||||
// Schema validation as part of the _getNormalizedManifest() call.
|
||||
let result = {
|
||||
apiNames,
|
||||
dependencies,
|
||||
|
@ -1148,18 +1186,6 @@ class ExtensionData {
|
|||
} else if (type.api) {
|
||||
apiNames.add(type.api);
|
||||
} else if (type.invalid) {
|
||||
if (!this.canCheckSignature() && PRIVILEGED_PERMS.has(perm)) {
|
||||
// Do not emit the warning if the invalid permission is a privileged one
|
||||
// and the current instance can't yet check for a valid signature
|
||||
// (see Bug 1675858 and the inline comment inside the canCheckSignature
|
||||
// method for more details).
|
||||
//
|
||||
// This parseManifest method will be called again on the Extension class
|
||||
// instance, which will have the signature available and the invalid
|
||||
// extension permission warnings will be collected and logged if necessary.
|
||||
continue;
|
||||
}
|
||||
|
||||
this.manifestWarning(`Invalid extension permission: ${perm}`);
|
||||
continue;
|
||||
}
|
||||
|
@ -2101,7 +2127,7 @@ let pendingExtensions = new Map();
|
|||
*/
|
||||
class Extension extends ExtensionData {
|
||||
constructor(addonData, startupReason) {
|
||||
super(addonData.resourceURI);
|
||||
super(addonData.resourceURI, addonData.isPrivileged);
|
||||
|
||||
this.startupStates = new Set();
|
||||
this.state = "Not started";
|
||||
|
@ -2345,15 +2371,6 @@ class Extension extends ExtensionData {
|
|||
return [this.id, this.version, Services.locale.appLocaleAsBCP47];
|
||||
}
|
||||
|
||||
get isPrivileged() {
|
||||
return (
|
||||
this.addonData.signedState === AddonManager.SIGNEDSTATE_PRIVILEGED ||
|
||||
this.addonData.signedState === AddonManager.SIGNEDSTATE_SYSTEM ||
|
||||
this.addonData.builtIn ||
|
||||
(AddonSettings.EXPERIMENTS_ENABLED && this.temporarilyInstalled)
|
||||
);
|
||||
}
|
||||
|
||||
get temporarilyInstalled() {
|
||||
return !!this.addonData.temporarilyInstalled;
|
||||
}
|
||||
|
|
|
@ -36,6 +36,11 @@ ChromeUtils.defineModuleGetter(
|
|||
"Extension",
|
||||
"resource://gre/modules/Extension.jsm"
|
||||
);
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this,
|
||||
"ExtensionData",
|
||||
"resource://gre/modules/Extension.jsm"
|
||||
);
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this,
|
||||
"ExtensionParent",
|
||||
|
@ -575,6 +580,12 @@ ExtensionTestCommon = class ExtensionTestCommon {
|
|||
signedState = AddonManager.SIGNEDSTATE_SYSTEM;
|
||||
}
|
||||
|
||||
let isPrivileged = ExtensionData.getIsPrivileged({
|
||||
signedState,
|
||||
builtIn: false,
|
||||
temporarilyInstalled: !!data.temporarilyInstalled,
|
||||
});
|
||||
|
||||
return new Extension(
|
||||
{
|
||||
id,
|
||||
|
@ -583,6 +594,7 @@ ExtensionTestCommon = class ExtensionTestCommon {
|
|||
signedState,
|
||||
incognitoOverride: data.incognitoOverride,
|
||||
temporarilyInstalled: !!data.temporarilyInstalled,
|
||||
isPrivileged,
|
||||
TEST_NO_ADDON_MANAGER: true,
|
||||
// By default we set TEST_NO_DELAYED_STARTUP to true
|
||||
TEST_NO_DELAYED_STARTUP: !data.delayedStartup,
|
||||
|
|
|
@ -668,26 +668,28 @@ add_task(async function update_unprivileged_with_mozillaAddons() {
|
|||
});
|
||||
|
||||
// Tests that invalid permission warning for privileged permissions requested
|
||||
// without the privilged signature are emitted by the Extension class instance
|
||||
// but not for the ExtensionData instances (on which the signature is not
|
||||
// available and the warning would be emitted even for the ones signed correctly).
|
||||
// are not emitted for privileged extensions, only for unprivileged extensions.
|
||||
add_task(
|
||||
async function test_invalid_permission_warning_on_privileged_permission() {
|
||||
await AddonTestUtils.promiseStartupManager();
|
||||
|
||||
const MANIFEST_WARNINGS = [
|
||||
"Reading manifest: Invalid extension permission: mozillaAddons",
|
||||
"Reading manifest: Invalid extension permission: resource://x/",
|
||||
"Reading manifest: Invalid extension permission: about:reader*",
|
||||
];
|
||||
|
||||
async function testInvalidPermissionWarning({ isPrivileged }) {
|
||||
let id = isPrivileged
|
||||
? "privileged-addon@mochi.test"
|
||||
: "nonprivileged-addon@mochi.test";
|
||||
|
||||
let expectedWarnings = isPrivileged
|
||||
? []
|
||||
: ["Reading manifest: Invalid extension permission: mozillaAddons"];
|
||||
let expectedWarnings = isPrivileged ? [] : MANIFEST_WARNINGS;
|
||||
|
||||
const ext = ExtensionTestUtils.loadExtension({
|
||||
useAddonManager: "permanent",
|
||||
manifest: {
|
||||
permissions: ["mozillaAddons"],
|
||||
permissions: ["mozillaAddons", "resource://x/", "about:reader*"],
|
||||
applications: { gecko: { id } },
|
||||
},
|
||||
background() {},
|
||||
|
@ -711,28 +713,67 @@ add_task(
|
|||
// ExtensionData instance created below).
|
||||
let generatedExt = ExtensionTestCommon.generate({
|
||||
manifest: {
|
||||
permissions: ["mozillaAddons"],
|
||||
permissions: ["mozillaAddons", "resource://x/", "about:reader*"],
|
||||
applications: { gecko: { id: "extension-data@mochi.test" } },
|
||||
},
|
||||
});
|
||||
|
||||
// Verify that XPIInstall.jsm will not collect the warning for the
|
||||
// privileged permission as expected.
|
||||
const extData = new ExtensionData(generatedExt.rootURI);
|
||||
await extData.loadManifest();
|
||||
async function getWarningsFromExtensionData({ isPrivileged }) {
|
||||
let extData;
|
||||
if (typeof isPrivileged == "function") {
|
||||
// isPrivileged expected to be computed asynchronously.
|
||||
extData = await ExtensionData.constructAsync({
|
||||
rootURI: generatedExt.rootURI,
|
||||
checkPrivileged: isPrivileged,
|
||||
});
|
||||
} else {
|
||||
extData = new ExtensionData(generatedExt.rootURI, isPrivileged);
|
||||
}
|
||||
await extData.loadManifest();
|
||||
|
||||
// This assertion is just meant to prevent the test to pass if there were
|
||||
// no warnings because some errors prevented the warnings to be
|
||||
// collected).
|
||||
Assert.deepEqual(
|
||||
extData.errors,
|
||||
[],
|
||||
"No errors collected by the ExtensionData instance"
|
||||
);
|
||||
return extData.warnings;
|
||||
}
|
||||
|
||||
Assert.deepEqual(
|
||||
extData.warnings,
|
||||
[],
|
||||
"No warnings for mozillaAddons permission collected for the ExtensionData instance"
|
||||
await getWarningsFromExtensionData({ isPrivileged: undefined }),
|
||||
MANIFEST_WARNINGS,
|
||||
"Got warnings about privileged permissions by default"
|
||||
);
|
||||
|
||||
// This assertion is just meant to prevent the test to pass if there were no warnings
|
||||
// because some errors prevented the warnings to be collected).
|
||||
Assert.deepEqual(
|
||||
extData.errors,
|
||||
[],
|
||||
"No errors collected by the ExtensionData instance"
|
||||
await getWarningsFromExtensionData({ isPrivileged: false }),
|
||||
MANIFEST_WARNINGS,
|
||||
"Got warnings about privileged permissions for non-privileged extensions"
|
||||
);
|
||||
|
||||
Assert.deepEqual(
|
||||
await getWarningsFromExtensionData({ isPrivileged: true }),
|
||||
[],
|
||||
"No warnings about privileged permissions on privileged extensions"
|
||||
);
|
||||
|
||||
Assert.deepEqual(
|
||||
await getWarningsFromExtensionData({ isPrivileged: async () => false }),
|
||||
MANIFEST_WARNINGS,
|
||||
"Got warnings about privileged permissions for non-privileged extensions (async)"
|
||||
);
|
||||
|
||||
Assert.deepEqual(
|
||||
await getWarningsFromExtensionData({ isPrivileged: async () => true }),
|
||||
[],
|
||||
"No warnings about privileged permissions on privileged extensions (async)"
|
||||
);
|
||||
|
||||
// Cleanup the generated xpi file.
|
||||
await generatedExt.cleanupGeneratedFile();
|
||||
|
||||
|
|
|
@ -441,14 +441,30 @@ function waitForAllPromises(promises) {
|
|||
*
|
||||
* @param {Package} aPackage
|
||||
* The install package for the add-on
|
||||
* @returns {AddonInternal}
|
||||
* @param {XPIStateLocation} aLocation
|
||||
* The install location the add-on is installed in, or will be
|
||||
* installed to.
|
||||
* @returns {{ addon: AddonInternal, verifiedSignedState: object}}
|
||||
* @throws if the install manifest in the stream is corrupt or could not
|
||||
* be read
|
||||
*/
|
||||
async function loadManifestFromWebManifest(aPackage) {
|
||||
let extension = new ExtensionData(
|
||||
XPIInternal.maybeResolveURI(aPackage.rootURI)
|
||||
);
|
||||
async function loadManifestFromWebManifest(aPackage, aLocation) {
|
||||
let verifiedSignedState;
|
||||
let extension = await ExtensionData.constructAsync({
|
||||
rootURI: XPIInternal.maybeResolveURI(aPackage.rootURI),
|
||||
async checkPrivileged(type, id) {
|
||||
verifiedSignedState = await aPackage.verifySignedState(
|
||||
id,
|
||||
type,
|
||||
aLocation
|
||||
);
|
||||
return ExtensionData.getIsPrivileged({
|
||||
signedState: verifiedSignedState.signedState,
|
||||
builtIn: aLocation.isBuiltin,
|
||||
temporarilyInstalled: aLocation.isTemporary,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
let manifest = await extension.loadManifest();
|
||||
|
||||
|
@ -574,7 +590,7 @@ async function loadManifestFromWebManifest(aPackage) {
|
|||
addon.softDisabled =
|
||||
addon.blocklistState == nsIBlocklistService.STATE_SOFTBLOCKED;
|
||||
|
||||
return addon;
|
||||
return { addon, verifiedSignedState };
|
||||
}
|
||||
|
||||
async function readRecommendationStates(aPackage, aAddonID) {
|
||||
|
@ -649,13 +665,23 @@ function generateTemporaryInstallID(aFile) {
|
|||
|
||||
var loadManifest = async function(aPackage, aLocation, aOldAddon) {
|
||||
let addon;
|
||||
let verifiedSignedState;
|
||||
if (await aPackage.hasResource("manifest.json")) {
|
||||
addon = await loadManifestFromWebManifest(aPackage);
|
||||
({ addon, verifiedSignedState } = await loadManifestFromWebManifest(
|
||||
aPackage,
|
||||
aLocation
|
||||
));
|
||||
} else {
|
||||
// TODO bug 1674799: Remove this unused branch.
|
||||
for (let loader of AddonManagerPrivate.externalExtensionLoaders.values()) {
|
||||
if (await aPackage.hasResource(loader.manifestFile)) {
|
||||
addon = await loader.loadManifest(aPackage);
|
||||
addon.loader = loader.name;
|
||||
verifiedSignedState = await aPackage.verifySignedState(
|
||||
addon.id,
|
||||
addon.type,
|
||||
aLocation
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -671,11 +697,7 @@ var loadManifest = async function(aPackage, aLocation, aOldAddon) {
|
|||
addon.rootURI = aPackage.rootURI.spec;
|
||||
addon.location = aLocation;
|
||||
|
||||
let { signedState, cert } = await aPackage.verifySignedState(
|
||||
addon.id,
|
||||
addon.type,
|
||||
aLocation
|
||||
);
|
||||
let { signedState, cert } = verifiedSignedState;
|
||||
addon.signedState = signedState;
|
||||
addon.signedDate = cert?.validity?.notBefore / 1000 || null;
|
||||
if (!addon.isPrivileged) {
|
||||
|
|
|
@ -33,6 +33,7 @@ XPCOMUtils.defineLazyModuleGetters(this, {
|
|||
AsyncShutdown: "resource://gre/modules/AsyncShutdown.jsm",
|
||||
Dictionary: "resource://gre/modules/Extension.jsm",
|
||||
Extension: "resource://gre/modules/Extension.jsm",
|
||||
ExtensionData: "resource://gre/modules/Extension.jsm",
|
||||
Langpack: "resource://gre/modules/Extension.jsm",
|
||||
SitePermission: "resource://gre/modules/Extension.jsm",
|
||||
FileUtils: "resource://gre/modules/FileUtils.jsm",
|
||||
|
@ -1804,6 +1805,13 @@ class BootstrapScope {
|
|||
}
|
||||
}
|
||||
}
|
||||
// TODO D128233: Replace AddonInternal's isPrivileged getter with a call to
|
||||
// Extensions.getIsPrivileged, and use addon.isPrivileged instead of this.
|
||||
const isPrivileged = ExtensionData.getIsPrivileged({
|
||||
signedState: addon.signedState,
|
||||
builtIn: addon.location.isBuiltin,
|
||||
temporarilyInstalled: addon.location.isTemporary,
|
||||
});
|
||||
|
||||
let params = {
|
||||
id: addon.id,
|
||||
|
@ -1813,6 +1821,7 @@ class BootstrapScope {
|
|||
temporarilyInstalled: addon.location.isTemporary,
|
||||
builtIn: addon.location.isBuiltin,
|
||||
isSystem: addon.location.isSystem,
|
||||
isPrivileged,
|
||||
recommendationState: addon.recommendationState,
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,189 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
const { XPIInstall } = ChromeUtils.import(
|
||||
"resource://gre/modules/addons/XPIInstall.jsm"
|
||||
);
|
||||
const {
|
||||
XPIInternal: {
|
||||
BuiltInLocation,
|
||||
KEY_APP_PROFILE,
|
||||
KEY_APP_SYSTEM_DEFAULTS,
|
||||
KEY_APP_SYSTEM_PROFILE,
|
||||
TemporaryInstallLocation,
|
||||
XPIStates,
|
||||
},
|
||||
} = ChromeUtils.import("resource://gre/modules/addons/XPIProvider.jsm");
|
||||
|
||||
AddonTestUtils.init(this);
|
||||
AddonTestUtils.overrideCertDB();
|
||||
|
||||
Services.prefs.setIntPref(
|
||||
"extensions.enabledScopes",
|
||||
// SCOPE_PROFILE is enabled by default,
|
||||
// SCOPE_APPLICATION is to enable KEY_APP_SYSTEM_PROFILE, which we need to
|
||||
// test the combination (isSystem && !isBuiltin) in test_system_location.
|
||||
AddonManager.SCOPE_PROFILE | AddonManager.SCOPE_APPLICATION
|
||||
);
|
||||
// test_builtin_system_location tests the (isSystem && isBuiltin) combination
|
||||
// (i.e. KEY_APP_SYSTEM_DEFAULTS). That location only exists if this directory
|
||||
// is found:
|
||||
const distroDir = FileUtils.getDir("ProfD", ["sysfeatures"], true);
|
||||
registerDirectory("XREAppFeat", distroDir);
|
||||
|
||||
function getInstallLocation({
|
||||
isBuiltin = false,
|
||||
isSystem = false,
|
||||
isTemporary = false,
|
||||
}) {
|
||||
if (isTemporary) {
|
||||
// Temporary installation. Signatures will not be verified.
|
||||
return TemporaryInstallLocation; // KEY_APP_TEMPORARY
|
||||
}
|
||||
let location;
|
||||
if (isSystem) {
|
||||
if (isBuiltin) {
|
||||
// System location. Signatures will not be verified.
|
||||
location = XPIStates.getLocation(KEY_APP_SYSTEM_DEFAULTS);
|
||||
} else {
|
||||
// Normandy installations. Signatures will be verified.
|
||||
location = XPIStates.getLocation(KEY_APP_SYSTEM_PROFILE);
|
||||
}
|
||||
} else if (isBuiltin) {
|
||||
// Packaged with the application. Signatures will not be verified.
|
||||
location = BuiltInLocation; // KEY_APP_BUILTINS
|
||||
} else {
|
||||
// By default - The profile directory. Signatures will be verified.
|
||||
location = XPIStates.getLocation(KEY_APP_PROFILE);
|
||||
}
|
||||
// Sanity checks to make sure that the flags match the expected values.
|
||||
if (location.isSystem !== isSystem) {
|
||||
ok(false, `${location.name}, unexpected isSystem=${location.isSystem}`);
|
||||
}
|
||||
if (location.isBuiltin !== isBuiltin) {
|
||||
ok(false, `${location.name}, unexpected isBuiltin=${location.isBuiltin}`);
|
||||
}
|
||||
return location;
|
||||
}
|
||||
|
||||
async function testLoadManifest({ location, expectPrivileged }) {
|
||||
location ??= getInstallLocation({});
|
||||
let xpi = await AddonTestUtils.createTempWebExtensionFile({
|
||||
manifest: {
|
||||
applications: { gecko: { id: "@with-privileged-perm" } },
|
||||
permissions: ["mozillaAddons", "cookies"],
|
||||
},
|
||||
});
|
||||
let actualPermissions;
|
||||
let { messages } = await AddonTestUtils.promiseConsoleOutput(async () => {
|
||||
let addon = await XPIInstall.loadManifestFromFile(xpi, location);
|
||||
actualPermissions = addon.userPermissions;
|
||||
});
|
||||
if (expectPrivileged) {
|
||||
AddonTestUtils.checkMessages(messages, {
|
||||
expected: [],
|
||||
forbidden: [
|
||||
{
|
||||
message: /Reading manifest: Invalid extension permission/,
|
||||
},
|
||||
],
|
||||
});
|
||||
Assert.deepEqual(
|
||||
actualPermissions,
|
||||
{ origins: [], permissions: ["mozillaAddons", "cookies"] },
|
||||
"Privileged permission should exist"
|
||||
);
|
||||
} else {
|
||||
AddonTestUtils.checkMessages(messages, {
|
||||
expected: [
|
||||
{
|
||||
message: /Reading manifest: Invalid extension permission: mozillaAddons/,
|
||||
},
|
||||
],
|
||||
forbidden: [],
|
||||
});
|
||||
Assert.deepEqual(
|
||||
actualPermissions,
|
||||
{ origins: [], permissions: ["cookies"] },
|
||||
"Privileged permission should be ignored"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
add_task(async function setup() {
|
||||
await ExtensionTestUtils.startAddonManager();
|
||||
});
|
||||
|
||||
add_task(async function test_regular_addon() {
|
||||
AddonTestUtils.usePrivilegedSignatures = false;
|
||||
await testLoadManifest({
|
||||
expectPrivileged: false,
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function test_privileged_signature() {
|
||||
AddonTestUtils.usePrivilegedSignatures = true;
|
||||
await testLoadManifest({
|
||||
expectPrivileged: true,
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function test_system_signature() {
|
||||
AddonTestUtils.usePrivilegedSignatures = "system";
|
||||
await testLoadManifest({
|
||||
expectPrivileged: true,
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function test_builtin_location() {
|
||||
AddonTestUtils.usePrivilegedSignatures = false;
|
||||
await testLoadManifest({
|
||||
expectPrivileged: true,
|
||||
location: getInstallLocation({ isBuiltin: true }),
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function test_system_location() {
|
||||
AddonTestUtils.usePrivilegedSignatures = false;
|
||||
await testLoadManifest({
|
||||
expectPrivileged: false,
|
||||
location: getInstallLocation({ isSystem: true }),
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function test_builtin_system_location() {
|
||||
AddonTestUtils.usePrivilegedSignatures = false;
|
||||
await testLoadManifest({
|
||||
expectPrivileged: true,
|
||||
location: getInstallLocation({ isSystem: true, isBuiltin: true }),
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function test_temporary_regular() {
|
||||
AddonTestUtils.usePrivilegedSignatures = false;
|
||||
Services.prefs.setBoolPref("extensions.experiments.enabled", false);
|
||||
await testLoadManifest({
|
||||
expectPrivileged: false,
|
||||
location: getInstallLocation({ isTemporary: true }),
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function test_temporary_privileged_signature() {
|
||||
AddonTestUtils.usePrivilegedSignatures = true;
|
||||
Services.prefs.setBoolPref("extensions.experiments.enabled", false);
|
||||
await testLoadManifest({
|
||||
expectPrivileged: true,
|
||||
location: getInstallLocation({ isTemporary: true }),
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function test_temporary_experiments_enabled() {
|
||||
AddonTestUtils.usePrivilegedSignatures = false;
|
||||
Services.prefs.setBoolPref("extensions.experiments.enabled", true);
|
||||
await testLoadManifest({
|
||||
expectPrivileged: true,
|
||||
location: getInstallLocation({ isTemporary: true }),
|
||||
});
|
||||
});
|
|
@ -57,6 +57,7 @@ skip-if = appname != "firefox" || (os == "win" && processor == "aarch64") # bug
|
|||
[test_installtrigger_schemes.js]
|
||||
[test_isDebuggable.js]
|
||||
[test_isReady.js]
|
||||
[test_loadManifest_isPrivileged.js]
|
||||
[test_locale.js]
|
||||
[test_moved_extension_metadata.js]
|
||||
skip-if = true
|
||||
|
|
Загрузка…
Ссылка в новой задаче