mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-02 18:08:58 +00:00
Bug 1888975 - Move common optional permissions validation to ExtensionPermissions.sys.mjs r=willdurand
Differential Revision: https://phabricator.services.mozilla.com/D206722
This commit is contained in:
parent
90377d20c4
commit
e0900dc042
@ -13,6 +13,7 @@ const lazy = {};
|
|||||||
ChromeUtils.defineESModuleGetters(lazy, {
|
ChromeUtils.defineESModuleGetters(lazy, {
|
||||||
AddonManager: "resource://gre/modules/AddonManager.sys.mjs",
|
AddonManager: "resource://gre/modules/AddonManager.sys.mjs",
|
||||||
AddonManagerPrivate: "resource://gre/modules/AddonManager.sys.mjs",
|
AddonManagerPrivate: "resource://gre/modules/AddonManager.sys.mjs",
|
||||||
|
Extension: "resource://gre/modules/Extension.sys.mjs",
|
||||||
ExtensionParent: "resource://gre/modules/ExtensionParent.sys.mjs",
|
ExtensionParent: "resource://gre/modules/ExtensionParent.sys.mjs",
|
||||||
FileUtils: "resource://gre/modules/FileUtils.sys.mjs",
|
FileUtils: "resource://gre/modules/FileUtils.sys.mjs",
|
||||||
JSONFile: "resource://gre/modules/JSONFile.sys.mjs",
|
JSONFile: "resource://gre/modules/JSONFile.sys.mjs",
|
||||||
@ -347,6 +348,50 @@ export var ExtensionPermissions = {
|
|||||||
return this._getCached(extensionId);
|
return this._getCached(extensionId);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate and normalize passed in `perms`, including a fixup to
|
||||||
|
* include all possible "all sites" permissions when appropriate.
|
||||||
|
*
|
||||||
|
* @throws if an origin or permission is not part of optional permissions.
|
||||||
|
*
|
||||||
|
* @typedef {object} Perms
|
||||||
|
* @property {string[]} origins
|
||||||
|
* @property {string[]} permissions
|
||||||
|
*
|
||||||
|
* @param {Perms} perms api permissions and origins to be added/removed.
|
||||||
|
* @param {Perms} optional permissions and origins from the manifest.
|
||||||
|
* @returns {Perms} normalized
|
||||||
|
*/
|
||||||
|
normalizeOptional(perms, optional) {
|
||||||
|
let allSites = false;
|
||||||
|
let patterns = new MatchPatternSet(optional.origins, { ignorePath: true });
|
||||||
|
let normalized = Object.assign({}, perms);
|
||||||
|
|
||||||
|
for (let o of perms.origins) {
|
||||||
|
if (!patterns.subsumes(new MatchPattern(o))) {
|
||||||
|
throw new Error(`${o} was not declared in the manifest`);
|
||||||
|
}
|
||||||
|
// If this is one of the "all sites" permissions
|
||||||
|
allSites ||= lazy.Extension.isAllSitesPermission(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allSites) {
|
||||||
|
// Grant/revoke ALL "all sites" optional permissions from the manifest.
|
||||||
|
let origins = perms.origins.concat(
|
||||||
|
optional.origins.filter(o => lazy.Extension.isAllSitesPermission(o))
|
||||||
|
);
|
||||||
|
normalized.origins = Array.from(new Set(origins));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let p of perms.permissions) {
|
||||||
|
if (!optional.permissions.includes(p)) {
|
||||||
|
throw new Error(`${p} was not declared in optional_permissions`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return normalized;
|
||||||
|
},
|
||||||
|
|
||||||
_fixupAllUrlsPerms(perms) {
|
_fixupAllUrlsPerms(perms) {
|
||||||
// Unfortunately, we treat <all_urls> as an API permission as well.
|
// Unfortunately, we treat <all_urls> as an API permission as well.
|
||||||
// If it is added to either, ensure it is added to both.
|
// If it is added to either, ensure it is added to both.
|
||||||
@ -362,7 +407,7 @@ export var ExtensionPermissions = {
|
|||||||
* in the format that is passed to browser.permissions.request().
|
* in the format that is passed to browser.permissions.request().
|
||||||
*
|
*
|
||||||
* @param {string} extensionId The extension id
|
* @param {string} extensionId The extension id
|
||||||
* @param {object} perms Object with permissions and origins array.
|
* @param {Perms} perms Object with permissions and origins array.
|
||||||
* @param {EventEmitter} [emitter] optional object implementing emitter interfaces
|
* @param {EventEmitter} [emitter] optional object implementing emitter interfaces
|
||||||
*/
|
*/
|
||||||
async add(extensionId, perms, emitter) {
|
async add(extensionId, perms, emitter) {
|
||||||
@ -401,7 +446,7 @@ export var ExtensionPermissions = {
|
|||||||
* in the format that is passed to browser.permissions.request().
|
* in the format that is passed to browser.permissions.request().
|
||||||
*
|
*
|
||||||
* @param {string} extensionId The extension id
|
* @param {string} extensionId The extension id
|
||||||
* @param {object} perms Object with permissions and origins array.
|
* @param {Perms} perms Object with permissions and origins array.
|
||||||
* @param {EventEmitter} [emitter] optional object implementing emitter interfaces
|
* @param {EventEmitter} [emitter] optional object implementing emitter interfaces
|
||||||
*/
|
*/
|
||||||
async remove(extensionId, perms, emitter) {
|
async remove(extensionId, perms, emitter) {
|
||||||
|
@ -1059,3 +1059,47 @@ add_task(async function test_internal_permissions() {
|
|||||||
|
|
||||||
await extension.unload();
|
await extension.unload();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
add_task(function test_normalizeOptional() {
|
||||||
|
const optional1 = {
|
||||||
|
origins: ["*://site.com/", "*://*.domain.com/"],
|
||||||
|
permissions: ["downloads", "tabs"],
|
||||||
|
};
|
||||||
|
|
||||||
|
function normalize(perms, optional) {
|
||||||
|
perms = { origins: [], permissions: [], ...perms };
|
||||||
|
optional = { origins: [], permissions: [], ...optional };
|
||||||
|
return ExtensionPermissions.normalizeOptional(perms, optional);
|
||||||
|
}
|
||||||
|
|
||||||
|
normalize({ origins: ["http://site.com/"] }, optional1);
|
||||||
|
normalize({ origins: ["https://site.com/"] }, optional1);
|
||||||
|
normalize({ origins: ["*://blah.domain.com/"] }, optional1);
|
||||||
|
normalize({ permissions: ["downloads", "tabs"] }, optional1);
|
||||||
|
|
||||||
|
Assert.throws(
|
||||||
|
() => normalize({ origins: ["http://www.example.com/"] }, optional1),
|
||||||
|
/was not declared in the manifest/
|
||||||
|
);
|
||||||
|
Assert.throws(
|
||||||
|
() => normalize({ permissions: ["proxy"] }, optional1),
|
||||||
|
/was not declared in optional_permissions/
|
||||||
|
);
|
||||||
|
|
||||||
|
const optional2 = {
|
||||||
|
origins: ["<all_urls>", "*://*/*"],
|
||||||
|
permissions: ["idle", "clipboardWrite"],
|
||||||
|
};
|
||||||
|
|
||||||
|
normalize({ origins: ["http://site.com/"] }, optional2);
|
||||||
|
normalize({ origins: ["https://site.com/"] }, optional2);
|
||||||
|
normalize({ origins: ["*://blah.domain.com/"] }, optional2);
|
||||||
|
normalize({ permissions: ["idle", "clipboardWrite"] }, optional2);
|
||||||
|
|
||||||
|
let perms = normalize({ origins: ["<all_urls>"] }, optional2);
|
||||||
|
equal(
|
||||||
|
perms.origins.sort().join(),
|
||||||
|
optional2.origins.sort().join(),
|
||||||
|
`Expect both "all sites" permissions`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
@ -2451,45 +2451,27 @@ class AddonCard extends HTMLElement {
|
|||||||
|
|
||||||
async setAddonPermission(permission, type, action) {
|
async setAddonPermission(permission, type, action) {
|
||||||
let { addon } = this;
|
let { addon } = this;
|
||||||
let origins = [],
|
let perms = { origins: [], permissions: [] };
|
||||||
permissions = [];
|
|
||||||
if (!["add", "remove"].includes(action)) {
|
if (!["add", "remove"].includes(action)) {
|
||||||
throw new Error("invalid action for permission change");
|
throw new Error("invalid action for permission change");
|
||||||
}
|
}
|
||||||
if (type == "permission") {
|
|
||||||
if (
|
|
||||||
action == "add" &&
|
|
||||||
!addon.optionalPermissions.permissions.includes(permission)
|
|
||||||
) {
|
|
||||||
throw new Error("permission missing from manifest");
|
|
||||||
}
|
|
||||||
permissions = [permission];
|
|
||||||
} else if (type == "origin") {
|
|
||||||
if (action === "add") {
|
|
||||||
let { origins } = addon.optionalPermissions;
|
|
||||||
let patternSet = new MatchPatternSet(origins, { ignorePath: true });
|
|
||||||
if (!patternSet.subsumes(new MatchPattern(permission))) {
|
|
||||||
throw new Error("origin missing from manifest");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
origins = [permission];
|
|
||||||
|
|
||||||
// If this is one of the "all sites" permissions
|
if (type === "permission") {
|
||||||
if (Extension.isAllSitesPermission(permission)) {
|
perms.permissions = [permission];
|
||||||
// Grant/revoke ALL "all sites" optional permissions from the manifest.
|
} else if (type === "origin") {
|
||||||
origins = addon.optionalPermissions.origins.filter(perm =>
|
perms.origins = [permission];
|
||||||
Extension.isAllSitesPermission(perm)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
throw new Error("unknown permission type changed");
|
throw new Error("unknown permission type changed");
|
||||||
}
|
}
|
||||||
let policy = WebExtensionPolicy.getByID(addon.id);
|
|
||||||
ExtensionPermissions[action](
|
let normalized = ExtensionPermissions.normalizeOptional(
|
||||||
addon.id,
|
perms,
|
||||||
{ origins, permissions },
|
addon.optionalPermissions
|
||||||
policy?.extension
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let policy = WebExtensionPolicy.getByID(addon.id);
|
||||||
|
ExtensionPermissions[action](addon.id, normalized, policy?.extension);
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleEvent(e) {
|
async handleEvent(e) {
|
||||||
|
@ -613,7 +613,7 @@ add_task(async function testPermissionsViewStates() {
|
|||||||
let card = getAddonCard(view, addon.id);
|
let card = getAddonCard(view, addon.id);
|
||||||
await Assert.rejects(
|
await Assert.rejects(
|
||||||
card.setAddonPermission("webRequest", "permission", "add"),
|
card.setAddonPermission("webRequest", "permission", "add"),
|
||||||
/permission missing from manifest/,
|
/was not declared in optional_permissions/,
|
||||||
"unable to set the addon permission"
|
"unable to set the addon permission"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user