Bug 1521808 - Add xpcshell-test for CrossOriginOpenerPolicy r=mayhemer,nika,annevk
☠☠ backed out by a791ece512e1 ☠ ☠
authorValentin Gosu <valentin.gosu@gmail.com>
Tue, 12 Feb 2019 12:16:28 +0000
changeset 458672 c8c151d92c036ce95b5deb097e95e5aab9ab34fc
parent 458671 cc0a5c7dabb45dcdfd2a2b723c950cb7f1baf322
child 458673 80aeea5f7abe0a5c98c5c2f8c9de7d152c2a82e2
push id35543
push userccoroiu@mozilla.com
push dateTue, 12 Feb 2019 16:27:27 +0000
treeherdermozilla-central@4ad4e42bcb99 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmayhemer, nika, annevk
bugs1521808
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 1521808 - Add xpcshell-test for CrossOriginOpenerPolicy r=mayhemer,nika,annevk Differential Revision: https://phabricator.services.mozilla.com/D18246
netwerk/test/unit/test_crossOriginOpenerPolicy.js
netwerk/test/unit/xpcshell.ini
netwerk/test/unit_ipc/test_crossOriginOpenerPolicy_wrap.js
netwerk/test/unit_ipc/xpcshell.ini
new file mode 100644
--- /dev/null
+++ b/netwerk/test/unit/test_crossOriginOpenerPolicy.js
@@ -0,0 +1,223 @@
+"use strict";
+
+const {HttpServer} = ChromeUtils.import("resource://testing-common/httpd.js");
+const {NetUtil} = ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+let testServer = null;
+
+function inChildProcess() {
+  return Cc["@mozilla.org/xre/app-info;1"]
+           .getService(Ci.nsIXULRuntime)
+           .processType != Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
+}
+
+class OnStartListener {
+  constructor(onStartCallback) {
+    this.callback = onStartCallback;
+    this.done = new Promise(resolve => { this.resolve = resolve; });
+  }
+
+  onStartRequest(request, context) {
+    this.callback(request.QueryInterface(Ci.nsIHttpChannel));
+  }
+
+  onDataAvailable(request, context, stream, offset, count) {
+    let string = NetUtil.readInputStreamToString(stream, count);
+  }
+
+  onStopRequest(request, context, status) { this.resolve(); }
+}
+
+
+function handler_null(metadata, response)
+{
+  info("request_null");
+  response.setStatusLine(metadata.httpVersion, 200, "OK");
+  response.setHeader("Content-Type", "text/plain", false);
+  let body = "foo";
+  response.bodyOutputStream.write(body, body.length);
+}
+
+function handler_same_origin(metadata, response)
+{
+  info("request_same_origin");
+  response.setStatusLine(metadata.httpVersion, 200, "OK");
+  response.setHeader("Content-Type", "text/plain", false);
+  response.setHeader("Cross-Origin-Opener-Policy", "same-origin", false);
+  let body = "foo";
+  response.bodyOutputStream.write(body, body.length);
+}
+
+function handler_same_origin_allow_outgoing(metadata, response)
+{
+  info("request_same_origin");
+  response.setStatusLine(metadata.httpVersion, 200, "OK");
+  response.setHeader("Content-Type", "text/plain", false);
+  response.setHeader("Cross-Origin-Opener-Policy", "same-origin unsafe-allow-outgoing", false);
+  let body = "foo";
+  response.bodyOutputStream.write(body, body.length);
+}
+
+function handler_same_site(metadata, response)
+{
+  info("request_same_site");
+  response.setStatusLine(metadata.httpVersion, 200, "OK");
+  response.setHeader("Content-Type", "text/plain", false);
+  response.setHeader("Cross-Origin-Opener-Policy", "same-site", false);
+  let body = "foo";
+  response.bodyOutputStream.write(body, body.length);
+}
+
+function handler_same_site_allow_outgoing(metadata, response)
+{
+  info("request_same_site");
+  response.setStatusLine(metadata.httpVersion, 200, "OK");
+  response.setHeader("Content-Type", "text/plain", false);
+  response.setHeader("Cross-Origin-Opener-Policy", "same-site unsafe-allow-outgoing", false);
+  let body = "foo";
+  response.bodyOutputStream.write(body, body.length);
+}
+
+add_task(async function test_setup() {
+  if (!inChildProcess()) {
+    Services.prefs.setCharPref("network.dns.localDomains", "foo.example.com, example.com, example.org");
+    registerCleanupFunction(function() {
+      Services.prefs.clearUserPref("network.dns.localDomains");
+    });
+  }
+
+  testServer = new HttpServer();
+  testServer.start(-1);
+  registerCleanupFunction(() => testServer.stop(() => {}));
+  testServer.registerPathHandler("/null", handler_null);
+  testServer.registerPathHandler("/same-origin", handler_same_origin);
+  testServer.registerPathHandler("/same-origin_allow-outgoing", handler_same_origin_allow_outgoing);
+  testServer.registerPathHandler("/same-site", handler_same_site);
+  testServer.registerPathHandler("/same-site_allow-outgoing", handler_same_site_allow_outgoing);
+  testServer.identity.setPrimary("http", "example.com", testServer.identity.primaryPort);
+});
+
+// policyA, originA, policyB, originB
+async function generate_test(policyA, originA, policyB, originB, expectedResult, contentPolicyType = Ci.nsIContentPolicy.TYPE_DOCUMENT) {
+  let listener = new OnStartListener((channel) => {
+    if (!inChildProcess()) {
+      equal(channel.hasCrossOriginOpenerPolicyMismatch(), expectedResult, `check for mismatch testing ${policyA}, ${originA}, ${policyB}, ${originB}, ${expectedResult}`);
+    }
+  });
+
+  let chan = NetUtil.newChannel({
+    uri: `${originB}:${testServer.identity.primaryPort}/${policyB}`,
+    loadUsingSystemPrincipal: true,
+    contentPolicyType: contentPolicyType,
+  }).QueryInterface(Ci.nsIHttpChannel);
+
+  if (policyA == "null") {
+    chan.loadInfo.openerPolicy = Ci.nsILoadInfo.OPENER_POLICY_NULL;
+  } else if (policyA == "same-origin") {
+    chan.loadInfo.openerPolicy = Ci.nsILoadInfo.OPENER_POLICY_SAME_ORIGIN;
+  } else if (policyA == "same-origin_allow-outgoing") {
+    chan.loadInfo.openerPolicy = Ci.nsILoadInfo.OPENER_POLICY_SAME_ORIGIN_ALLOW_OUTGOING;
+  } else if (policyA == "same-site") {
+    chan.loadInfo.openerPolicy = Ci.nsILoadInfo.OPENER_POLICY_SAME_SITE;
+  } else if (policyA == "same-site_allow-outgoing") {
+    chan.loadInfo.openerPolicy = Ci.nsILoadInfo.OPENER_POLICY_SAME_SITE_ALLOW_OUTGOING;
+  }
+
+  let principalA = Services.scriptSecurityManager.createNullPrincipal({});
+  if (originA != "about:blank") {
+    principalA = Services.scriptSecurityManager.createCodebasePrincipal(Services.io.newURI(`${originA}:${testServer.identity.primaryPort}/${policyA}`), {});
+  }
+
+  chan.QueryInterface(Ci.nsIHttpChannelInternal).setTopWindowPrincipal(principalA);
+  equal(chan.loadInfo.externalContentPolicyType, contentPolicyType);
+
+  if (inChildProcess()) {
+    do_send_remote_message("prepare-test", {"channelId": chan.channelId,
+                                            "policyA": policyA,
+                                            "originA": `${originA}`,
+                                            "policyB": policyB,
+                                            "originB": `${originB}`,
+                                            "expectedResult": expectedResult});
+    await do_await_remote_message("test-ready");
+  }
+
+  chan.asyncOpen2(listener);
+  return listener.done;
+}
+
+add_task(async function test_policies() {
+  // Note: This test only verifies that the result of
+  // nsIHttpChannel.hasCrossOriginOpenerPolicyMismatch() is correct. It does not
+  // check that the header is honored for auxiliary browsing contexts, how it
+  // affects a top-level browsing context opened from a sandboxed iframe,
+  // whether it affects the browsing context name, etc
+
+  await generate_test("null", "http://example.com", "null", "http://example.com", false);
+  await generate_test("null", "http://example.com", "same-origin", "http://example.com", true);
+  await generate_test("null", "http://example.com", "same-origin_allow-outgoing", "http://example.com", true);
+  await generate_test("null", "http://example.com", "same-site", "http://example.com", true);
+  await generate_test("null", "http://example.com", "same-site_allow-outgoing", "http://example.com", true);
+  await generate_test("null", "about:blank", "null", "http://example.com", false);
+  await generate_test("null", "about:blank", "same-origin", "http://example.com", true);
+  await generate_test("null", "about:blank", "same-origin_allow-outgoing", "http://example.com", true);
+  await generate_test("null", "about:blank", "same-site", "http://example.com", true);
+  await generate_test("null", "about:blank", "same-site_allow-outgoing", "http://example.com", true);
+
+  await generate_test("same-origin", "http://example.com", "null", "http://example.com", true);
+  await generate_test("same-origin_allow-outgoing", "http://example.com", "null", "http://example.com", true);
+  await generate_test("same-origin", "about:blank", "null", "http://example.com", true);
+  await generate_test("same-origin_allow-outgoing", "about:blank", "null", "http://example.com", false);
+  await generate_test("same-origin", "http://example.com", "same-origin", "http://example.com", false);
+  await generate_test("same-origin_allow-outgoing", "http://example.com", "same-origin", "http://example.com", true);
+  await generate_test("same-origin", "http://example.com", "same-origin_allow-outgoing", "http://example.com", true);
+  await generate_test("same-origin_allow-outgoing", "http://example.com", "same-origin_allow-outgoing", "http://example.com", false);
+  await generate_test("same-origin", "https://example.com", "same-origin", "http://example.com", true);
+  // true because policyB is not null
+  await generate_test("same-origin", "about:blank", "same-origin", "http://example.com", true);
+  await generate_test("same-origin_allow-outgoing", "about:blank", "same-origin", "http://example.com", true);
+  await generate_test("same-origin", "about:blank", "same-origin_allow-outgoing", "http://example.com", true);
+  await generate_test("same-origin_allow-outgoing", "about:blank", "same-origin_allow-outgoing", "http://example.com", true);
+  await generate_test("same-origin", "http://foo.example.com", "same-origin", "http://example.com", true);
+  await generate_test("same-origin_allow-outgoing", "http://foo.example.com", "same-origin", "http://example.com", true);
+  await generate_test("same-origin", "http://foo.example.com", "same-origin_allow-outgoing", "http://example.com", true);
+  await generate_test("same-origin_allow-outgoing", "http://foo.example.com", "same-origin_allow-outgoing", "http://example.com", true);
+  await generate_test("same-origin", "http://example.org", "same-origin", "http://example.com", true);
+  await generate_test("same-origin_allow-outgoing", "http://example.org", "same-origin", "http://example.com", true);
+  await generate_test("same-origin", "http://example.org", "same-origin_allow-outgoing", "http://example.com", true);
+  await generate_test("same-origin_allow-outgoing", "http://example.org", "same-origin_allow-outgoing", "http://example.com", true);
+  await generate_test("same-origin", "http://example.com", "same-site", "http://example.com", true);
+  await generate_test("same-origin_allow-outgoing", "http://example.com", "same-site", "http://example.com", true);
+  await generate_test("same-origin", "http://example.com", "same-site_allow-outgoing", "http://example.com", true);
+  await generate_test("same-origin_allow-outgoing", "http://example.com", "same-site_allow-outgoing", "http://example.com", true);
+
+  await generate_test("same-site", "http://example.com", "null", "http://example.com", true);
+  await generate_test("same-site_allow-outgoing", "http://example.com", "null", "http://example.com", true);
+  await generate_test("same-site", "about:blank", "null", "http://example.com", true);
+  await generate_test("same-site_allow-outgoing", "about:blank", "null", "http://example.com", false);
+  await generate_test("same-site", "http://example.com", "same-origin", "http://example.com", true);
+  await generate_test("same-site_allow-outgoing", "http://example.com", "same-origin", "http://example.com", true);
+  await generate_test("same-site", "http://example.com", "same-origin_allow-outgoing", "http://example.com", true);
+  await generate_test("same-site_allow-outgoing", "http://example.com", "same-origin_allow-outgoing", "http://example.com", true);
+  // true because policyB is not null
+  await generate_test("same-site", "about:blank", "same-origin", "http://example.com", true);
+  await generate_test("same-site_allow-outgoing", "about:blank", "same-origin", "http://example.com", true);
+  await generate_test("same-site", "about:blank", "same-origin_allow-outgoing", "http://example.com", true);
+  await generate_test("same-site_allow-outgoing", "about:blank", "same-origin_allow-outgoing", "http://example.com", true);
+  // true because they have different schemes
+  await generate_test("same-site", "https://example.com", "same-site", "http://example.com", true);
+  await generate_test("same-site_allow-outgoing", "https://example.com", "same-site", "http://example.com", true);
+  await generate_test("same-site", "https://example.com", "same-site_allow-outgoing", "http://example.com", true);
+  await generate_test("same-site_allow-outgoing", "https://example.com", "same-site_allow-outgoing", "http://example.com", true);
+
+  // These return false because the contentPolicyType is not TYPE_DOCUMENT
+  await generate_test("null", "http://example.com", "same-origin", "http://example.com", false, Ci.nsIContentPolicy.TYPE_OTHER);
+  await generate_test("same-origin", "http://example.com", "null", "http://example.com", false, Ci.nsIContentPolicy.TYPE_OTHER);
+
+  if (inChildProcess()) {
+    // send one message with no payload to clear the listener
+    info("finishing");
+    do_send_remote_message("prepare-test");
+    await do_await_remote_message("test-ready");
+  }
+});
--- a/netwerk/test/unit/xpcshell.ini
+++ b/netwerk/test/unit/xpcshell.ini
@@ -422,8 +422,9 @@ skip-if = os == "android" # CP service i
 run-sequentially = node server exceptions dont replay well
 [test_esni_dns_fetch.js]
 # http2-using tests require node available
 skip-if = os == "android"
 [test_network_connectivity_service.js]
 skip-if = os == "android" # DNSv6 issues on android
 [test_suspend_channel_on_authRetry.js]
 [test_suspend_channel_on_examine_merged_response.js]
+[test_crossOriginOpenerPolicy.js]
new file mode 100644
--- /dev/null
+++ b/netwerk/test/unit_ipc/test_crossOriginOpenerPolicy_wrap.js
@@ -0,0 +1,47 @@
+"use strict";
+
+const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+// This holds information about the test that is currently running.
+// We can only call nsIHttpChannel.crossOriginOpenerPolicyMismatch
+// in the main process, so for each channel opened in the child process we need
+// to perform the check in the parent. We intercept it during onStartRequest
+// with the "http-on-examine-response" observer notification.
+let gData = null;
+
+function observer(subject, topic, state) {
+  info("observer called with " + topic);
+  let chan = subject.QueryInterface(Ci.nsIHttpChannel);
+  equal(chan.channelId, gData.channelId);
+  equal(chan.hasCrossOriginOpenerPolicyMismatch(), gData.expectedResult, `check for mismatch testing ${gData.policyA}, ${gData.originA}, ${gData.policyB}, ${gData.originB}, ${gData.expectedResult}`);
+}
+
+function waitForTest() {
+  do_await_remote_message("prepare-test").then((data) => {
+    info(`prepare test: ${data}`);
+    gData = data;
+    do_send_remote_message("test-ready");
+
+    // So we don't hang, the child must send a final message with no data
+    // so we can clear the listener. Otherwise we call waitForTest again.
+    if (!data) {
+      info("parent test finishing");
+      return;
+    }
+    waitForTest();
+  });
+}
+
+function run_test() {
+  Services.obs.addObserver(observer, "http-on-examine-response");
+  Services.prefs.setCharPref("network.dns.localDomains", "foo.example.com, example.com, example.org");
+
+  registerCleanupFunction(function() {
+    Services.prefs.clearUserPref("network.dns.localDomains");
+    Services.obs.removeObserver(observer, "http-on-examine-response");
+  });
+
+  waitForTest();
+
+  run_test_in_child("../unit/test_crossOriginOpenerPolicy.js");
+}
--- a/netwerk/test/unit_ipc/xpcshell.ini
+++ b/netwerk/test/unit_ipc/xpcshell.ini
@@ -52,16 +52,17 @@ support-files =
   !/netwerk/test/unit/data/test_readline8.txt
   !/netwerk/test/unit/data/signed_win.exe
   !/netwerk/test/unit/test_alt-data_simple.js
   !/netwerk/test/unit/test_alt-data_stream.js
   !/netwerk/test/unit/test_channel_priority.js
   !/netwerk/test/unit/test_multipart_streamconv.js
   !/netwerk/test/unit/test_original_sent_received_head.js
   !/netwerk/test/unit/test_alt-data_cross_process.js
+  !/netwerk/test/unit/test_crossOriginOpenerPolicy.js
   child_cookie_header.js
 
 [test_bug528292_wrap.js]
 [test_cookie_header_stripped.js]
 [test_cacheflags_wrap.js]
 [test_cache-entry-id_wrap.js]
 [test_cache_jar_wrap.js]
 [test_channel_close_wrap.js]
@@ -99,8 +100,9 @@ skip-if = true
 [test_alt-data_stream_wrap.js]
 [test_original_sent_received_head_wrap.js]
 [test_channel_id.js]
 [test_trackingProtection_annotateChannels_wrap1.js]
 [test_trackingProtection_annotateChannels_wrap2.js]
 [test_channel_priority_wrap.js]
 [test_multipart_streamconv_wrap.js]
 [test_alt-data_cross_process_wrap.js]
+[test_crossOriginOpenerPolicy_wrap.js]