From 5a917f7627a37aab5349192e20a51bb0866e6c81 Mon Sep 17 00:00:00 2001 From: Andreas Tolfsen Date: Sat, 10 Dec 2016 16:15:53 -1000 Subject: [PATCH] Bug 1322862 - Make __webDriverArguments et al. content safe; r=automatedtester Properties created in a more privileged scope than content cause permission denied errors when they are accessed from a less privileged scope. This is the case when we assign a document unload handler from chrome to a sandbox with content privileges. A permission denied error will be thrown if this handler is inspected from the code evaluated inside the sandbox. By cloning the properties along with their functions and wrapping their reflectors, we can ensure they can be safely inspected from content. MozReview-Commit-ID: Hy5MYvdTsv8 --HG-- extra : rebase_source : 068cd67f3bc6f99c312303c4682c47cd9c5143d7 --- testing/marionette/evaluate.js | 24 +++++++++++++------ .../tests/unit/test_execute_script.py | 14 +++++++++++ 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/testing/marionette/evaluate.js b/testing/marionette/evaluate.js index 2e66430b759e..38a80eb39450 100644 --- a/testing/marionette/evaluate.js +++ b/testing/marionette/evaluate.js @@ -113,7 +113,7 @@ evaluate.sandbox = function (sb, script, args = [], opts = {}) { if (opts.async) { sb[CALLBACK] = sb[COMPLETE]; } - sb[ARGUMENTS] = Cu.cloneInto(args, sb, {wrapReflectors: true}); + sb[ARGUMENTS] = sandbox.cloneInto(args, sb); // callback function made private // so that introspection is possible @@ -138,20 +138,18 @@ evaluate.sandbox = function (sb, script, args = [], opts = {}) { // see bug 1128760 for more details if (opts.debug) { sb.window.onerror = (msg, url, line) => { - let err = new JavaScriptError(`${msg} at: ${url} line: ${line}`); + let err = new JavaScriptError(`${msg} at ${url}:${line}`); reject(err); }; } // timeout and unload handlers - scriptTimeoutID = setTimeout( - timeoutHandler, opts.timeout || DEFAULT_TIMEOUT); - sb.window.addEventListener("unload", unloadHandler); + scriptTimeoutID = setTimeout(timeoutHandler, opts.timeout || DEFAULT_TIMEOUT); + sb.window.onunload = sandbox.cloneInto(unloadHandler, sb); let res; try { - res = Cu.evalInSandbox( - src, sb, "1.8", opts.filename || "dummy file", 0); + res = Cu.evalInSandbox(src, sb, "1.8", opts.filename || "dummy file", 0); } catch (e) { let err = new JavaScriptError( e, @@ -176,6 +174,18 @@ evaluate.sandbox = function (sb, script, args = [], opts = {}) { this.sandbox = {}; +/** + * Provides a safe way to take an object defined in a privileged scope and + * create a structured clone of it in a less-privileged scope. It returns + * a reference to the clone. + * + * Unlike for |Components.utils.cloneInto|, |obj| may contain functions + * and DOM elemnets. + */ +sandbox.cloneInto = function (obj, sb) { + return Cu.cloneInto(obj, sb, {cloneFunctions: true, wrapReflectors: true}); +}; + /** * Augment given sandbox by an adapter that has an {@code exports} * map property, or a normal map, of function names and function diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_execute_script.py b/testing/marionette/harness/marionette_harness/tests/unit/test_execute_script.py index 2d6d97e1f47f..1ef4549d3cff 100644 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_execute_script.py +++ b/testing/marionette/harness/marionette_harness/tests/unit/test_execute_script.py @@ -262,6 +262,17 @@ class TestExecuteContent(MarionetteTestCase): content_timeout_triggered, message="Scheduled setTimeout event was cancelled by call to execute_script") + def test_privileged_code_inspection(self): + # test permission denied on toString of unload event handler + self.marionette.navigate(inline(""" + """)) + self.marionette.execute_script("", sandbox=None) + + # test inspection of arguments + self.marionette.execute_script("__webDriverArguments.toString()") + class TestExecuteChrome(WindowManagerMixin, TestExecuteContent): @@ -337,6 +348,9 @@ class TestExecuteChrome(WindowManagerMixin, TestExecuteContent): def test_window_set_timeout_is_not_cancelled(self): pass + def test_privileged_code_inspection(self): + pass + class TestElementCollections(MarionetteTestCase):