Bug 1298939: Don't initialize APIs that the extension does not have permissions for. r?rpl
MozReview-Commit-ID: Y0MTBL1z2O
--- a/toolkit/components/extensions/Extension.jsm
+++ b/toolkit/components/extensions/Extension.jsm
@@ -195,20 +195,18 @@ var Management = {
}
}
}
for (let api of apis) {
if (namespaces && !namespaces.includes(api.namespace)) {
continue;
}
- if (api.permission) {
- if (!context.extension.hasPermission(api.permission)) {
- continue;
- }
+ if (!Schemas.checkPermissions(api.namespace, context.extension)) {
+ continue;
}
api = api.getAPI(context);
copy(obj, api);
}
},
// The ext-*.js scripts can ask to be notified for certain hooks.
@@ -621,16 +619,19 @@ GlobalManager = {
injectInObject(context, defaultCallback, dest, namespaces = null) {
let apis = {
extensionTypes: {},
};
Management.generateAPIs(context, Management.schemaApis, apis, namespaces);
Management.generateAPIs(context, context.extension.apis, apis, namespaces);
+ // For testing only.
+ context._unwrappedAPIs = apis;
+
let schemaWrapper = {
get principal() {
return context.principal;
},
get cloneScope() {
return context.cloneScope;
},
--- a/toolkit/components/extensions/Schemas.jsm
+++ b/toolkit/components/extensions/Schemas.jsm
@@ -1804,16 +1804,24 @@ this.Schemas = {
let data = Services.ppmm.initialProcessData;
data["Extension:Schemas"] = this.schemaJSON;
Services.ppmm.broadcastAsyncMessage("Schema:Delete", {url});
this.flushSchemas();
},
+ checkPermissions(namespace, wrapperFuncs) {
+ let ns = this.namespaces.get(namespace);
+ if (ns && ns.permissions) {
+ return ns.permissions.some(perm => wrapperFuncs.hasPermission(perm));
+ }
+ return true;
+ },
+
/**
* Inject registered extension APIs into `dest`.
*
* @param {object} dest The root namespace for the APIs.
* This object is usually exposed to extensions as "chrome" or "browser".
* @param {object} wrapperFuncs An implementation of the InjectionContext
* interface, which runs the actual functionality of the generated API.
*/
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_api_permissions.js
@@ -0,0 +1,41 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+add_task(function* test_storage_api_without_permissions() {
+ let extension = ExtensionTestUtils.loadExtension({
+ background() {},
+
+ manifest: {
+ permissions: [],
+ },
+ });
+
+ yield extension.startup();
+
+ let context = Array.from(extension.extension.views)[0];
+
+ ok(!("storage" in context._unwrappedAPIs),
+ "The storage API should not be initialized");
+
+ yield extension.unload();
+});
+
+add_task(function* test_storage_api_with_permissions() {
+ let extension = ExtensionTestUtils.loadExtension({
+ background() {},
+
+ manifest: {
+ permissions: ["storage"],
+ },
+ });
+
+ yield extension.startup();
+
+ let context = Array.from(extension.extension.views)[0];
+
+ equal(typeof context._unwrappedAPIs.storage, "object",
+ "The storage API should be initialized");
+
+ yield extension.unload();
+});
--- a/toolkit/components/extensions/test/xpcshell/xpcshell.ini
+++ b/toolkit/components/extensions/test/xpcshell/xpcshell.ini
@@ -8,16 +8,17 @@ support-files =
tags = webextensions
[test_csp_custom_policies.js]
[test_csp_validator.js]
[test_ext_alarms.js]
[test_ext_alarms_does_not_fire.js]
[test_ext_alarms_periodic.js]
[test_ext_alarms_replaces.js]
+[test_ext_api_permissions.js]
[test_ext_background_generated_load_events.js]
[test_ext_background_generated_reload.js]
[test_ext_background_global_history.js]
skip-if = os == "android" # Android does not use Places for history.
[test_ext_background_runtime_connect_params.js]
[test_ext_background_sub_windows.js]
[test_ext_background_window_properties.js]
skip-if = os == "android"