diff --git a/addon-sdk/source/lib/sdk/content/sandbox.js b/addon-sdk/source/lib/sdk/content/sandbox.js index e8c9a1533c6..7fb88838848 100644 --- a/addon-sdk/source/lib/sdk/content/sandbox.js +++ b/addon-sdk/source/lib/sdk/content/sandbox.js @@ -38,6 +38,12 @@ const metadata = require('@loader/options').metadata; const permissions = (metadata && metadata['permissions']) || {}; const EXPANDED_PRINCIPALS = permissions['cross-domain-content'] || []; +const waiveSecurityMembrane = !!permissions['unsafe-content-script']; + +const nsIScriptSecurityManager = Ci.nsIScriptSecurityManager; +const secMan = Cc["@mozilla.org/scriptsecuritymanager;1"]. + getService(Ci.nsIScriptSecurityManager); + const JS_VERSION = '1.8'; const WorkerSandbox = Class({ @@ -96,8 +102,10 @@ const WorkerSandbox = Class({ this.emit = this.emit.bind(this); this.emitSync = this.emitSync.bind(this); - // Eventually use expanded principal sandbox feature, if some are given. - // + // Use expanded principal for content-script if the content is a + // regular web content for better isolation. + // (This behavior can be turned off for now with the unsafe-content-script + // flag to give addon developers time for making the necessary changes) // But prevent it when the Worker isn't used for a content script but for // injecting `addon` object into a Panel, Widget, ... scope. // That's because: @@ -110,12 +118,17 @@ const WorkerSandbox = Class({ // domain principal. let principals = window; let wantGlobalProperties = []; - if (EXPANDED_PRINCIPALS.length > 0 && !requiresAddonGlobal(worker)) { - principals = EXPANDED_PRINCIPALS.concat(window); - // We have to replace XHR constructor of the content document - // with a custom cross origin one, automagically added by platform code: - delete proto.XMLHttpRequest; - wantGlobalProperties.push('XMLHttpRequest'); + let isSystemPrincipal = secMan.isSystemPrincipal( + window.document.nodePrincipal); + if (!isSystemPrincipal && !requiresAddonGlobal(worker)) { + if (EXPANDED_PRINCIPALS.length > 0) { + // We have to replace XHR constructor of the content document + // with a custom cross origin one, automagically added by platform code: + delete proto.XMLHttpRequest; + wantGlobalProperties.push('XMLHttpRequest'); + } + if (!waiveSecurityMembrane) + principals = EXPANDED_PRINCIPALS.concat(window); } // Instantiate trusted code in another Sandbox in order to prevent content @@ -129,6 +142,7 @@ const WorkerSandbox = Class({ sandboxPrototype: proto, wantXrays: true, wantGlobalProperties: wantGlobalProperties, + wantExportHelpers: !waiveSecurityMembrane, sameZoneAs: window, metadata: { SDKContentScript: true, diff --git a/addon-sdk/source/test/addons/unsafe-content-script/main.js b/addon-sdk/source/test/addons/unsafe-content-script/main.js new file mode 100644 index 00000000000..81a2359af4b --- /dev/null +++ b/addon-sdk/source/test/addons/unsafe-content-script/main.js @@ -0,0 +1,61 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +const { create: makeFrame } = require("sdk/frame/utils"); +const { window } = require("sdk/addon/window"); +const { Loader } = require('sdk/test/loader'); +const loader = Loader(module); +const Worker = loader.require("sdk/content/worker").Worker; + +exports.testMembranelessMode = function(assert, done) { + + let url = "data:text/html;charset=utf-8," + encodeURIComponent( + '' + ); + + let element = makeFrame(window.document, { + nodeName: "iframe", + type: "content", + allowJavascript: true, + allowPlugins: true, + allowAuth: true, + uri: url + }); + + element.addEventListener("DOMContentLoaded", onDOMReady, false); + + function onDOMReady() { + let worker = Worker({ + window: element.contentWindow, + contentScript: + 'new ' + function () { + var assert = function assert(v, msg) { + self.port.emit("assert", { assertion: v, msg: msg }); + } + var done = function done() { + self.port.emit("done"); + } + window.wrappedJSObject.fuu = { bar: 42 }; + window.wrappedJSObject.assert = assert; + window.wrappedJSObject.runTest(); + done(); + } + }); + worker.port.on("done", function () { + element.parentNode.removeChild(element); + done(); + }); + worker.port.on("assert", function (data) { + assert.ok(data.assertion, data.msg); + }); + } +}; + +require("sdk/test/runner").runTestsFromModule(module); diff --git a/addon-sdk/source/test/addons/unsafe-content-script/package.json b/addon-sdk/source/test/addons/unsafe-content-script/package.json new file mode 100644 index 00000000000..72c5b512f81 --- /dev/null +++ b/addon-sdk/source/test/addons/unsafe-content-script/package.json @@ -0,0 +1,6 @@ +{ + "id": "content-permissions", + "permissions": { + "unsafe-content-script": true + } +} diff --git a/addon-sdk/source/test/test-content-script.js b/addon-sdk/source/test/test-content-script.js index 38acfc1ca16..7bfa3123c91 100644 --- a/addon-sdk/source/test/test-content-script.js +++ b/addon-sdk/source/test/test-content-script.js @@ -842,4 +842,30 @@ exports["test MutationObvserver"] = createProxyTest(html, function (helper) { }); +let html = ''; +exports["test nsEp for content-script"] = createProxyTest(html, function (helper) { + + helper.createWorker( + 'let glob = this; new ' + function ContentScriptScope() { + + exportFunction(assert, unsafeWindow, { defineAs: "assert" }); + window.wrappedJSObject.assert(true, "assert exported"); + window.wrappedJSObject.exportedObj = { prop: 42 }; + window.wrappedJSObject.accessCheck(); + done(); + } + ); + +}); + require("test").run(exports);