Bug 1402850 Don't include runtime permissions in prompts for webextension updates r=zombie

MozReview-Commit-ID: 1cnNsWLVGmg
This commit is contained in:
Andrew Swan
2017-11-08 17:14:11 -08:00
parent 01b62940da
commit d3734cd97e
6 changed files with 136 additions and 20 deletions

View File

@@ -24,7 +24,7 @@
},
{
"namespace": "devtools",
"permissions": ["devtools"],
"permissions": ["manifest:devtools_page"],
"allowedContexts": ["devtools", "devtools_only"],
"defaultContexts": ["devtools", "devtools_only"]
}

View File

@@ -423,12 +423,51 @@ class ExtensionData {
});
}
// This method should return a structured representation of any
// capabilities this extension has access to, as derived from the
// manifest. The current implementation just returns the contents
// of the permissions attribute, if we add things like url_overrides,
// they should also be added here.
get userPermissions() {
/**
* Returns an object representing any capabilities that the extension
* has access to based on fixed properties in the manifest. The result
* includes the contents of the "permissions" property as well as other
* capabilities that are derived from manifest fields that users should
* be informed of (e.g., origins where content scripts are injected).
*/
get manifestPermissions() {
if (this.type !== "extension") {
return null;
}
let permissions = new Set();
let origins = new Set();
for (let perm of this.manifest.permissions || []) {
let type = classifyPermission(perm);
if (type.origin) {
origins.add(perm);
} else if (!type.api) {
permissions.add(perm);
}
}
if (this.manifest.devtools_page) {
permissions.add("devtools");
}
for (let entry of this.manifest.content_scripts || []) {
for (let origin of entry.matches) {
origins.add(origin);
}
}
return {
permissions: Array.from(permissions),
origins: Array.from(origins),
};
}
/**
* Returns an object representing all capabilities this extension has
* access to, including fixed ones from the manifest as well as dynamically
* granted permissions.
*/
get activePermissions() {
if (this.type !== "extension") {
return null;
}
@@ -438,11 +477,6 @@ class ExtensionData {
apis: [...this.apiNames],
};
if (Array.isArray(this.manifest.content_scripts)) {
for (let entry of this.manifest.content_scripts) {
result.origins.push(...entry.matches);
}
}
const EXP_PATTERN = /^experiments\.\w+/;
result.permissions = [...this.permissions]
.filter(p => !result.origins.includes(p) && !EXP_PATTERN.test(p));
@@ -542,10 +576,6 @@ class ExtensionData {
};
if (this.type === "extension") {
if (this.manifest.devtools_page) {
permissions.add("devtools");
}
for (let perm of manifest.permissions) {
if (perm === "geckoProfiler" && !this.isPrivileged) {
const acceptedExtensions = Services.prefs.getStringPref("extensions.geckoProfiler.acceptedExtensionIds", "");

View File

@@ -64,7 +64,7 @@ this.permissions = class extends ExtensionAPI {
},
async getAll() {
let perms = context.extension.userPermissions;
let perms = context.extension.activePermissions;
delete perms.apis;
return perms;
},

View File

@@ -1,7 +1,9 @@
"use strict";
ChromeUtils.import("resource://gre/modules/AddonManager.jsm");
ChromeUtils.import("resource://gre/modules/ExtensionPermissions.jsm");
ChromeUtils.import("resource://gre/modules/MessageChannel.jsm");
ChromeUtils.import("resource://gre/modules/osfile.jsm");
const BROWSER_PROPERTIES = "chrome://browser/locale/browser.properties";
@@ -435,3 +437,88 @@ add_task(function test_permissions_have_localization_strings() {
}
}
});
// Check that optional permissions are not included in update prompts
add_task(async function test_permissions_prompt() {
function background() {
browser.test.onMessage.addListener(async (msg, arg) => {
if (msg == "request") {
let result = await browser.permissions.request(arg);
browser.test.sendMessage("result", result);
}
});
}
let extension = ExtensionTestUtils.loadExtension({
background,
manifest: {
name: "permissions test",
description: "permissions test",
manifest_version: 2,
version: "1.0",
permissions: ["tabs", "https://test1.example.com/*"],
optional_permissions: ["clipboardWrite", "<all_urls>"],
content_scripts: [
{
matches: ["https://test2.example.com/*"],
js: [],
},
],
},
useAddonManager: "permanent",
});
await extension.startup();
await withHandlingUserInput(extension, async () => {
extension.sendMessage("request", {
permissions: ["clipboardWrite"],
origins: ["https://test2.example.com/*"],
});
let result = await extension.awaitMessage("result");
equal(result, true, "request() for optional permissions succeeded");
});
const PERMS = ["history", "tabs"];
const ORIGINS = ["https://test1.example.com/*", "https://test3.example.com/"];
let xpi = Extension.generateXPI({
background,
manifest: {
name: "permissions test",
description: "permissions test",
manifest_version: 2,
version: "2.0",
applications: {gecko: {id: extension.id}},
permissions: [...PERMS, ...ORIGINS],
optional_permissions: ["clipboardWrite", "<all_urls>"],
},
});
let install = await AddonManager.getInstallForFile(xpi);
Services.prefs.setBoolPref("extensions.webextPermissionPrompts", true);
registerCleanupFunction(() => {
Services.prefs.clearUserPref("extensions.webextPermissionPrompts");
});
let perminfo;
install.promptHandler = info => {
perminfo = info;
return Promise.resolve();
};
await AddonTestUtils.promiseCompleteInstall(install);
await extension.awaitStartup();
notEqual(perminfo, undefined, "Permission handler was invoked");
let perms = perminfo.addon.userPermissions;
deepEqual(perms.permissions, PERMS, "Update details includes only manifest api permissions");
deepEqual(perms.origins, ORIGINS, "Update details includes only manifest origin permissions");
await extension.unload();
await OS.File.remove(xpi.path);
});

View File

@@ -375,7 +375,7 @@ async function loadManifestFromWebManifest(aUri) {
addon.iconURL = null;
addon.icon64URL = null;
addon.icons = manifest.icons || {};
addon.userPermissions = extension.userPermissions;
addon.userPermissions = extension.manifestPermissions;
addon.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DEFAULT;

View File

@@ -498,7 +498,7 @@ add_task(async function test_permissions_prompt() {
manifest_version: 2,
version: "1.0",
permissions: ["tabs", "storage", "https://*.example.com/*", "<all_urls>", "experiments.test"],
permissions: ["tabs", "storage", "https://*.example.com/*", "<all_urls>"],
};
let xpi = ExtensionTestCommon.generateXPI({manifest});
@@ -519,7 +519,6 @@ add_task(async function test_permissions_prompt() {
let perms = perminfo.addon.userPermissions;
deepEqual(perms.permissions, ["tabs", "storage"], "API permissions are correct");
deepEqual(perms.origins, ["https://*.example.com/*", "<all_urls>"], "Host permissions are correct");
deepEqual(perms.apis, ["test"], "Experiments permissions are correct");
let addon = await promiseAddonByID(perminfo.addon.id);
notEqual(addon, null, "Extension was installed");