Bug 1693385 - Support manifest.host_permissions for MV3 r=mixedpuppy

Differential Revision: https://phabricator.services.mozilla.com/D118305
This commit is contained in:
Tomislav Jovanovic 2021-07-24 12:44:03 +00:00
parent 38cb903038
commit 11f130af70
6 changed files with 110 additions and 42 deletions

View File

@ -699,13 +699,15 @@ class ExtensionData {
* that contains seperate host origins and permissions arrays.
*
* @param {Array} permissionsArray
* @param {Array} [hostPermissions]
* @returns {Object} permissions object
*/
permissionsObject(permissionsArray) {
permissionsObject(permissionsArray = [], hostPermissions = []) {
let permissions = new Set();
let origins = new Set();
let { restrictSchemes, isPrivileged } = this;
for (let perm of permissionsArray || []) {
for (let perm of permissionsArray.concat(hostPermissions)) {
let type = classifyPermission(perm, restrictSchemes, isPrivileged);
if (type.origin) {
origins.add(perm);
@ -713,6 +715,7 @@ class ExtensionData {
permissions.add(perm);
}
}
return {
permissions,
origins,
@ -732,7 +735,8 @@ class ExtensionData {
}
let { permissions, origins } = this.permissionsObject(
this.manifest.permissions
this.manifest.permissions,
this.manifest.host_permissions
);
if (
@ -1038,7 +1042,9 @@ class ExtensionData {
isPrivileged && manifest.permissions.includes("mozillaAddons")
);
for (let perm of manifest.permissions) {
let host_permissions = manifest.host_permissions ?? [];
for (let perm of manifest.permissions.concat(host_permissions)) {
if (perm === "geckoProfiler" && !isPrivileged) {
const acceptedExtensions = Services.prefs.getStringPref(
"extensions.geckoProfiler.acceptedExtensionIds",
@ -1310,6 +1316,7 @@ class ExtensionData {
await this.apiManager.lazyInit();
this.webAccessibleResources = manifestData.webAccessibleResources;
this.allowedOrigins = new MatchPatternSet(manifestData.originPermissions, {
restrictSchemes: this.restrictSchemes,
});

View File

@ -277,6 +277,15 @@ ExtensionTestCommon = class ExtensionTestCommon {
provide(manifest, ["manifest_version"], 2);
provide(manifest, ["version"], "1.0");
// Make it easier to test same manifest in both MV2 and MV3 configurations.
if (manifest.manifest_version === 2 && manifest.host_permissions) {
manifest.permissions = [].concat(
manifest.permissions || [],
manifest.host_permissions
);
delete manifest.host_permissions;
}
if (data.background) {
let bgScript = uuidGen.generateUUID().number + ".js";

View File

@ -213,13 +213,37 @@
},
"permissions": {
"type": "array",
"default": [],
"optional": true,
"choices": [
{
"max_manifest_version": 2,
"type": "array",
"items": {
"$ref": "PermissionOrOrigin",
"onError": "warn"
}
},
{
"min_manifest_version": 3,
"type": "array",
"items": {
"$ref": "Permission",
"onError": "warn"
}
}
]
},
"host_permissions": {
"min_manifest_version": 3,
"type": "array",
"items": {
"$ref": "PermissionOrOrigin",
"$ref": "MatchPattern",
"onError": "warn"
},
"optional": true
"optional": true,
"default": []
},
"optional_permissions": {

View File

@ -403,7 +403,8 @@ async function runCSPTest(test) {
js: ["content_script.js"],
},
],
permissions: ["webRequest", "webRequestBlocking", "<all_urls>"],
permissions: ["webRequest", "webRequestBlocking"],
host_permissions: ["<all_urls>"],
background: { scripts: ["background.js"] },
},
files: {

View File

@ -23,6 +23,8 @@ AddonTestUtils.overrideCertDB();
AddonTestUtils.usePrivilegedSignatures = id => id.startsWith("privileged");
createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "42");
Services.prefs.setBoolPref("extensions.manifestV3.enabled", true);
async function getManifestPermissions(extensionData) {
let extension = ExtensionTestCommon.generate(extensionData);
// Some tests contain invalid permissions; ignore the warnings about their invalidity.
@ -341,30 +343,40 @@ add_task(async function host_permissions() {
],
},
];
for (let {
description,
manifest,
expectedOrigins,
expectedWarnings,
options,
} of permissionTestCases) {
let manifestPermissions = await getManifestPermissions({
for (let manifest_version of [2, 3]) {
for (let {
description,
manifest,
});
deepEqual(
manifestPermissions.origins,
expectedOrigins,
`Expected origins (${description})`
);
deepEqual(
manifestPermissions.permissions,
[],
`Expected no non-host permissions (${description})`
);
expectedWarnings,
options,
} of permissionTestCases) {
manifest = Object.assign({}, manifest, { manifest_version });
if (manifest_version > 2) {
manifest.host_permissions = manifest.permissions;
manifest.permissions = [];
}
let warnings = getPermissionWarnings(manifestPermissions, options);
deepEqual(warnings, expectedWarnings, `Expected warnings (${description})`);
let manifestPermissions = await getManifestPermissions({ manifest });
deepEqual(
manifestPermissions.origins,
expectedOrigins,
`Expected origins (${description})`
);
deepEqual(
manifestPermissions.permissions,
[],
`Expected no non-host permissions (${description})`
);
let warnings = getPermissionWarnings(manifestPermissions, options);
deepEqual(
warnings,
expectedWarnings,
`Expected warnings (${description})`
);
}
}
});

View File

@ -7,6 +7,8 @@ const { ExtensionPermissions } = ChromeUtils.import(
"resource://gre/modules/ExtensionPermissions.jsm"
);
Services.prefs.setBoolPref("extensions.manifestV3.enabled", true);
// ExtensionParent.jsm is being imported lazily because when it is imported Services.appinfo will be
// retrieved and cached (as a side-effect of Schemas.jsm being imported), and so Services.appinfo
// will not be returning the version set by AddonTestUtils.createAppInfo and this test will
@ -105,7 +107,7 @@ add_task(async function test_permissions_on_startup() {
await extension.unload();
});
add_task(async function test_permissions() {
async function test_permissions(manifest_version) {
const REQUIRED_PERMISSIONS = ["downloads"];
const REQUIRED_ORIGINS = ["*://site.com/", "*://*.domain.com/"];
const REQUIRED_ORIGINS_NORMALIZED = ["*://site.com/*", "*://*.domain.com/*"];
@ -153,7 +155,9 @@ add_task(async function test_permissions() {
let extension = ExtensionTestUtils.loadExtension({
background,
manifest: {
permissions: [...REQUIRED_PERMISSIONS, ...REQUIRED_ORIGINS],
manifest_version,
permissions: REQUIRED_PERMISSIONS,
host_permissions: REQUIRED_ORIGINS,
optional_permissions: [...OPTIONAL_PERMISSIONS, ...OPTIONAL_ORIGINS],
},
useAddonManager: "permanent",
@ -339,7 +343,9 @@ add_task(async function test_permissions() {
deepEqual(result, perms, "Back to default permissions after removing more");
await extension.unload();
});
}
add_task(() => test_permissions(2));
add_task(() => test_permissions(3));
add_task(async function test_startup() {
async function background() {
@ -413,14 +419,15 @@ add_task(async function test_startup() {
});
// Test that we don't prompt for permissions an extension already has.
add_task(async function test_alreadyGranted() {
const REQUIRED_PERMISSIONS = [
"geolocation",
async function test_alreadyGranted(manifest_version) {
const REQUIRED_PERMISSIONS = ["geolocation"];
const REQUIRED_ORIGINS = [
"*://required-host.com/",
"*://*.required-domain.com/",
];
const OPTIONAL_PERMISSIONS = [
...REQUIRED_PERMISSIONS,
...REQUIRED_ORIGINS,
"clipboardRead",
"*://optional-host.com/",
"*://*.optional-domain.com/",
@ -448,7 +455,9 @@ add_task(async function test_alreadyGranted() {
},
manifest: {
manifest_version,
permissions: REQUIRED_PERMISSIONS,
host_permissions: REQUIRED_ORIGINS,
optional_permissions: OPTIONAL_PERMISSIONS,
},
@ -537,7 +546,9 @@ add_task(async function test_alreadyGranted() {
});
await extension.unload();
});
}
add_task(() => test_alreadyGranted(2));
add_task(() => test_alreadyGranted(3));
// IMPORTANT: Do not change this list without review from a Web Extensions peer!
@ -638,7 +649,7 @@ add_task(async function test_optional_all_urls() {
});
// Check that optional permissions are not included in update prompts
add_task(async function test_permissions_prompt() {
async function test_permissions_prompt(manifest_version) {
function background() {
browser.test.onMessage.addListener(async (msg, arg) => {
if (msg == "request") {
@ -653,10 +664,11 @@ add_task(async function test_permissions_prompt() {
manifest: {
name: "permissions test",
description: "permissions test",
manifest_version: 2,
manifest_version,
version: "1.0",
permissions: ["tabs", "https://test1.example.com/*"],
permissions: ["tabs"],
host_permissions: ["https://test1.example.com/*"],
optional_permissions: ["clipboardWrite", "<all_urls>"],
content_scripts: [
@ -687,12 +699,13 @@ add_task(async function test_permissions_prompt() {
manifest: {
name: "permissions test",
description: "permissions test",
manifest_version: 2,
manifest_version,
version: "2.0",
applications: { gecko: { id: extension.id } },
permissions: [...PERMS, ...ORIGINS],
permissions: PERMS,
host_permissions: ORIGINS,
optional_permissions: ["clipboardWrite", "<all_urls>"],
},
});
@ -727,7 +740,9 @@ add_task(async function test_permissions_prompt() {
);
await extension.unload();
});
}
add_task(() => test_permissions_prompt(2));
add_task(() => test_permissions_prompt(3));
// Check that internal permissions can not be set and are not returned by the API.
add_task(async function test_internal_permissions() {