Bug 1516736 [wpt PR 14686] - [serial] Implement feature policy checks, a=testonly
authorReilly Grant <reillyg@chromium.org>
Mon, 18 Feb 2019 19:26:24 +0000
changeset 461320 b23139608c8ae59a00c34a85cfd9ba4b5218f62b
parent 461319 80797a975d97d6b5af7d2d2bd71a0637d04fae23
child 461321 22ba9f77d25ce999a8b2213e409697db2721e524
push id35622
push userncsoregi@mozilla.com
push dateWed, 27 Feb 2019 04:32:15 +0000
treeherdermozilla-central@5b8896aa3f69 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstestonly
bugs1516736, 14686, 884928, 1392357, 630196
milestone67.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 1516736 [wpt PR 14686] - [serial] Implement feature policy checks, a=testonly Automatic update from web-platform-tests [serial] Implement feature policy checks This API should follow the example of WebUSB and require explicit authorization via feature policy when used in a cross-origin iframe. Bug: 884928 Change-Id: Ia953c86a45e8b38b901b790b8aee912fd991e1a1 Reviewed-on: https://chromium-review.googlesource.com/c/1392357 Reviewed-by: Luna Lu <loonybear@chromium.org> Reviewed-by: Nasko Oskov <nasko@chromium.org> Reviewed-by: Oliver Chang <ochang@chromium.org> Commit-Queue: Luna Lu <loonybear@chromium.org> Auto-Submit: Reilly Grant <reillyg@chromium.org> Cr-Commit-Position: refs/heads/master@{#630196} -- wpt-commits: 5041c80124749711b4f143c2136903e84ad42ad1 wpt-pr: 14686
testing/web-platform/tests/feature-policy/reporting/serial-report-only.https.html
testing/web-platform/tests/feature-policy/reporting/serial-report-only.https.html.headers
testing/web-platform/tests/feature-policy/reporting/serial-reporting.https.html
testing/web-platform/tests/feature-policy/reporting/serial-reporting.https.html.headers
testing/web-platform/tests/feature-policy/resources/feature-policy-serial-worker.html
testing/web-platform/tests/feature-policy/resources/feature-policy-serial-worker.js
testing/web-platform/tests/feature-policy/resources/feature-policy-serial.html
testing/web-platform/tests/serial/resources/serial-allowed-by-feature-policy-worker.js
testing/web-platform/tests/serial/resources/serial-disabled-by-feature-policy-worker.js
testing/web-platform/tests/serial/serial-allowed-by-feature-policy-attribute-redirect-on-load.https.sub.html
testing/web-platform/tests/serial/serial-allowed-by-feature-policy-attribute.https.sub.html
testing/web-platform/tests/serial/serial-allowed-by-feature-policy.https.sub.html
testing/web-platform/tests/serial/serial-allowed-by-feature-policy.https.sub.html.headers
testing/web-platform/tests/serial/serial-default-feature-policy.https.sub.html
testing/web-platform/tests/serial/serial-disabled-by-feature-policy.https.sub.html
testing/web-platform/tests/serial/serial-disabled-by-feature-policy.https.sub.html.headers
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/feature-policy/reporting/serial-report-only.https.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <script src='/resources/testharness.js'></script>
+    <script src='/resources/testharnessreport.js'></script>
+    <script src='/resources/testdriver.js'></script>
+    <script src="/resources/testdriver-vendor.js"></script>
+  </head>
+  <body>
+    <div id='fs'></div>
+    <script>
+var check_report_format = ([reports, observer]) => {
+  let report = reports[0];
+  assert_equals(report.type, "feature-policy-violation");
+  assert_equals(report.body.featureId, "serial");
+  assert_equals(report.body.disposition, "report");
+};
+
+promise_test(async t => {
+  const report = new Promise(resolve => {
+    new ReportingObserver((reports, observer) => resolve([reports, observer]),
+                          {types: ['feature-policy-violation']}).observe();
+  });
+
+  await test_driver.bless('Activate document for serial.requestPort');
+  try {
+    await navigator.serial.requestPort({filters: []});
+    assert_unreached('requestPort() call should fail when no port is selected.');
+  } catch (e) {
+    assert_equals(e.code, DOMException.NOT_FOUND_ERR);
+  }
+  check_report_format(await report);
+}, "requestPort in serial report only mode");
+
+promise_test(async t => {
+  const report = new Promise(resolve => {
+    new ReportingObserver((reports, observer) => resolve([reports, observer]),
+                          {types: ['feature-policy-violation']}).observe();
+  });
+
+  await navigator.serial.getPorts();
+  check_report_format(await report);
+}, "getPorts in serial report only mode");
+    </script>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/feature-policy/reporting/serial-report-only.https.html.headers
@@ -0,0 +1,1 @@
+Feature-Policy-Report-Only: serial 'none'
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/feature-policy/reporting/serial-reporting.https.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <script src='/resources/testharness.js'></script>
+    <script src='/resources/testharnessreport.js'></script>
+    <script src='/resources/testdriver.js'></script>
+    <script src="/resources/testdriver-vendor.js"></script>
+  </head>
+  <body>
+    <script>
+var check_report_format = ([reports, observer]) => {
+  let report = reports[0];
+  assert_equals(report.type, "feature-policy-violation");
+  assert_equals(report.url, document.location.href);
+  assert_equals(report.body.featureId, "serial");
+  assert_equals(report.body.sourceFile, document.location.href);
+  assert_equals(typeof report.body.lineNumber, "number");
+  assert_equals(typeof report.body.columnNumber, "number");
+  assert_equals(report.body.disposition, "enforce");
+};
+
+promise_test(async t => {
+  const report = new Promise(resolve => {
+    new ReportingObserver((reports, observer) => resolve([reports, observer]),
+                          {types: ['feature-policy-violation']}).observe();
+  });
+
+  await test_driver.bless('Activate document for serial.requestPort');
+  try {
+    await navigator.serial.requestPort({ filters: [] });
+    assert_unreached("Serial port access should not be allowed in this document.");
+  } catch (e) {
+    assert_equals(e.code, DOMException.SECURITY_ERR);
+  }
+  check_report_format(await report);
+}, "requestPort in serial reporting mode");
+
+promise_test(async t => {
+  const report = new Promise(resolve => {
+    new ReportingObserver((reports, observer) => resolve([reports, observer]),
+                          {types: ['feature-policy-violation']}).observe();
+  });
+
+  try {
+    await navigator.serial.getPorts();
+    assert_unreached("Serial port access should not be allowed in this document.");
+  } catch (e) {
+    assert_equals(e.code, DOMException.SECURITY_ERR);
+  }
+  check_report_format(await report);
+}, "getPorts in serial reporting mode");
+    </script>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/feature-policy/reporting/serial-reporting.https.html.headers
@@ -0,0 +1,1 @@
+Feature-Policy: serial 'none'
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/feature-policy/resources/feature-policy-serial-worker.html
@@ -0,0 +1,10 @@
+<script>
+'use strict';
+
+let worker = new Worker('feature-policy-serial-worker.js');
+
+worker.onmessage = event => {
+  window.parent.postMessage(event.data, '*');
+};
+worker.postMessage({ type: 'ready' });
+</script>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/feature-policy/resources/feature-policy-serial-worker.js
@@ -0,0 +1,14 @@
+'use strict';
+
+// Dedicated worker
+if (typeof postMessage === 'function') {
+  onmessage = event => {
+    switch(event.data.type) {
+      case 'ready':
+        navigator.serial.getPorts().then(
+            () => postMessage({ enabled: true }),
+            error => postMessage ({ enabled: false }));
+        break;
+    }
+  };
+}
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/feature-policy/resources/feature-policy-serial.html
@@ -0,0 +1,9 @@
+<script>
+'use strict';
+
+navigator.serial.getPorts().then(ports => {
+  window.parent.postMessage({ enabled: true }, '*');
+}, error => {
+  window.parent.postMessage({ enabled: false }, '*');
+});
+</script>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/serial/resources/serial-allowed-by-feature-policy-worker.js
@@ -0,0 +1,14 @@
+'use strict';
+
+importScripts('/resources/testharness.js');
+
+let workerType;
+
+if (typeof postMessage === 'function') {
+  workerType = 'dedicated';
+}
+
+promise_test(() => navigator.serial.getPorts(),
+    `Inherited header feature policy allows ${workerType} workers.`);
+
+done();
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/serial/resources/serial-disabled-by-feature-policy-worker.js
@@ -0,0 +1,17 @@
+'use strict';
+
+importScripts('/resources/testharness.js');
+
+const header = 'Feature-Policy header {"serial" : []}';
+let workerType;
+
+if (typeof postMessage === 'function') {
+  workerType = 'dedicated';
+}
+
+promise_test(() => navigator.serial.getPorts().then(
+        () => assert_unreached('expected promise to reject with SecurityError'),
+        error => assert_equals(error.name, 'SecurityError')),
+    `Inherited ${header} disallows ${workerType} workers.`);
+
+done();
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/serial/serial-allowed-by-feature-policy-attribute-redirect-on-load.https.sub.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<body>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/feature-policy/resources/featurepolicy.js></script>
+<script>
+'use strict';
+const relative_path = '/feature-policy/resources/feature-policy-serial.html';
+const base_src = '/feature-policy/resources/redirect-on-load.html#';
+const relative_worker_frame_path =
+    '/feature-policy/resources/feature-policy-serial-worker.html';
+const sub = 'https://{{domains[www]}}:{{ports[https][0]}}';
+const same_origin_src = base_src + relative_path;
+const cross_origin_src = base_src + sub + relative_path;
+const same_origin_worker_frame_src = base_src + relative_worker_frame_path;
+const cross_origin_worker_frame_src = base_src + sub +
+    relative_worker_frame_path;
+const header = 'Feature-Policy allow="serial"';
+
+async_test(t => {
+  test_feature_availability(
+      'serial.getPorts()', t, same_origin_src,
+      expect_feature_available_default, 'serial');
+}, header + ' allows same-origin relocation.');
+
+async_test(t => {
+  test_feature_availability(
+      'serial.getPorts()', t, same_origin_worker_frame_src,
+      expect_feature_available_default, 'serial');
+}, header + ' allows workers in same-origin relocation.');
+
+async_test(t => {
+  test_feature_availability(
+      'serial.getPorts()', t, cross_origin_src,
+      expect_feature_unavailable_default, 'serial');
+}, header + ' disallows cross-origin relocation.');
+
+async_test(t => {
+  test_feature_availability(
+      'serial.getPorts()', t, cross_origin_worker_frame_src,
+      expect_feature_unavailable_default, 'serial');
+}, header + ' disallows workers in cross-origin relocation.');
+</script>
+</body>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/serial/serial-allowed-by-feature-policy-attribute.https.sub.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<body>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/feature-policy/resources/featurepolicy.js></script>
+<script>
+'use strict';
+const sub = 'https://{{domains[www]}}:{{ports[https][0]}}';
+const same_origin_src = '/feature-policy/resources/feature-policy-serial.html';
+const cross_origin_src = sub + same_origin_src;
+const same_origin_worker_frame_src =
+    '/feature-policy/resources/feature-policy-serial-worker.html';
+const cross_origin_worker_frame_src = sub + same_origin_worker_frame_src;
+const feature_name = 'Feature policy "serial"';
+const header = 'allow="serial" attribute';
+
+async_test(t => {
+  test_feature_availability(
+      'serial.getPorts()', t, same_origin_src,
+      expect_feature_available_default, 'serial');
+}, feature_name + ' can be enabled in same-origin iframe using ' + header);
+
+async_test(t => {
+  test_feature_availability(
+      'serial.getPorts()', t, same_origin_worker_frame_src,
+      expect_feature_available_default, 'serial');
+}, feature_name + ' can be enabled in a worker in same-origin iframe using ' +
+    header);
+
+async_test(t => {
+  test_feature_availability(
+      'serial.getPorts()', t, cross_origin_src,
+      expect_feature_available_default, 'serial');
+}, feature_name + ' can be enabled in cross-origin iframe using ' + header);
+
+async_test(t => {
+  test_feature_availability(
+      'serial.getPorts()', t, cross_origin_worker_frame_src,
+      expect_feature_available_default, 'serial');
+}, feature_name + ' can be enabled in a worker in cross-origin iframe using ' +
+    header);
+
+fetch_tests_from_worker(new Worker(
+    'resources/serial-allowed-by-feature-policy-worker.js'));
+</script>
+</body>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/serial/serial-allowed-by-feature-policy.https.sub.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<body>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/feature-policy/resources/featurepolicy.js></script>
+<script>
+'use strict';
+const sub = 'https://{{domains[www]}}:{{ports[https][0]}}';
+const same_origin_src = '/feature-policy/resources/feature-policy-serial.html';
+const cross_origin_src = sub + same_origin_src;
+const same_origin_worker_frame_src =
+    '/feature-policy/resources/feature-policy-serial-worker.html';
+const cross_origin_worker_frame_src = sub + same_origin_worker_frame_src;
+const header = 'Feature-Policy header {"serial" : ["*"]}';
+
+promise_test(
+    () => navigator.serial.getPorts(),
+    header + ' allows the top-level document.');
+
+async_test(t => {
+  test_feature_availability('serial.getPorts()', t, same_origin_src,
+      expect_feature_available_default);
+}, header + ' allows same-origin iframes.');
+
+async_test(t => {
+  test_feature_availability('serial.getPorts()', t, same_origin_worker_frame_src,
+      expect_feature_available_default);
+}, header + ' allows workers in same-origin iframes.');
+
+async_test(t => {
+  test_feature_availability('serial.getPorts()', t, cross_origin_src,
+      expect_feature_available_default);
+}, header + ' allows cross-origin iframes.');
+
+async_test(t => {
+  test_feature_availability('serial.getPorts()', t,
+      cross_origin_worker_frame_src,
+      expect_feature_available_default);
+}, header + ' allows workers in cross-origin iframes.');
+
+fetch_tests_from_worker(new Worker(
+    'resources/serial-allowed-by-feature-policy-worker.js'));
+</script>
+</body>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/serial/serial-allowed-by-feature-policy.https.sub.html.headers
@@ -0,0 +1,1 @@
+Feature-Policy: serial *
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/serial/serial-default-feature-policy.https.sub.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<body>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/feature-policy/resources/featurepolicy.js></script>
+<script>
+'use strict';
+var same_origin_src = '/feature-policy/resources/feature-policy-serial.html';
+var cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' +
+  same_origin_src;
+var header = 'Default "serial" feature policy ["self"]';
+
+promise_test(
+    () => navigator.serial.getPorts(),
+    header + ' allows the top-level document.');
+
+async_test(t => {
+  test_feature_availability('serial.getPorts()', t, same_origin_src,
+      expect_feature_available_default);
+}, header + ' allows same-origin iframes.');
+
+async_test(t => {
+  test_feature_availability('serial.getPorts()', t, cross_origin_src,
+      expect_feature_unavailable_default);
+}, header + ' disallows cross-origin iframes.');
+</script>
+</body>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/serial/serial-disabled-by-feature-policy.https.sub.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/feature-policy/resources/featurepolicy.js"></script>
+<script>
+'use strict';
+const sub = 'https://{{domains[www]}}:{{ports[https][0]}}';
+const same_origin_src = '/feature-policy/resources/feature-policy-serial.html';
+const cross_origin_src = sub + same_origin_src;
+const same_origin_worker_frame_src =
+    '/feature-policy/resources/feature-policy-serial-worker.html';
+const cross_origin_worker_frame_src = sub + same_origin_worker_frame_src;
+const header = 'Feature-Policy header {"serial" : []}';
+
+promise_test(() => {
+  return navigator.serial.getPorts().then(() => {
+    assert_unreached('expected promise to reject with SecurityError');
+  }, error => {
+    assert_equals(error.name, 'SecurityError');
+  });
+}, header + ' disallows getPorts in the top-level document.');
+
+async_test(t => {
+  test_feature_availability('serial.getPorts()', t, same_origin_src,
+      expect_feature_unavailable_default);
+}, header + ' disallows same-origin iframes.');
+
+async_test(t => {
+  test_feature_availability('serial.getPorts()', t, same_origin_worker_frame_src,
+      expect_feature_unavailable_default);
+}, header + ' disallows workers in same-origin iframes.');
+
+async_test(t => {
+  test_feature_availability('serial.getPorts()', t, cross_origin_src,
+      expect_feature_unavailable_default);
+}, header + ' disallows cross-origin iframes.');
+
+async_test(t => {
+  test_feature_availability('serial.getPorts()', t,
+      cross_origin_worker_frame_src,
+      expect_feature_unavailable_default);
+}, header + ' disallows workers in cross-origin iframes.');
+
+fetch_tests_from_worker(new Worker(
+    'resources/serial-disabled-by-feature-policy-worker.js'));
+</script>
+</body>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/serial/serial-disabled-by-feature-policy.https.sub.html.headers
@@ -0,0 +1,1 @@
+Feature-Policy: serial 'none'