From c7fb77eccf3e78d0a27332e6767cacde2f77613a Mon Sep 17 00:00:00 2001 From: Alexandra Borovova Date: Wed, 17 Aug 2022 14:35:02 +0000 Subject: [PATCH] Bug 1770480 - [wdspec] Add tests for script evaluation inside sandboxes for "script.evaluate" and "script.callFunction" commands. r=webdriver-reviewers,jdescottes Depends on D153440 Differential Revision: https://phabricator.services.mozilla.com/D153442 --- .../bidi/script/call_function/sandbox.py.ini | 28 +++ .../tests/bidi/script/evaluate/sandbox.py.ini | 24 +++ .../bidi/script/call_function/sandbox.py | 180 ++++++++++++++++++ .../tests/bidi/script/evaluate/sandbox.py | 158 +++++++++++++++ 4 files changed, 390 insertions(+) create mode 100644 testing/web-platform/meta/webdriver/tests/bidi/script/call_function/sandbox.py.ini create mode 100644 testing/web-platform/meta/webdriver/tests/bidi/script/evaluate/sandbox.py.ini create mode 100644 testing/web-platform/tests/webdriver/tests/bidi/script/call_function/sandbox.py create mode 100644 testing/web-platform/tests/webdriver/tests/bidi/script/evaluate/sandbox.py diff --git a/testing/web-platform/meta/webdriver/tests/bidi/script/call_function/sandbox.py.ini b/testing/web-platform/meta/webdriver/tests/bidi/script/call_function/sandbox.py.ini new file mode 100644 index 000000000000..c088018b318c --- /dev/null +++ b/testing/web-platform/meta/webdriver/tests/bidi/script/call_function/sandbox.py.ini @@ -0,0 +1,28 @@ +[sandbox.py] + [test_sandbox] + disabled: + if os == "android": https://bugzilla.mozilla.org/show_bug.cgi?id=1506782 + + [test_sandbox_with_empty_name] + disabled: + if os == "android": https://bugzilla.mozilla.org/show_bug.cgi?id=1506782 + + [test_switch_sandboxes] + disabled: + if os == "android": https://bugzilla.mozilla.org/show_bug.cgi?id=1506782 + + [test_sandbox_with_side_effects] + disabled: + if os == "android": https://bugzilla.mozilla.org/show_bug.cgi?id=1506782 + + [test_arguments] + disabled: + if os == "android": https://bugzilla.mozilla.org/show_bug.cgi?id=1506782 + + [test_exception_details[True\]] + disabled: + if os == "android": https://bugzilla.mozilla.org/show_bug.cgi?id=1506782 + + [test_exception_details[False\]] + disabled: + if os == "android": https://bugzilla.mozilla.org/show_bug.cgi?id=1506782 diff --git a/testing/web-platform/meta/webdriver/tests/bidi/script/evaluate/sandbox.py.ini b/testing/web-platform/meta/webdriver/tests/bidi/script/evaluate/sandbox.py.ini new file mode 100644 index 000000000000..124c1889ae5b --- /dev/null +++ b/testing/web-platform/meta/webdriver/tests/bidi/script/evaluate/sandbox.py.ini @@ -0,0 +1,24 @@ +[sandbox.py] + [test_sandbox] + disabled: + if os == "android": https://bugzilla.mozilla.org/show_bug.cgi?id=1506782 + + [test_sandbox_with_empty_name] + disabled: + if os == "android": https://bugzilla.mozilla.org/show_bug.cgi?id=1506782 + + [test_switch_sandboxes] + disabled: + if os == "android": https://bugzilla.mozilla.org/show_bug.cgi?id=1506782 + + [test_sandbox_with_side_effects] + disabled: + if os == "android": https://bugzilla.mozilla.org/show_bug.cgi?id=1506782 + + [test_exception_details[True\]] + disabled: + if os == "android": https://bugzilla.mozilla.org/show_bug.cgi?id=1506782 + + [test_exception_details[False\]] + disabled: + if os == "android": https://bugzilla.mozilla.org/show_bug.cgi?id=1506782 diff --git a/testing/web-platform/tests/webdriver/tests/bidi/script/call_function/sandbox.py b/testing/web-platform/tests/webdriver/tests/bidi/script/call_function/sandbox.py new file mode 100644 index 000000000000..068143709f32 --- /dev/null +++ b/testing/web-platform/tests/webdriver/tests/bidi/script/call_function/sandbox.py @@ -0,0 +1,180 @@ +import pytest + +from webdriver.bidi.modules.script import ContextTarget, ScriptEvaluateResultException + +from ... import any_int, any_string, recursive_compare +from .. import any_stack_trace + + +@pytest.mark.asyncio +async def test_sandbox(bidi_session, new_tab): + # Make changes in window + await bidi_session.script.call_function( + function_declaration="() => { window.foo = 1; }", + target=ContextTarget(new_tab["context"]), + await_promise=True, + ) + + # Check that changes are not present in sandbox + result_in_sandbox = await bidi_session.script.call_function( + function_declaration="() => window.foo", + target=ContextTarget(new_tab["context"], "sandbox"), + await_promise=True, + ) + assert result_in_sandbox == {"type": "undefined"} + + # Make changes in sandbox + await bidi_session.script.call_function( + function_declaration="() => { window.bar = 2; }", + target=ContextTarget(new_tab["context"], "sandbox"), + await_promise=True, + ) + + # Make sure that changes are present in sandbox + result_in_sandbox = await bidi_session.script.call_function( + function_declaration="() => window.bar", + target=ContextTarget(new_tab["context"], "sandbox"), + await_promise=True, + ) + assert result_in_sandbox == {"type": "number", "value": 2} + + # Make sure that changes didn't leak from sandbox + result_in_window = await bidi_session.script.call_function( + function_declaration="() => window.bar", + target=ContextTarget(new_tab["context"]), + await_promise=True, + ) + assert result_in_window == {"type": "undefined"} + + +@pytest.mark.asyncio +async def test_sandbox_with_empty_name(bidi_session, new_tab): + # BiDi specification doesn't have restrictions of a sandbox name, + # that's why we want to make sure that it works with an empty name + await bidi_session.script.call_function( + function_declaration="() => window.foo = 'bar'", + target=ContextTarget(new_tab["context"], ""), + await_promise=True, + ) + + # Make sure that we can find the sandbox with the empty name + result = await bidi_session.script.call_function( + function_declaration="() => window.foo", + target=ContextTarget(new_tab["context"], ""), + await_promise=True, + ) + assert result == {"type": "string", "value": "bar"} + + # Make sure that changes didn't leak from sandbox + result = await bidi_session.script.evaluate( + expression="window.foo", + target=ContextTarget(new_tab["context"]), + await_promise=True, + ) + assert result == {"type": "undefined"} + + +@pytest.mark.asyncio +async def test_switch_sandboxes(bidi_session, new_tab): + # Test that sandboxes are retained when switching between them + await bidi_session.script.call_function( + function_declaration="() => { window.foo = 1; }", + target=ContextTarget(new_tab["context"], "sandbox_1"), + await_promise=True, + ) + await bidi_session.script.call_function( + function_declaration="() => { window.foo = 2; }", + target=ContextTarget(new_tab["context"], "sandbox_2"), + await_promise=True, + ) + + result_in_sandbox_1 = await bidi_session.script.call_function( + function_declaration="() => window.foo", + target=ContextTarget(new_tab["context"], "sandbox_1"), + await_promise=True, + ) + assert result_in_sandbox_1 == {"type": "number", "value": 1} + + result_in_sandbox_2 = await bidi_session.script.call_function( + function_declaration="() => window.foo", + target=ContextTarget(new_tab["context"], "sandbox_2"), + await_promise=True, + ) + assert result_in_sandbox_2 == {"type": "number", "value": 2} + + +@pytest.mark.asyncio +async def test_sandbox_with_side_effects(bidi_session, new_tab): + # Make sure changing the node in sandbox will affect the other sandbox as well + await bidi_session.script.call_function( + function_declaration="() => document.querySelector('body').textContent = 'foo'", + target=ContextTarget(new_tab["context"], "sandbox_1"), + await_promise=True, + ) + expected_value = {"type": "string", "value": "foo"} + + result_in_sandbox_1 = await bidi_session.script.call_function( + function_declaration="() => document.querySelector('body').textContent", + target=ContextTarget(new_tab["context"], "sandbox_1"), + await_promise=True, + ) + assert result_in_sandbox_1 == expected_value + + result_in_sandbox_2 = await bidi_session.script.call_function( + function_declaration="() => document.querySelector('body').textContent", + target=ContextTarget(new_tab["context"], "sandbox_2"), + await_promise=True, + ) + assert result_in_sandbox_2 == expected_value + + +@pytest.mark.asyncio +async def test_arguments(bidi_session, new_tab): + argument = { + "type": "set", + "value": [ + {"type": "string", "value": "foobar"}, + ], + } + result = await bidi_session.script.call_function( + function_declaration=f"""(arg) => {{ + if(! (arg instanceof Set)) + throw Error("Argument type should be Set, but was "+ + Object.prototype.toString.call(arg)); + return arg; + }}""", + arguments=[argument], + await_promise=False, + target=ContextTarget(new_tab["context"], "sandbox"), + ) + + recursive_compare(argument, result) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("await_promise", [True, False]) +async def test_exception_details(bidi_session, new_tab, await_promise): + function_declaration = "()=>{{ throw 1 }}" + if await_promise: + function_declaration = "async" + function_declaration + + with pytest.raises(ScriptEvaluateResultException) as exception: + await bidi_session.script.call_function( + function_declaration=function_declaration, + await_promise=await_promise, + target=ContextTarget(new_tab["context"], "sandbox"), + ) + + recursive_compare( + { + "realm": any_string, + "exceptionDetails": { + "columnNumber": any_int, + "exception": {"type": "number", "value": 1}, + "lineNumber": any_int, + "stackTrace": any_stack_trace, + "text": any_string, + }, + }, + exception.value.result, + ) diff --git a/testing/web-platform/tests/webdriver/tests/bidi/script/evaluate/sandbox.py b/testing/web-platform/tests/webdriver/tests/bidi/script/evaluate/sandbox.py new file mode 100644 index 000000000000..964a9c578570 --- /dev/null +++ b/testing/web-platform/tests/webdriver/tests/bidi/script/evaluate/sandbox.py @@ -0,0 +1,158 @@ +import pytest + +from webdriver.bidi.modules.script import ContextTarget, ScriptEvaluateResultException + +from ... import any_int, any_string, recursive_compare +from .. import any_stack_trace + + +@pytest.mark.asyncio +async def test_sandbox(bidi_session, new_tab): + # Make changes in window + await bidi_session.script.evaluate( + expression="window.foo = 1", + target=ContextTarget(new_tab["context"]), + await_promise=True, + ) + + # Check that changes are not present in sandbox + result_in_sandbox = await bidi_session.script.evaluate( + expression="window.foo", + target=ContextTarget(new_tab["context"], "sandbox"), + await_promise=True, + ) + assert result_in_sandbox == {"type": "undefined"} + + # Make changes in sandbox + await bidi_session.script.evaluate( + expression="window.bar = 1", + target=ContextTarget(new_tab["context"], "sandbox"), + await_promise=True, + ) + + # Make sure that changes are present in sandbox + result_in_sandbox = await bidi_session.script.evaluate( + expression="window.bar", + target=ContextTarget(new_tab["context"], "sandbox"), + await_promise=True, + ) + assert result_in_sandbox == {"type": "number", "value": 1} + + # Make sure that changes didn't leak from sandbox + result = await bidi_session.script.evaluate( + expression="window.bar", + target=ContextTarget(new_tab["context"]), + await_promise=True, + ) + assert result == {"type": "undefined"} + + +@pytest.mark.asyncio +async def test_sandbox_with_empty_name(bidi_session, new_tab): + # BiDi specification doesn't have restrictions of a sandbox name, + # that's why we want to make sure that it works with an empty name + await bidi_session.script.evaluate( + expression="window.foo = 'bar'", + target=ContextTarget(new_tab["context"], ""), + await_promise=True, + ) + + # Make sure that we can find the sandbox with the empty name + result = await bidi_session.script.evaluate( + expression="window.foo", + target=ContextTarget(new_tab["context"], ""), + await_promise=True, + ) + assert result == {"type": "string", "value": "bar"} + + # Make sure that changes didn't leak from sandbox + result = await bidi_session.script.evaluate( + expression="window.foo", + target=ContextTarget(new_tab["context"]), + await_promise=True, + ) + assert result == {"type": "undefined"} + + +@pytest.mark.asyncio +async def test_switch_sandboxes(bidi_session, new_tab): + # Test that sandboxes are retained when switching between them + await bidi_session.script.evaluate( + expression="window.foo = 1", + target=ContextTarget(new_tab["context"], "sandbox_1"), + await_promise=True, + ) + await bidi_session.script.evaluate( + expression="window.foo = 2", + target=ContextTarget(new_tab["context"], "sandbox_2"), + await_promise=True, + ) + + result_in_sandbox_1 = await bidi_session.script.evaluate( + expression="window.foo", + target=ContextTarget(new_tab["context"], "sandbox_1"), + await_promise=True, + ) + assert result_in_sandbox_1 == {"type": "number", "value": 1} + + result_in_sandbox_2 = await bidi_session.script.evaluate( + expression="window.foo", + target=ContextTarget(new_tab["context"], "sandbox_2"), + await_promise=True, + ) + assert result_in_sandbox_2 == {"type": "number", "value": 2} + + +@pytest.mark.asyncio +async def test_sandbox_with_side_effects(bidi_session, new_tab): + # Make sure changing the node in sandbox will affect the other sandbox as well + await bidi_session.script.evaluate( + expression="document.querySelector('body').textContent = 'foo'", + target=ContextTarget(new_tab["context"], "sandbox_1"), + await_promise=True, + ) + expected_value = {"type": "string", "value": "foo"} + + result_in_sandbox_1 = await bidi_session.script.evaluate( + expression="document.querySelector('body').textContent", + target=ContextTarget(new_tab["context"], "sandbox_1"), + await_promise=True, + ) + assert result_in_sandbox_1 == expected_value + + result_in_sandbox_2 = await bidi_session.script.evaluate( + expression="document.querySelector('body').textContent", + target=ContextTarget(new_tab["context"], "sandbox_2"), + await_promise=True, + ) + assert result_in_sandbox_2 == expected_value + + +@pytest.mark.asyncio +@pytest.mark.parametrize("await_promise", [True, False]) +async def test_exception_details(bidi_session, new_tab, await_promise): + if await_promise: + expression = "Promise.reject(1)" + else: + expression = "throw 1" + + with pytest.raises(ScriptEvaluateResultException) as exception: + await bidi_session.script.evaluate( + expression=expression, + target=ContextTarget(new_tab["context"], "sandbox"), + await_promise=await_promise, + ) + + recursive_compare( + { + "realm": any_string, + "exceptionDetails": { + "columnNumber": any_int, + "exception": {"type": "number", "value": 1}, + "lineNumber": any_int, + "stackTrace": any_stack_trace, + "text": any_string, + }, + }, + exception.value.result, + )