[COOP access reporting] Preliminary WPT tests.
Add some basic WPT tests about the COOP access reporting feature.
No web browsers actually implement this. As a result, chrome do not
pass them yet.
The tests aren't complete yet, they will evolve along the specification
and the implementations.
Bug: 922191
Change-Id: Ie4675e5fb5ec0f839ca1527c15fafbb456925a0d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2228884
Reviewed-by: Pâris Meuleman <pmeuleman@chromium.org>
Commit-Queue: Arthur Sonzogni <arthursonzogni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#775020}
diff --git a/html/cross-origin-opener-policy/access-reporting/META.yml b/html/cross-origin-opener-policy/access-reporting/META.yml
new file mode 100644
index 0000000..0db2820
--- /dev/null
+++ b/html/cross-origin-opener-policy/access-reporting/META.yml
@@ -0,0 +1,6 @@
+suggested_reviewers:
+ - ArthurSonzogni
+ - ParisMeuleman
+ - camillelamy
+ - hemeryar
+ - mikewest
diff --git a/html/cross-origin-opener-policy/access-reporting/openee-accessed_openee-coop-ro.https.html b/html/cross-origin-opener-policy/access-reporting/openee-accessed_openee-coop-ro.https.html
new file mode 100644
index 0000000..2f4ea87
--- /dev/null
+++ b/html/cross-origin-opener-policy/access-reporting/openee-accessed_openee-coop-ro.https.html
@@ -0,0 +1,84 @@
+<title>
+ COOP reports are sent when the openee used COOP-RO+COEP and then its opener
+ tries to access it.
+</title>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src="/common/utils.js"></script>
+<script src="./resources/dispatcher.js"></script>
+<script>
+
+const directory = "/html/cross-origin-opener-policy/access-reporting";
+const executor_path = directory + "/resources/executor.html?pipe=";
+const cross_origin = get_host_info().HTTPS_REMOTE_ORIGIN;
+const coep_header = '|header(Cross-Origin-Embedder-Policy,require-corp)';
+
+let operation = [
+//[test name , operation ] ,
+ ["Call blur" , w => w.blur() ] ,
+ ["Call foo" , w => w.foo() ] ,
+ ["Call location" , w => w.location() ] ,
+ ["Call opener" , w => w.opener() ] ,
+ ["Call postMessage" , w => w.postMessage() ] ,
+ ["Call window" , w => w.window() ] ,
+ ["Read blur" , w => w.blur ] ,
+ ["Read foo" , w => w.foo ] ,
+ ["Read location" , w => w.location ] ,
+ ["Read opener" , w => w.opener ] ,
+ ["Read postMessage" , w => w.postMessage ] ,
+ ["Read window" , w => w.window ] ,
+ ["Write blur" , w => w.blur = "test" ] ,
+ ["Write foo" , w => w.foo = "test" ] ,
+ ["Write location" , w => w.location = "test" ] ,
+ ["Write opener" , w => w.opener = "test" ] ,
+ ["Write postMessage" , w => w.postMessage = "test" ] ,
+ ["Write window" , w => w.window = "test" ] ,
+];
+
+operation.forEach(([test, op]) => {
+ promise_test(async t => {
+ const report_token = token();
+ const executor_token = token();
+ const callback_token = token();
+
+ const reportTo = reportToHeaders(report_token);
+ const openee_url = cross_origin + executor_path +
+ reportTo.header + reportTo.coopReportOnlySameOrigin + coep_header +
+ `&uuid=${executor_token}`;
+ const openee = window.open(openee_url);
+ t.add_cleanup(() => send(executor_token, "window.close()"))
+
+ // 1. Make sure the new document to be loaded.
+ send(executor_token, `
+ send("${callback_token}", "Ready");
+ `);
+ let reply = await receive(callback_token);
+ assert_equals(reply, "Ready");
+
+ // 2. Skip the first report about the opener breakage.
+ let report_1 = await receive(report_token);
+ assert_not_equals(report_1, "timeout");
+ report_1 = JSON.parse(report_1);
+ assert_equals(report_1.length, 1);
+ assert_equals(report_1[0].type, "coop");
+ assert_equals(report_1[0].body["violation-type"], "navigation-to-document");
+ assert_equals(report_1[0].body["disposition"], "reporting");
+
+ // 3. Try to access the openee. A report is sent, because of COOP-RO+COEP.
+ try {op(openee)} catch(e) {}
+
+ // 4. A COOP access reports must be sent as a result of (3).
+ let report_2 = await receive(report_token);
+ assert_not_equals(report_1, "timeout");
+ report_2 = JSON.parse(report_2);
+ assert_equals(report_2.length, 1);
+ assert_equals(report_2[0].type, "coop");
+ assert_equals(report_2[0].body["violation-type"], "access");
+ assert_equals(report_2[0].body["disposition"], "reporting");
+ assert_equals(report_2[0].body["effective-policy"], "same-origin-plus-coep");
+ }, `${test}`);
+});
+
+</script>
diff --git a/html/cross-origin-opener-policy/access-reporting/openee-accessed_openee-coop.https.html b/html/cross-origin-opener-policy/access-reporting/openee-accessed_openee-coop.https.html
new file mode 100644
index 0000000..07fd43b
--- /dev/null
+++ b/html/cross-origin-opener-policy/access-reporting/openee-accessed_openee-coop.https.html
@@ -0,0 +1,84 @@
+<title>
+ COOP reports are sent when the openee used COOP+COEP and then its opener
+ tries to access it.
+</title>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src="/common/utils.js"></script>
+<script src="./resources/dispatcher.js"></script>
+<script>
+
+const directory = "/html/cross-origin-opener-policy/access-reporting";
+const executor_path = directory + "/resources/executor.html?pipe=";
+const cross_origin = get_host_info().HTTPS_REMOTE_ORIGIN;
+const coep_header = '|header(Cross-Origin-Embedder-Policy,require-corp)';
+
+let operation = [
+//[test name , operation ] ,
+ ["Call blur" , w => w.blur() ] ,
+ ["Call foo" , w => w.foo() ] ,
+ ["Call location" , w => w.location() ] ,
+ ["Call opener" , w => w.opener() ] ,
+ ["Call postMessage" , w => w.postMessage() ] ,
+ ["Call window" , w => w.window() ] ,
+ ["Read blur" , w => w.blur ] ,
+ ["Read foo" , w => w.foo ] ,
+ ["Read location" , w => w.location ] ,
+ ["Read opener" , w => w.opener ] ,
+ ["Read postMessage" , w => w.postMessage ] ,
+ ["Read window" , w => w.window ] ,
+ ["Write blur" , w => w.blur = "test" ] ,
+ ["Write foo" , w => w.foo = "test" ] ,
+ ["Write location" , w => w.location = "test" ] ,
+ ["Write opener" , w => w.opener = "test" ] ,
+ ["Write postMessage" , w => w.postMessage = "test" ] ,
+ ["Write window" , w => w.window = "test" ] ,
+];
+
+operation.forEach(([test, op]) => {
+ promise_test(async t => {
+ const report_token = token();
+ const executor_token = token();
+ const callback_token = token();
+
+ const reportTo = reportToHeaders(report_token);
+ const openee_url = cross_origin + executor_path +
+ reportTo.header + reportTo.coopSameOrigin + coep_header +
+ `&uuid=${executor_token}`;
+ const openee = window.open(openee_url);
+ t.add_cleanup(() => send(executor_token, "window.close()"))
+
+ // 1. Make sure the new document to be loaded.
+ send(executor_token, `
+ send("${callback_token}", "Ready");
+ `);
+ let reply = await receive(callback_token);
+ assert_equals(reply, "Ready");
+
+ // 2. Skip the first report about the opener breakage.
+ let report_1 = await receive(report_token);
+ assert_not_equals(report_1, "timeout");
+ report_1 = JSON.parse(report_1);
+ assert_equals(report_1.length, 1);
+ assert_equals(report_1[0].type, "coop");
+ assert_equals(report_1[0].body["violation-type"], "navigation-to-document");
+ assert_equals(report_1[0].body["disposition"], "enforce");
+
+ // 3. Try to access the openee. This shouldn't work because of COOP+COEP.
+ try {op(openee)} catch(e) {}
+
+ // 4. A COOP access reports must be sent as a result of (3).
+ let report_2 = await receive(report_token);
+ assert_not_equals(report_2, "timeout");
+ report_2 = JSON.parse(report_2);
+ assert_equals(report_2.length, 1);
+ assert_equals(report_2[0].type, "coop");
+ assert_equals(report_2[0].body["violation-type"], "access");
+ assert_equals(report_2[0].body["disposition"], "enforce");
+ assert_equals(report_2[0].body["effective-policy"], "same-origin-plus-coep");
+ }, `${test}`);
+});
+
+</script>
diff --git a/html/cross-origin-opener-policy/access-reporting/opener-accessed_openee-coop-ro.https.html b/html/cross-origin-opener-policy/access-reporting/opener-accessed_openee-coop-ro.https.html
new file mode 100644
index 0000000..c942b9c
--- /dev/null
+++ b/html/cross-origin-opener-policy/access-reporting/opener-accessed_openee-coop-ro.https.html
@@ -0,0 +1,83 @@
+<title>
+ COOP reports are sent when the openee used COOP-RO+COEP and then tries to
+ access its opener.
+</title>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src="/common/utils.js"></script>
+<script src="./resources/dispatcher.js"></script>
+<script>
+
+const directory = "/html/cross-origin-opener-policy/access-reporting";
+const executor_path = directory + "/resources/executor.html?pipe=";
+const cross_origin = get_host_info().HTTPS_REMOTE_ORIGIN;
+const coep_header = '|header(Cross-Origin-Embedder-Policy,require-corp)';
+
+let operation = [
+//[test name , operation ] ,
+ ["Call blur" , "opener.blur()" ] ,
+ ["Call foo" , "opener.foo()" ] ,
+ ["Call location" , "opener.location()" ] ,
+ ["Call opener" , "opener.opener()" ] ,
+ ["Call postMessage" , "opener.postMessage()" ] ,
+ ["Call window" , "opener.window()" ] ,
+ ["Read blur" , "opener.blur" ] ,
+ ["Read foo" , "opener.foo" ] ,
+ ["Read location" , "opener.location" ] ,
+ ["Read opener" , "opener.opener" ] ,
+ ["Read postMessage" , "opener.postMessage" ] ,
+ ["Read window" , "opener.window" ] ,
+ ["Write blur" , "opener.blur = 'test'" ] ,
+ ["Write foo" , "opener.foo = 'test'" ] ,
+ ["Write location" , "opener.location = 'test'" ] ,
+ ["Write opener" , "opener.opener = 'test'" ] ,
+ ["Write postMessage" , "opener.postMessage = 'test'" ] ,
+ ["Write window" , "opener.window = 'test'" ] ,
+];
+
+operation.forEach(([test, op]) => {
+ promise_test(async t => {
+ const report_token = token();
+ const executor_token = token();
+ const callback_token = token();
+
+ const reportTo = reportToHeaders(report_token);
+ const openee_url = cross_origin + executor_path +
+ reportTo.header + reportTo.coopReportOnlySameOrigin + coep_header +
+ `&uuid=${executor_token}`;
+ const openee = window.open(openee_url);
+ t.add_cleanup(() => send(executor_token, "window.close()"))
+
+ // 1. Skip the first report about the opener breakage.
+ let report_1 = await receive(report_token);
+ assert_not_equals(report_1, "timeout");
+ report_1 = JSON.parse(report_1);
+ assert_equals(report_1.length, 1);
+ assert_equals(report_1[0].type, "coop");
+ assert_equals(report_1[0].body["violation-type"], "navigation-to-document");
+ assert_equals(report_1[0].body["disposition"], "reporting");
+
+ // 3. Try to access the opener. A report is sent, because of COOP-RO+COEP.
+ send(executor_token, `
+ try {${op}} catch(e) {}
+ send("${callback_token}", "Done");
+ `);
+ let reply = await receive(callback_token);
+ assert_equals(reply, "Done");
+
+ // 4. A COOP access reports must be sent as a result of (3).
+ let report_2 = await receive(report_token);
+ assert_not_equals(report_2, "timeout");
+ report_2 = JSON.parse(report_2);
+
+ assert_equals(report_2.length, 1);
+ assert_equals(report_2[0].type, "coop");
+ assert_equals(report_2[0].body["violation-type"], "access");
+ assert_equals(report_2[0].body["disposition"], "reporting");
+ assert_equals(report_2[0].body["effective-policy"], "same-origin-plus-coep");
+ }, `${test}`);
+});
+
+</script>
diff --git a/html/cross-origin-opener-policy/access-reporting/opener-accessed_openee-coop.https.html b/html/cross-origin-opener-policy/access-reporting/opener-accessed_openee-coop.https.html
new file mode 100644
index 0000000..921fdf7
--- /dev/null
+++ b/html/cross-origin-opener-policy/access-reporting/opener-accessed_openee-coop.https.html
@@ -0,0 +1,83 @@
+<title>
+ COOP reports are sent when the openee used COOP+COEP and then tries to
+ access its opener.
+</title>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src="/common/utils.js"></script>
+<script src="./resources/dispatcher.js"></script>
+<script>
+
+const directory = "/html/cross-origin-opener-policy/access-reporting";
+const executor_path = directory + "/resources/executor.html?pipe=";
+const cross_origin = get_host_info().HTTPS_REMOTE_ORIGIN;
+const coep_header = '|header(Cross-Origin-Embedder-Policy,require-corp)';
+
+let operation = [
+//[test name , operation ] ,
+ ["Call blur" , "opener.blur()" ] ,
+ ["Call foo" , "opener.foo()" ] ,
+ ["Call location" , "opener.location()" ] ,
+ ["Call opener" , "opener.opener()" ] ,
+ ["Call postMessage" , "opener.postMessage()" ] ,
+ ["Call window" , "opener.window()" ] ,
+ ["Read blur" , "opener.blur" ] ,
+ ["Read foo" , "opener.foo" ] ,
+ ["Read location" , "opener.location" ] ,
+ ["Read opener" , "opener.opener" ] ,
+ ["Read postMessage" , "opener.postMessage" ] ,
+ ["Read window" , "opener.window" ] ,
+ ["Write blur" , "opener.blur = 'test'" ] ,
+ ["Write foo" , "opener.foo = 'test'" ] ,
+ ["Write location" , "opener.location = 'test'" ] ,
+ ["Write opener" , "opener.opener = 'test'" ] ,
+ ["Write postMessage" , "opener.postMessage = 'test'" ] ,
+ ["Write window" , "opener.window = 'test'" ] ,
+];
+
+operation.forEach(([test, op]) => {
+ promise_test(async t => {
+ const report_token = token();
+ const executor_token = token();
+ const callback_token = token();
+
+ const reportTo = reportToHeaders(report_token);
+ const openee_url = cross_origin + executor_path +
+ reportTo.header + reportTo.coopSameOrigin + coep_header +
+ `&uuid=${executor_token}`;
+ const openee = window.open(openee_url);
+ t.add_cleanup(() => send(executor_token, "window.close()"));
+
+ // 1. Skip the first report about the opener breakage.
+ let report_1 = await receive(report_token);
+ assert_not_equals(report_1, "timeout");
+ report_1 = JSON.parse(report_1);
+ assert_equals(report_1.length, 1);
+ assert_equals(report_1[0].type, "coop");
+ assert_equals(report_1[0].body["violation-type"], "navigation-to-document");
+ assert_equals(report_1[0].body["disposition"], "enforce");
+
+ // 3. Try to access the opener. A report is sent, because of COOP-RO+COEP.
+ send(executor_token, `
+ try {${op}} catch(e) {}
+ send("${callback_token}", "Done");
+ `);
+ let reply = await receive(callback_token);
+ assert_equals(reply, "Done");
+
+ // 4. A COOP access reports must be sent as a result of (3).
+ let report_2 = await receive(report_token);
+ assert_not_equals(report_2, "timeout");
+ report_2 = JSON.parse(report_2);
+
+ assert_equals(report_2.length, 1);
+ assert_equals(report_2[0].type, "coop");
+ assert_equals(report_2[0].body["violation-type"], "access");
+ assert_equals(report_2[0].body["disposition"], "enforce");
+ assert_equals(report_2[0].body["effective-policy"], "same-origin-plus-coep");
+ }, `${test}`);
+});
+
+</script>
diff --git a/html/cross-origin-opener-policy/access-reporting/resources/dispatcher.js b/html/cross-origin-opener-policy/access-reporting/resources/dispatcher.js
new file mode 100644
index 0000000..d0184d5
--- /dev/null
+++ b/html/cross-origin-opener-policy/access-reporting/resources/dispatcher.js
@@ -0,0 +1,55 @@
+// Define an universal message passing API.
+//
+// In particular, this works:
+// - cross-origin and
+// - cross-browsing-context-group.
+//
+// It can also be used to receive reports.
+
+const dispatcher_path =
+ '/html/cross-origin-opener-policy/access-reporting/resources/dispatcher.py';
+const dispatcher_url = new URL(dispatcher_path, location.href).href;
+
+const send = function(uuid, message) {
+ fetch(dispatcher_url + `?uuid=${uuid}`, {
+ method: 'POST',
+ body: message
+ });
+}
+
+const receive = async function(uuid) {
+ const timeout = 3000;
+ const retry_delay = 100;
+ for(let i = 0; i * retry_delay < timeout; ++i) {
+ let response = await fetch(dispatcher_url + `?uuid=${uuid}`);
+ let data = await response.text();
+ if (data != 'not ready')
+ return data;
+ await new Promise(r => step_timeout(r, retry_delay));
+ }
+ return "timeout";
+}
+
+// Build a set of headers to tests the reporting API. This defines a set of
+// matching 'Report-To', 'Cross-Origin-Opener-Policy' and
+// 'Cross-Origin-Opener-Policy-Report-Only' headers.
+const reportToHeaders = function(uuid) {
+ const report_endpoint_url = dispatcher_path + `?uuid=${uuid}`;
+ let reportToJSON = {
+ 'group': `${uuid}`,
+ 'max_age': 3600,
+ 'endpoints': [
+ {'url': report_endpoint_url.toString()},
+ ]
+ };
+ reportToJSON = JSON.stringify(reportToJSON)
+ .replace(/,/g, '\\,')
+ .replace(/\(/g, '\\\(')
+ .replace(/\)/g, '\\\)=');
+
+ return {
+ header: `|header(report-to,${reportToJSON})`,
+ coopSameOrigin: `|header(Cross-Origin-Opener-Policy, same-origin%3Breport-to="${uuid}")`,
+ coopReportOnlySameOrigin: `|header(Cross-Origin-Opener-Policy-Report-Only, same-origin%3Breport-to="${uuid}")`,
+ };
+};
diff --git a/html/cross-origin-opener-policy/access-reporting/resources/dispatcher.py b/html/cross-origin-opener-policy/access-reporting/resources/dispatcher.py
new file mode 100644
index 0000000..67b8cc3
--- /dev/null
+++ b/html/cross-origin-opener-policy/access-reporting/resources/dispatcher.py
@@ -0,0 +1,22 @@
+# A server used to store and retrieve arbitrary data.
+# This is used by: ./dispatcher.js
+import json
+
+def main(request, response):
+ response.headers.set('Access-Control-Allow-Origin', '*')
+ response.headers.set('Access-Control-Allow-Methods', 'OPTIONS, GET, POST')
+ response.headers.set('Access-Control-Allow-Headers', 'Content-Type')
+ response.headers.set('Cache-Control', 'no-cache, no-store, must-revalidate');
+ if request.method == 'OPTIONS': # CORS preflight
+ return ''
+
+ uuid = request.GET['uuid']
+
+ if request.method == 'POST':
+ return request.server.stash.put(uuid, request.body)
+ else:
+ body = request.server.stash.take(uuid)
+ if body is None:
+ return 'not ready'
+ else:
+ return body
diff --git a/html/cross-origin-opener-policy/access-reporting/resources/executor.html b/html/cross-origin-opener-policy/access-reporting/resources/executor.html
new file mode 100644
index 0000000..3b10da0
--- /dev/null
+++ b/html/cross-origin-opener-policy/access-reporting/resources/executor.html
@@ -0,0 +1,12 @@
+<script src=/resources/testharness.js></script>
+<script src="./dispatcher.js"></script>
+<script>
+ const params = new URLSearchParams(window.location.search);
+ const uuid = params.get('uuid');
+
+ let executeOrders = async function() {
+ while(true)
+ eval(await receive(uuid));
+ };
+ executeOrders();
+</script>