Bug 1243602 - don't expose browser.* properties that lack required permissions. r=kmag
authorMatthew Wein <mwein@mozilla.com>
Wed, 24 Feb 2016 18:08:59 -0800
changeset 321922 96732e2e7174c1085ed2f81dfc9fdaff10e0e712
parent 321921 9b5fd0f8dbf1223932f7af246f871f6968e13ee1
child 321923 5b53d1dcb00952e51a7f298c9b7cd77b7634e9d2
push id5913
push userjlund@mozilla.com
push dateMon, 25 Apr 2016 16:57:49 +0000
treeherdermozilla-beta@dcaf0a6fa115 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskmag
bugs1243602
milestone47.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1243602 - don't expose browser.* properties that lack required permissions. r=kmag
toolkit/components/extensions/Extension.jsm
toolkit/components/extensions/Schemas.jsm
toolkit/components/extensions/test/mochitest/test_ext_schema.html
toolkit/components/extensions/test/mochitest/test_ext_webnavigation.html
toolkit/components/extensions/test/xpcshell/test_ext_schemas.js
--- a/toolkit/components/extensions/Extension.jsm
+++ b/toolkit/components/extensions/Extension.jsm
@@ -379,19 +379,27 @@ GlobalManager = {
       // does not.
       let injectObject = (name, defaultCallback) => {
         let browserObj = Cu.createObjectIn(contentWindow, {defineAs: name});
 
         let api = Management.generateAPIs(extension, context, Management.apis);
         injectAPI(api, browserObj);
 
         let schemaApi = Management.generateAPIs(extension, context, Management.schemaApis);
+
+        // Add in any extra API namespaces which do not have implementations
+        // outside of their schema file.
+        schemaApi.extensionTypes = {};
+
         function findPath(path) {
           let obj = schemaApi;
           for (let elt of path) {
+            if (!(elt in obj)) {
+              return null;
+            }
             obj = obj[elt];
           }
           return obj;
         }
         let schemaWrapper = {
           get cloneScope() {
             return context.cloneScope;
           },
@@ -418,16 +426,20 @@ GlobalManager = {
                 Cu.reportError(e);
                 promise = Promise.reject({message: "An unexpected error occurred"});
               }
             }
 
             return context.wrapPromise(promise || Promise.resolve(), callback);
           },
 
+          shouldInject(path, name) {
+            return findPath(path) != null;
+          },
+
           getProperty(path, name) {
             return findPath(path)[name];
           },
 
           setProperty(path, name, value) {
             findPath(path)[name] = value;
           },
 
--- a/toolkit/components/extensions/Schemas.jsm
+++ b/toolkit/components/extensions/Schemas.jsm
@@ -1314,17 +1314,19 @@ this.Schemas = {
       }
     });
   },
 
   inject(dest, wrapperFuncs) {
     for (let [namespace, ns] of this.namespaces) {
       let obj = Cu.createObjectIn(dest, {defineAs: namespace});
       for (let [name, entry] of ns) {
-        entry.inject([namespace], name, obj, new Context(wrapperFuncs));
+        if (wrapperFuncs.shouldInject([namespace], name)) {
+          entry.inject([namespace], name, obj, new Context(wrapperFuncs));
+        }
       }
 
       if (!Object.keys(obj).length) {
         delete dest[namespace];
       }
     }
   },
 
--- a/toolkit/components/extensions/test/mochitest/test_ext_schema.html
+++ b/toolkit/components/extensions/test/mochitest/test_ext_schema.html
@@ -11,28 +11,30 @@
 <body>
 
 <script type="text/javascript">
 "use strict";
 
 add_task(function* testEmptySchema() {
   function background() {
     browser.test.assertTrue(!("manifest" in browser), "browser.manifest is not defined");
+    browser.test.assertTrue("storage" in browser, "browser.storage should be defined");
+    browser.test.assertTrue(!("contextMenus" in browser), "browser.contextMenus should not be defined");
     browser.test.notifyPass("schema");
   }
 
   let extension = ExtensionTestUtils.loadExtension({
     background: `(${background})()`,
+    manifest: {
+      permissions: ["storage"],
+    },
   });
 
-
   yield extension.startup();
-
   yield extension.awaitFinish("schema");
-
   yield extension.unload();
 });
 
 add_task(function* testUnknownProperties() {
   function background() {
     browser.test.notifyPass("loaded");
   }
 
--- a/toolkit/components/extensions/test/mochitest/test_ext_webnavigation.html
+++ b/toolkit/components/extensions/test/mochitest/test_ext_webnavigation.html
@@ -57,17 +57,17 @@ function backgroundScript() {
   }
 
   let listeners = {};
   for (let event of EVENTS) {
     listeners[event] = gotEvent.bind(null, event);
     browser.webNavigation[event].addListener(listeners[event]);
   }
 
-  browser.test.sendMessage("ready", browser.webRequest.ResourceType);
+  browser.test.sendMessage("ready");
 }
 
 const BASE = "http://mochi.test:8888/tests/toolkit/components/extensions/test/mochitest";
 const URL = BASE + "/file_WebNavigation_page1.html";
 const FRAME = BASE + "/file_WebNavigation_page2.html";
 const FRAME2 = BASE + "/file_WebNavigation_page3.html";
 const FRAME_PUSHSTATE = BASE + "/file_WebNavigation_page3_pushState.html";
 
--- a/toolkit/components/extensions/test/xpcshell/test_ext_schemas.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_schemas.js
@@ -276,16 +276,28 @@ let json = [
        type: "function",
        extraParameters: [{
          name: "filter",
          type: "integer",
        }],
      },
    ],
   },
+  {
+    namespace: "inject",
+    properties: {
+      PROP1: {value: "should inject"},
+    },
+  },
+  {
+    namespace: "do-not-inject",
+    properties: {
+      PROP1: {value: "should not inject"},
+    },
+  },
 ];
 
 let tallied = null;
 
 function tally(kind, ns, name, args) {
   tallied = [kind, ns, name, args];
 }
 
@@ -317,16 +329,21 @@ let wrapper = {
     talliedErrors.push(message);
   },
 
   callFunction(path, name, args) {
     let ns = path.join(".");
     tally("call", ns, name, args);
   },
 
+  shouldInject(path) {
+    let ns = path.join(".");
+    return ns != "do-not-inject";
+  },
+
   getProperty(path, name) {
     let ns = path.join(".");
     tally("get", ns, name);
   },
 
   setProperty(path, name, value) {
     let ns = path.join(".");
     tally("set", ns, name, value);
@@ -353,16 +370,19 @@ add_task(function* () {
 
   let root = {};
   Schemas.inject(root, wrapper);
 
   do_check_eq(root.testing.PROP1, 20, "simple value property");
   do_check_eq(root.testing.type1.VALUE1, "value1", "enum type");
   do_check_eq(root.testing.type1.VALUE2, "value2", "enum type");
 
+  do_check_eq("inject" in root, true, "namespace 'inject' should be injected");
+  do_check_eq("do-not-inject" in root, false, "namespace 'do-not-inject' should not be injected");
+
   root.testing.foo(11, true);
   verify("call", "testing", "foo", [11, true]);
 
   root.testing.foo(true);
   verify("call", "testing", "foo", [null, true]);
 
   root.testing.foo(null, true);
   verify("call", "testing", "foo", [null, true]);