summaryrefslogtreecommitdiffstats
path: root/dom/security/test/unit
diff options
context:
space:
mode:
Diffstat (limited to 'dom/security/test/unit')
-rw-r--r--dom/security/test/unit/test_csp_reports.js299
-rw-r--r--dom/security/test/unit/test_csp_upgrade_insecure_request_header.js102
-rw-r--r--dom/security/test/unit/test_deserialization_format_before_100.js244
-rw-r--r--dom/security/test/unit/test_https_only_https_first_default_port.js107
-rw-r--r--dom/security/test/unit/test_https_only_https_first_prefs.js360
-rw-r--r--dom/security/test/unit/test_isOriginPotentiallyTrustworthy.js51
-rw-r--r--dom/security/test/unit/xpcshell.ini11
7 files changed, 1174 insertions, 0 deletions
diff --git a/dom/security/test/unit/test_csp_reports.js b/dom/security/test/unit/test_csp_reports.js
new file mode 100644
index 0000000000..5cc9f64eb8
--- /dev/null
+++ b/dom/security/test/unit/test_csp_reports.js
@@ -0,0 +1,299 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const { NetUtil } = ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
+const { HttpServer } = ChromeUtils.import("resource://testing-common/httpd.js");
+
+var httpServer = new HttpServer();
+httpServer.start(-1);
+var testsToFinish = 0;
+
+var principal;
+
+const REPORT_SERVER_PORT = httpServer.identity.primaryPort;
+const REPORT_SERVER_URI = "http://localhost";
+
+/**
+ * Construct a callback that listens to a report submission and either passes
+ * or fails a test based on what it gets.
+ */
+function makeReportHandler(testpath, message, expectedJSON) {
+ return function (request, response) {
+ // we only like "POST" submissions for reports!
+ if (request.method !== "POST") {
+ do_throw("violation report should be a POST request");
+ return;
+ }
+
+ // check content-type of report is "application/csp-report"
+ var contentType = request.hasHeader("Content-Type")
+ ? request.getHeader("Content-Type")
+ : undefined;
+ if (contentType !== "application/csp-report") {
+ do_throw(
+ "violation report should have the 'application/csp-report' " +
+ "content-type, when in fact it is " +
+ contentType.toString()
+ );
+ }
+
+ // obtain violation report
+ var reportObj = JSON.parse(
+ NetUtil.readInputStreamToString(
+ request.bodyInputStream,
+ request.bodyInputStream.available()
+ )
+ );
+
+ // dump("GOT REPORT:\n" + JSON.stringify(reportObj) + "\n");
+ // dump("TESTPATH: " + testpath + "\n");
+ // dump("EXPECTED: \n" + JSON.stringify(expectedJSON) + "\n\n");
+
+ for (var i in expectedJSON) {
+ Assert.equal(expectedJSON[i], reportObj["csp-report"][i]);
+ }
+
+ testsToFinish--;
+ httpServer.registerPathHandler(testpath, null);
+ if (testsToFinish < 1) {
+ httpServer.stop(do_test_finished);
+ } else {
+ do_test_finished();
+ }
+ };
+}
+
+/**
+ * Everything created by this assumes it will cause a report. If you want to
+ * add a test here that will *not* cause a report to go out, you're gonna have
+ * to make sure the test cleans up after itself.
+ */
+function makeTest(id, expectedJSON, useReportOnlyPolicy, callback) {
+ testsToFinish++;
+ do_test_pending();
+
+ // set up a new CSP instance for each test.
+ var csp = Cc["@mozilla.org/cspcontext;1"].createInstance(
+ Ci.nsIContentSecurityPolicy
+ );
+ var policy =
+ "default-src 'none' 'report-sample'; " +
+ "report-uri " +
+ REPORT_SERVER_URI +
+ ":" +
+ REPORT_SERVER_PORT +
+ "/test" +
+ id;
+ var selfuri = NetUtil.newURI(
+ REPORT_SERVER_URI + ":" + REPORT_SERVER_PORT + "/foo/self"
+ );
+
+ dump("Created test " + id + " : " + policy + "\n\n");
+
+ principal = Services.scriptSecurityManager.createContentPrincipal(
+ selfuri,
+ {}
+ );
+ csp.setRequestContextWithPrincipal(principal, selfuri, "", 0);
+
+ // Load up the policy
+ // set as report-only if that's the case
+ csp.appendPolicy(policy, useReportOnlyPolicy, false);
+
+ // prime the report server
+ var handler = makeReportHandler("/test" + id, "Test " + id, expectedJSON);
+ httpServer.registerPathHandler("/test" + id, handler);
+
+ // trigger the violation
+ callback(csp);
+}
+
+function run_test() {
+ do_get_profile();
+
+ var selfuri = NetUtil.newURI(
+ REPORT_SERVER_URI + ":" + REPORT_SERVER_PORT + "/foo/self"
+ );
+
+ // test that inline script violations cause a report.
+ makeTest(
+ 0,
+ {
+ "blocked-uri": "inline",
+ "effective-directive": "script-src-elem",
+ disposition: "enforce",
+ },
+ false,
+ function (csp) {
+ let inlineOK = true;
+ inlineOK = csp.getAllowsInline(
+ Ci.nsIContentSecurityPolicy.SCRIPT_SRC_ELEM_DIRECTIVE,
+ false, // aHasUnsafeHash
+ "", // aNonce
+ false, // aParserCreated
+ null, // aTriggeringElement
+ null, // nsICSPEventListener
+ "", // aContentOfPseudoScript
+ 0, // aLineNumber
+ 0
+ ); // aColumnNumber
+
+ // this is not a report only policy, so it better block inline scripts
+ Assert.ok(!inlineOK);
+ }
+ );
+
+ // test that eval violations cause a report.
+ makeTest(
+ 1,
+ {
+ "blocked-uri": "eval",
+ // JSON script-sample is UTF8 encoded
+ "script-sample": "\xc2\xa3\xc2\xa5\xc2\xb5\xe5\x8c\x97\xf0\xa0\x9d\xb9",
+ "line-number": 1,
+ "column-number": 2,
+ },
+ false,
+ function (csp) {
+ let evalOK = true,
+ oReportViolation = { value: false };
+ evalOK = csp.getAllowsEval(oReportViolation);
+
+ // this is not a report only policy, so it better block eval
+ Assert.ok(!evalOK);
+ // ... and cause reports to go out
+ Assert.ok(oReportViolation.value);
+
+ if (oReportViolation.value) {
+ // force the logging, since the getter doesn't.
+ csp.logViolationDetails(
+ Ci.nsIContentSecurityPolicy.VIOLATION_TYPE_EVAL,
+ null, // aTriggeringElement
+ null, // nsICSPEventListener
+ selfuri.asciiSpec,
+ // sending UTF-16 script sample to make sure
+ // csp report in JSON is not cut-off, please
+ // note that JSON is UTF8 encoded.
+ "\u00a3\u00a5\u00b5\u5317\ud841\udf79",
+ 1, // line number
+ 2
+ ); // column number
+ }
+ }
+ );
+
+ makeTest(
+ 2,
+ { "blocked-uri": "http://blocked.test/foo.js" },
+ false,
+ function (csp) {
+ // shouldLoad creates and sends out the report here.
+ csp.shouldLoad(
+ Ci.nsIContentPolicy.TYPE_SCRIPT,
+ null, // nsICSPEventListener
+ NetUtil.newURI("http://blocked.test/foo.js"),
+ null,
+ true,
+ null,
+ false
+ );
+ }
+ );
+
+ // test that inline script violations cause a report in report-only policy
+ makeTest(
+ 3,
+ { "blocked-uri": "inline", disposition: "report" },
+ true,
+ function (csp) {
+ let inlineOK = true;
+ inlineOK = csp.getAllowsInline(
+ Ci.nsIContentSecurityPolicy.SCRIPT_SRC_ELEM_DIRECTIVE,
+ false, // aHasUnsafeHash
+ "", // aNonce
+ false, // aParserCreated
+ null, // aTriggeringElement
+ null, // nsICSPEventListener
+ "", // aContentOfPseudoScript
+ 0, // aLineNumber
+ 0
+ ); // aColumnNumber
+
+ // this is a report only policy, so it better allow inline scripts
+ Assert.ok(inlineOK);
+ }
+ );
+
+ // test that eval violations cause a report in report-only policy
+ makeTest(4, { "blocked-uri": "eval" }, true, function (csp) {
+ let evalOK = true,
+ oReportViolation = { value: false };
+ evalOK = csp.getAllowsEval(oReportViolation);
+
+ // this is a report only policy, so it better allow eval
+ Assert.ok(evalOK);
+ // ... but still cause reports to go out
+ Assert.ok(oReportViolation.value);
+
+ if (oReportViolation.value) {
+ // force the logging, since the getter doesn't.
+ csp.logViolationDetails(
+ Ci.nsIContentSecurityPolicy.VIOLATION_TYPE_EVAL,
+ null, // aTriggeringElement
+ null, // nsICSPEventListener
+ selfuri.asciiSpec,
+ "script sample",
+ 4, // line number
+ 5
+ ); // column number
+ }
+ });
+
+ // test that only the uri's scheme is reported for globally unique identifiers
+ makeTest(5, { "blocked-uri": "data" }, false, function (csp) {
+ var base64data =
+ "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12" +
+ "P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==";
+ // shouldLoad creates and sends out the report here.
+ csp.shouldLoad(
+ Ci.nsIContentPolicy.TYPE_IMAGE,
+ null, // nsICSPEventListener
+ NetUtil.newURI("data:image/png;base64," + base64data),
+ null,
+ true,
+ null,
+ false
+ );
+ });
+
+ // test that only the uri's scheme is reported for globally unique identifiers
+ makeTest(6, { "blocked-uri": "intent" }, false, function (csp) {
+ // shouldLoad creates and sends out the report here.
+ csp.shouldLoad(
+ Ci.nsIContentPolicy.TYPE_SUBDOCUMENT,
+ null, // nsICSPEventListener
+ NetUtil.newURI("intent://mymaps.com/maps?um=1&ie=UTF-8&fb=1&sll"),
+ null,
+ true,
+ null,
+ false
+ );
+ });
+
+ // test fragment removal
+ var selfSpec =
+ REPORT_SERVER_URI + ":" + REPORT_SERVER_PORT + "/foo/self/foo.js";
+ makeTest(7, { "blocked-uri": selfSpec }, false, function (csp) {
+ // shouldLoad creates and sends out the report here.
+ csp.shouldLoad(
+ Ci.nsIContentPolicy.TYPE_SCRIPT,
+ null, // nsICSPEventListener
+ NetUtil.newURI(selfSpec + "#bar"),
+ null,
+ true,
+ null,
+ false
+ );
+ });
+}
diff --git a/dom/security/test/unit/test_csp_upgrade_insecure_request_header.js b/dom/security/test/unit/test_csp_upgrade_insecure_request_header.js
new file mode 100644
index 0000000000..715ecbc979
--- /dev/null
+++ b/dom/security/test/unit/test_csp_upgrade_insecure_request_header.js
@@ -0,0 +1,102 @@
+const { HttpServer } = ChromeUtils.import("resource://testing-common/httpd.js");
+const { NetUtil } = ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
+const { XPCOMUtils } = ChromeUtils.importESModule(
+ "resource://gre/modules/XPCOMUtils.sys.mjs"
+);
+
+// Since this test creates a TYPE_DOCUMENT channel via javascript, it will
+// end up using the wrong LoadInfo constructor. Setting this pref will disable
+// the ContentPolicyType assertion in the constructor.
+Services.prefs.setBoolPref("network.loadinfo.skip_type_assertion", true);
+
+XPCOMUtils.defineLazyGetter(this, "URL", function () {
+ return "http://localhost:" + httpserver.identity.primaryPort;
+});
+
+var httpserver = null;
+var channel = null;
+var curTest = null;
+var testpath = "/footpath";
+
+var tests = [
+ {
+ description: "should not set request header for TYPE_OTHER",
+ expectingHeader: false,
+ contentType: Ci.nsIContentPolicy.TYPE_OTHER,
+ },
+ {
+ description: "should set request header for TYPE_DOCUMENT",
+ expectingHeader: true,
+ contentType: Ci.nsIContentPolicy.TYPE_DOCUMENT,
+ },
+ {
+ description: "should set request header for TYPE_SUBDOCUMENT",
+ expectingHeader: true,
+ contentType: Ci.nsIContentPolicy.TYPE_SUBDOCUMENT,
+ },
+ {
+ description: "should not set request header for TYPE_IMAGE",
+ expectingHeader: false,
+ contentType: Ci.nsIContentPolicy.TYPE_IMAGE,
+ },
+];
+
+function ChannelListener() {}
+
+ChannelListener.prototype = {
+ onStartRequest(request) {},
+ onDataAvailable(request, stream, offset, count) {
+ do_throw("Should not get any data!");
+ },
+ onStopRequest(request, status) {
+ var upgrade_insecure_header = false;
+ try {
+ if (request.getRequestHeader("Upgrade-Insecure-Requests")) {
+ upgrade_insecure_header = true;
+ }
+ } catch (e) {
+ // exception is thrown if header is not available on the request
+ }
+ // debug
+ // dump("executing test: " + curTest.description);
+ Assert.equal(upgrade_insecure_header, curTest.expectingHeader);
+ run_next_test();
+ },
+};
+
+function setupChannel(aContentType) {
+ var chan = NetUtil.newChannel({
+ uri: URL + testpath,
+ loadUsingSystemPrincipal: true,
+ contentPolicyType: aContentType,
+ });
+ chan.QueryInterface(Ci.nsIHttpChannel);
+ chan.requestMethod = "GET";
+ return chan;
+}
+
+function serverHandler(metadata, response) {
+ // no need to perform anything here
+}
+
+function run_next_test() {
+ curTest = tests.shift();
+ if (!curTest) {
+ httpserver.stop(do_test_finished);
+ return;
+ }
+ channel = setupChannel(curTest.contentType);
+ channel.asyncOpen(new ChannelListener());
+}
+
+function run_test() {
+ do_get_profile();
+
+ // set up the test environment
+ httpserver = new HttpServer();
+ httpserver.registerPathHandler(testpath, serverHandler);
+ httpserver.start(-1);
+
+ run_next_test();
+ do_test_pending();
+}
diff --git a/dom/security/test/unit/test_deserialization_format_before_100.js b/dom/security/test/unit/test_deserialization_format_before_100.js
new file mode 100644
index 0000000000..a21ae4838a
--- /dev/null
+++ b/dom/security/test/unit/test_deserialization_format_before_100.js
@@ -0,0 +1,244 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const ReferrerInfo = Components.Constructor(
+ "@mozilla.org/referrer-info;1",
+ "nsIReferrerInfo",
+ "init"
+);
+
+async function runTest(setupFunc, expected) {
+ let objectOutStream = Cc["@mozilla.org/binaryoutputstream;1"].createInstance(
+ Ci.nsIObjectOutputStream
+ );
+ let pipe = Cc["@mozilla.org/pipe;1"].createInstance(Ci.nsIPipe);
+ pipe.init(
+ false /* non-blocking input */,
+ false /* non-blocking output */,
+ 0 /* segment size */,
+ 0 /* max segments */
+ );
+ objectOutStream.setOutputStream(pipe.outputStream);
+
+ setupFunc(objectOutStream);
+
+ let objectInStream = Cc["@mozilla.org/binaryinputstream;1"].createInstance(
+ Ci.nsIObjectInputStream
+ );
+ objectInStream.setInputStream(pipe.inputStream);
+
+ let referrerInfo = new ReferrerInfo(Ci.nsIReferrerInfo.EMPTY);
+ try {
+ referrerInfo.read(objectInStream);
+ } catch (e) {
+ Assert.ok(false, "Shouldn't fail when deserializing.");
+ return;
+ }
+
+ Assert.ok(true, "Successfully deserialize the referrerInfo.");
+
+ let { referrerPolicy, sendReferrer, computedReferrerSpec } = expected;
+ Assert.equal(
+ referrerInfo.referrerPolicy,
+ referrerPolicy,
+ "The referrerInfo has the expected referrer policy."
+ );
+
+ Assert.equal(
+ referrerInfo.sendReferrer,
+ sendReferrer,
+ "The referrerInfo has the expected sendReferrer value."
+ );
+
+ if (computedReferrerSpec) {
+ Assert.equal(
+ referrerInfo.computedReferrerSpec,
+ computedReferrerSpec,
+ "The referrerInfo has the expected computedReferrerSpec value."
+ );
+ }
+}
+
+// Test deserializing referrer info with the old format.
+add_task(async function test_deserializeOldReferrerInfo() {
+ // Test with a regular old format.
+ await runTest(
+ stream => {
+ // Write to the output stream with the old format.
+ stream.writeBoolean(true); // nonNull
+ stream.writeStringZ("https://example.com/"); // spec
+ stream.write32(Ci.nsIReferrerInfo.STRICT_ORIGIN_WHEN_CROSS_ORIGIN); // policy
+ stream.writeBoolean(false); // sendReferrer
+ stream.writeBoolean(false); // isComputed
+ stream.writeBoolean(true); // initialized
+ stream.writeBoolean(false); // overridePolicyByDefault
+ },
+ {
+ referrerPolicy: Ci.nsIReferrerInfo.STRICT_ORIGIN_WHEN_CROSS_ORIGIN,
+ sendReferrer: false,
+ }
+ );
+
+ // Test with an old format with `sendReferrer` is true.
+ await runTest(
+ stream => {
+ // Write to the output stream with the old format.
+ stream.writeBoolean(true); // nonNull
+ stream.writeStringZ("https://example.com/"); // spec
+ stream.write32(Ci.nsIReferrerInfo.STRICT_ORIGIN_WHEN_CROSS_ORIGIN); // policy
+ stream.writeBoolean(true); // sendReferrer
+ stream.writeBoolean(false); // isComputed
+ stream.writeBoolean(true); // initialized
+ stream.writeBoolean(false); // overridePolicyByDefault
+ },
+ {
+ referrerPolicy: Ci.nsIReferrerInfo.STRICT_ORIGIN_WHEN_CROSS_ORIGIN,
+ sendReferrer: true,
+ }
+ );
+
+ // Test with an old format with a computed Referrer.
+ await runTest(
+ stream => {
+ // Write to the output stream with the old format with a string.
+ stream.writeBoolean(true); // nonNull
+ stream.writeStringZ("https://example.com/"); // spec
+ stream.write32(Ci.nsIReferrerInfo.STRICT_ORIGIN_WHEN_CROSS_ORIGIN); // policy
+ stream.writeBoolean(false); // sendReferrer
+ stream.writeBoolean(true); // isComputed
+ stream.writeStringZ("https://example.com/"); // computedReferrer
+ stream.writeBoolean(true); // initialized
+ stream.writeBoolean(false); // overridePolicyByDefault
+ },
+ {
+ referrerPolicy: Ci.nsIReferrerInfo.STRICT_ORIGIN_WHEN_CROSS_ORIGIN,
+ sendReferrer: false,
+ computedReferrerSpec: "https://example.com/",
+ }
+ );
+
+ // Test with an old format with a computed Referrer and sendReferrer as true.
+ await runTest(
+ stream => {
+ // Write to the output stream with the old format with a string.
+ stream.writeBoolean(true); // nonNull
+ stream.writeStringZ("https://example.com/"); // spec
+ stream.write32(Ci.nsIReferrerInfo.STRICT_ORIGIN_WHEN_CROSS_ORIGIN); // policy
+ stream.writeBoolean(true); // sendReferrer
+ stream.writeBoolean(true); // isComputed
+ stream.writeStringZ("https://example.com/"); // computedReferrer
+ stream.writeBoolean(true); // initialized
+ stream.writeBoolean(false); // overridePolicyByDefault
+ },
+ {
+ referrerPolicy: Ci.nsIReferrerInfo.STRICT_ORIGIN_WHEN_CROSS_ORIGIN,
+ sendReferrer: true,
+ computedReferrerSpec: "https://example.com/",
+ }
+ );
+});
+
+// Test deserializing referrer info with the current format.
+add_task(async function test_deserializeReferrerInfo() {
+ // Test with a current format.
+ await runTest(
+ stream => {
+ // Write to the output stream with the new format.
+ stream.writeBoolean(true); // nonNull
+ stream.writeStringZ("https://example.com/"); // spec
+ stream.write32(Ci.nsIReferrerInfo.STRICT_ORIGIN_WHEN_CROSS_ORIGIN); // policy
+ stream.write32(Ci.nsIReferrerInfo.STRICT_ORIGIN_WHEN_CROSS_ORIGIN); // original policy
+ stream.writeBoolean(false); // sendReferrer
+ stream.writeBoolean(false); // isComputed
+ stream.writeBoolean(true); // initialized
+ stream.writeBoolean(false); // overridePolicyByDefault
+ },
+ {
+ referrerPolicy: Ci.nsIReferrerInfo.STRICT_ORIGIN_WHEN_CROSS_ORIGIN,
+ sendReferrer: false,
+ }
+ );
+
+ // Test with a current format with sendReferrer as true.
+ await runTest(
+ stream => {
+ // Write to the output stream with the new format.
+ stream.writeBoolean(true); // nonNull
+ stream.writeStringZ("https://example.com/"); // spec
+ stream.write32(Ci.nsIReferrerInfo.STRICT_ORIGIN_WHEN_CROSS_ORIGIN); // policy
+ stream.write32(Ci.nsIReferrerInfo.STRICT_ORIGIN_WHEN_CROSS_ORIGIN); // original policy
+ stream.writeBoolean(true); // sendReferrer
+ stream.writeBoolean(false); // isComputed
+ stream.writeBoolean(true); // initialized
+ stream.writeBoolean(false); // overridePolicyByDefault
+ },
+ {
+ referrerPolicy: Ci.nsIReferrerInfo.STRICT_ORIGIN_WHEN_CROSS_ORIGIN,
+ sendReferrer: true,
+ }
+ );
+
+ // Test with a current format with a computedReferrer.
+ await runTest(
+ stream => {
+ // Write to the output stream with the new format with a string.
+ stream.writeBoolean(true); // nonNull
+ stream.writeStringZ("https://example.com/"); // spec
+ stream.write32(Ci.nsIReferrerInfo.STRICT_ORIGIN_WHEN_CROSS_ORIGIN); // policy
+ stream.write32(Ci.nsIReferrerInfo.STRICT_ORIGIN_WHEN_CROSS_ORIGIN); // original policy
+ stream.writeBoolean(false); // sendReferrer
+ stream.writeBoolean(true); // isComputed
+ stream.writeStringZ("https://example.com/"); // computedReferrer
+ stream.writeBoolean(true); // initialized
+ stream.writeBoolean(false); // overridePolicyByDefault
+ },
+ {
+ referrerPolicy: Ci.nsIReferrerInfo.STRICT_ORIGIN_WHEN_CROSS_ORIGIN,
+ sendReferrer: false,
+ computedReferrerSpec: "https://example.com/",
+ }
+ );
+
+ // Test with a current format with a computedReferrer and sendReferrer as true.
+ await runTest(
+ stream => {
+ // Write to the output stream with the new format with a string.
+ stream.writeBoolean(true); // nonNull
+ stream.writeStringZ("https://example.com/"); // spec
+ stream.write32(Ci.nsIReferrerInfo.STRICT_ORIGIN_WHEN_CROSS_ORIGIN); // policy
+ stream.write32(Ci.nsIReferrerInfo.STRICT_ORIGIN_WHEN_CROSS_ORIGIN); // original policy
+ stream.writeBoolean(true); // sendReferrer
+ stream.writeBoolean(true); // isComputed
+ stream.writeStringZ("https://example.com/"); // computedReferrer
+ stream.writeBoolean(true); // initialized
+ stream.writeBoolean(false); // overridePolicyByDefault
+ },
+ {
+ referrerPolicy: Ci.nsIReferrerInfo.STRICT_ORIGIN_WHEN_CROSS_ORIGIN,
+ sendReferrer: true,
+ computedReferrerSpec: "https://example.com/",
+ }
+ );
+
+ // Test with a current format that the tailing bytes are all zero.
+ await runTest(
+ stream => {
+ // Write to the output stream with the new format with a string.
+ stream.writeBoolean(true); // nonNull
+ stream.writeStringZ("https://example.com/"); // spec
+ stream.write32(Ci.nsIReferrerInfo.EMPTY); // policy
+ stream.write32(Ci.nsIReferrerInfo.EMPTY); // original policy
+ stream.writeBoolean(false); // sendReferrer
+ stream.writeBoolean(false); // isComputed
+ stream.writeBoolean(false); // initialized
+ stream.writeBoolean(false); // overridePolicyByDefault
+ },
+ {
+ referrerPolicy: Ci.nsIReferrerInfo.EMPTY,
+ sendReferrer: false,
+ }
+ );
+});
diff --git a/dom/security/test/unit/test_https_only_https_first_default_port.js b/dom/security/test/unit/test_https_only_https_first_default_port.js
new file mode 100644
index 0000000000..8960fd74b0
--- /dev/null
+++ b/dom/security/test/unit/test_https_only_https_first_default_port.js
@@ -0,0 +1,107 @@
+const { HttpServer } = ChromeUtils.import("resource://testing-common/httpd.js");
+const { NetUtil } = ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
+
+const TEST_PATH = "/https_only_https_first_port";
+var httpserver = null;
+var channel = null;
+var curTest = null;
+
+const TESTS = [
+ {
+ description: "Test 1 - Default Port (scheme: http, port: default)",
+ url: "http://test1.example.com",
+ expectedScheme: "https",
+ expectedPort: -1, // -1 == default
+ },
+ {
+ description: "Test 2 - Explicit Default Port (scheme: http, port: 80)",
+ url: "http://test1.example.com:80",
+ expectedScheme: "https",
+ expectedPort: -1, // -1 == default
+ },
+ {
+ description: "Test 3 - Explicit Custom Port (scheme: http, port: 8888)",
+ url: "http://test1.example.com:8888",
+ expectedScheme: "http",
+ expectedPort: 8888,
+ },
+ {
+ description:
+ "Test 4 - Explicit Default Port for https (scheme: https, port: 443)",
+ url: "https://test1.example.com:443",
+ expectedScheme: "https",
+ expectedPort: -1, // -1 == default
+ },
+];
+
+function ChannelListener() {}
+
+ChannelListener.prototype = {
+ onStartRequest(request) {
+ // dummy implementation
+ },
+ onDataAvailable(request, stream, offset, count) {
+ do_throw("Should not get any data!");
+ },
+ onStopRequest(request, status) {
+ var chan = request.QueryInterface(Ci.nsIChannel);
+ let requestURL = chan.URI;
+ Assert.equal(
+ requestURL.scheme,
+ curTest.expectedScheme,
+ curTest.description
+ );
+ Assert.equal(requestURL.port, curTest.expectedPort, curTest.description);
+ Assert.equal(requestURL.host, "test1.example.com", curTest.description);
+ run_next_test();
+ },
+};
+
+function setUpPrefs() {
+ // set up the required prefs
+ Services.prefs.setBoolPref("dom.security.https_first", true);
+ Services.prefs.setBoolPref("dom.security.https_only_mode", false);
+}
+
+function setUpChannel() {
+ var chan = NetUtil.newChannel({
+ uri: curTest.url + TEST_PATH,
+ loadingPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
+ contentPolicyType: Ci.nsIContentPolicy.TYPE_DOCUMENT,
+ securityFlags: Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
+ });
+ chan.QueryInterface(Ci.nsIHttpChannel);
+ chan.requestMethod = "GET";
+ return chan;
+}
+
+function serverHandler(metadata, response) {
+ // dummy implementation
+}
+
+function run_next_test() {
+ curTest = TESTS.shift();
+ if (!curTest) {
+ httpserver.stop(do_test_finished);
+ return;
+ }
+
+ channel = setUpChannel();
+ channel.asyncOpen(new ChannelListener());
+}
+
+function run_test() {
+ do_get_profile();
+ do_test_pending();
+
+ // set up the test environment
+ httpserver = new HttpServer();
+ httpserver.registerPathHandler(TEST_PATH, serverHandler);
+ httpserver.start(-1);
+
+ // set up prefs
+ setUpPrefs();
+
+ // run the tests
+ run_next_test();
+}
diff --git a/dom/security/test/unit/test_https_only_https_first_prefs.js b/dom/security/test/unit/test_https_only_https_first_prefs.js
new file mode 100644
index 0000000000..d3ce4e8615
--- /dev/null
+++ b/dom/security/test/unit/test_https_only_https_first_prefs.js
@@ -0,0 +1,360 @@
+const { HttpServer } = ChromeUtils.import("resource://testing-common/httpd.js");
+const { NetUtil } = ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
+const { XPCOMUtils } = ChromeUtils.importESModule(
+ "resource://gre/modules/XPCOMUtils.sys.mjs"
+);
+
+XPCOMUtils.defineLazyGetter(this, "HTTP_TEST_URL", function () {
+ return "http://test1.example.com";
+});
+
+const TEST_PATH = "/https_only_https_first_path";
+var httpserver = null;
+var channel = null;
+var curTest = null;
+
+const TESTS = [
+ {
+ // Test 1: all prefs to false
+ description: "Test 1 - top-level",
+ contentType: Ci.nsIContentPolicy.TYPE_DOCUMENT,
+ https_only: false,
+ https_only_pbm: false,
+ https_first: false,
+ https_first_pbm: false,
+ pbm: false,
+ expectedScheme: "http",
+ },
+ {
+ description: "Test 1 - top-level - pbm",
+ contentType: Ci.nsIContentPolicy.TYPE_DOCUMENT,
+ https_only: false,
+ https_only_pbm: false,
+ https_first: false,
+ https_first_pbm: false,
+ pbm: true,
+ expectedScheme: "http",
+ },
+ {
+ description: "Test 1 - sub-resource",
+ contentType: Ci.nsIContentPolicy.TYPE_IMAGE,
+ https_only: false,
+ https_only_pbm: false,
+ https_first: false,
+ https_first_pbm: false,
+ pbm: false,
+ expectedScheme: "http",
+ },
+ {
+ description: "Test 1 - sub-resource - pbm",
+ contentType: Ci.nsIContentPolicy.TYPE_IMAGE,
+ https_only: false,
+ https_only_pbm: false,
+ https_first: false,
+ https_first_pbm: false,
+ pbm: true,
+ expectedScheme: "http",
+ },
+ // Test 2: https_only true
+ {
+ description: "Test 2 - top-level",
+ contentType: Ci.nsIContentPolicy.TYPE_DOCUMENT,
+ https_only: true,
+ https_only_pbm: false,
+ https_first: false,
+ https_first_pbm: false,
+ pbm: false,
+ expectedScheme: "https",
+ },
+ {
+ description: "Test 2 - top-level - pbm",
+ contentType: Ci.nsIContentPolicy.TYPE_DOCUMENT,
+ https_only: true,
+ https_only_pbm: false,
+ https_first: false,
+ https_first_pbm: false,
+ pbm: true,
+ expectedScheme: "https",
+ },
+ {
+ description: "Test 2 - sub-resource",
+ contentType: Ci.nsIContentPolicy.TYPE_IMAGE,
+ https_only: true,
+ https_only_pbm: false,
+ https_first: false,
+ https_first_pbm: false,
+ pbm: false,
+ expectedScheme: "https",
+ },
+ {
+ description: "Test 2 - sub-resource - pbm",
+ contentType: Ci.nsIContentPolicy.TYPE_IMAGE,
+ https_only: true,
+ https_only_pbm: false,
+ https_first: false,
+ https_first_pbm: false,
+ pbm: true,
+ expectedScheme: "https",
+ },
+ // Test 3: https_only_pbm true
+ {
+ description: "Test 3 - top-level",
+ contentType: Ci.nsIContentPolicy.TYPE_DOCUMENT,
+ https_only: false,
+ https_only_pbm: true,
+ https_first: false,
+ https_first_pbm: false,
+ pbm: false,
+ expectedScheme: "http",
+ },
+ {
+ description: "Test 3 - top-level - pbm",
+ contentType: Ci.nsIContentPolicy.TYPE_DOCUMENT,
+ https_only: false,
+ https_only_pbm: true,
+ https_first: false,
+ https_first_pbm: false,
+ pbm: true,
+ expectedScheme: "https",
+ },
+ {
+ description: "Test 3 - sub-resource",
+ contentType: Ci.nsIContentPolicy.TYPE_IMAGE,
+ https_only: false,
+ https_only_pbm: true,
+ https_first: false,
+ https_first_pbm: false,
+ pbm: false,
+ expectedScheme: "http",
+ },
+ {
+ description: "Test 3 - sub-resource - pbm",
+ contentType: Ci.nsIContentPolicy.TYPE_IMAGE,
+ https_only: false,
+ https_only_pbm: true,
+ https_first: false,
+ https_first_pbm: false,
+ pbm: true,
+ expectedScheme: "https",
+ },
+ // Test 4: https_first true
+ {
+ description: "Test 4 - top-level",
+ contentType: Ci.nsIContentPolicy.TYPE_DOCUMENT,
+ https_only: false,
+ https_only_pbm: false,
+ https_first: true,
+ https_first_pbm: false,
+ pbm: false,
+ expectedScheme: "https",
+ },
+ {
+ description: "Test 4 - top-level - pbm",
+ contentType: Ci.nsIContentPolicy.TYPE_DOCUMENT,
+ https_only: false,
+ https_only_pbm: false,
+ https_first: true,
+ https_first_pbm: false,
+ pbm: true,
+ expectedScheme: "https",
+ },
+ {
+ description: "Test 4 - sub-resource",
+ contentType: Ci.nsIContentPolicy.TYPE_IMAGE,
+ https_only: false,
+ https_only_pbm: false,
+ https_first: true,
+ https_first_pbm: false,
+ pbm: false,
+ expectedScheme: "http",
+ },
+ {
+ description: "Test 4 - sub-resource - pbm",
+ contentType: Ci.nsIContentPolicy.TYPE_IMAGE,
+ https_only: false,
+ https_only_pbm: false,
+ https_first: true,
+ https_first_pbm: false,
+ pbm: true,
+ expectedScheme: "http",
+ },
+ // Test 5: https_first_pbm true
+ {
+ description: "Test 5 - top-level",
+ contentType: Ci.nsIContentPolicy.TYPE_DOCUMENT,
+ https_only: false,
+ https_only_pbm: false,
+ https_first: false,
+ https_first_pbm: true,
+ pbm: false,
+ expectedScheme: "http",
+ },
+ {
+ description: "Test 5 - top-level - pbm",
+ contentType: Ci.nsIContentPolicy.TYPE_DOCUMENT,
+ https_only: false,
+ https_only_pbm: false,
+ https_first: false,
+ https_first_pbm: true,
+ pbm: true,
+ expectedScheme: "https",
+ },
+ {
+ description: "Test 5 - sub-resource",
+ contentType: Ci.nsIContentPolicy.TYPE_IMAGE,
+ https_only: false,
+ https_only_pbm: false,
+ https_first: false,
+ https_first_pbm: true,
+ pbm: false,
+ expectedScheme: "http",
+ },
+ {
+ description: "Test 5 - sub-resource - pbm",
+ contentType: Ci.nsIContentPolicy.TYPE_IMAGE,
+ https_only: false,
+ https_only_pbm: false,
+ https_first: false,
+ https_first_pbm: true,
+ pbm: true,
+ expectedScheme: "http",
+ },
+ // Test 6: https_only overrules https_first
+ {
+ description: "Test 6 - top-level",
+ contentType: Ci.nsIContentPolicy.TYPE_DOCUMENT,
+ https_only: true,
+ https_only_pbm: false,
+ https_first: true,
+ https_first_pbm: false,
+ pbm: false,
+ expectedScheme: "https",
+ },
+ {
+ description: "Test 6 - top-level - pbm",
+ contentType: Ci.nsIContentPolicy.TYPE_DOCUMENT,
+ https_only: true,
+ https_only_pbm: false,
+ https_first: true,
+ https_first_pbm: false,
+ pbm: true,
+ expectedScheme: "https",
+ },
+ {
+ description: "Test 6 - sub-resource",
+ contentType: Ci.nsIContentPolicy.TYPE_IMAGE,
+ https_only: true,
+ https_only_pbm: false,
+ https_first: true,
+ https_first_pbm: false,
+ pbm: false,
+ expectedScheme: "https",
+ },
+ {
+ description: "Test 6 - sub-resource - pbm",
+ contentType: Ci.nsIContentPolicy.TYPE_IMAGE,
+ https_only: true,
+ https_only_pbm: false,
+ https_first: true,
+ https_first_pbm: false,
+ pbm: true,
+ expectedScheme: "https",
+ },
+];
+
+function ChannelListener() {}
+
+ChannelListener.prototype = {
+ onStartRequest(request) {
+ var chan = request.QueryInterface(Ci.nsIChannel);
+ var httpChan = chan.QueryInterface(Ci.nsIHttpChannel);
+ var authHeader = httpChan.getRequestHeader("Authorization");
+ Assert.equal(authHeader, "Basic user:pass", curTest.description);
+ },
+ onDataAvailable(request, stream, offset, count) {
+ do_throw("Should not get any data!");
+ },
+ onStopRequest(request, status) {
+ var chan = request.QueryInterface(Ci.nsIChannel);
+ let requestURL = chan.URI;
+ Assert.equal(
+ requestURL.scheme,
+ curTest.expectedScheme,
+ curTest.description
+ );
+ Assert.equal(requestURL.host, "test1.example.com", curTest.description);
+ Assert.equal(requestURL.filePath, TEST_PATH, curTest.description);
+ run_next_test();
+ },
+};
+
+function setUpPrefs() {
+ // set up the required prefs
+ Services.prefs.setBoolPref(
+ "dom.security.https_only_mode",
+ curTest.https_only
+ );
+ Services.prefs.setBoolPref(
+ "dom.security.https_only_mode_pbm",
+ curTest.https_only_pbm
+ );
+ Services.prefs.setBoolPref("dom.security.https_first", curTest.https_first);
+ Services.prefs.setBoolPref(
+ "dom.security.https_first_pbm",
+ curTest.https_first_pbm
+ );
+}
+
+function setUpChannel() {
+ // 1) Set up Principal using OA in case of Private Browsing
+ let attr = {};
+ if (curTest.pbm) {
+ attr.privateBrowsingId = 1;
+ }
+ let uri = Services.io.newURI("http://test1.example.com");
+ let principal = Services.scriptSecurityManager.createContentPrincipal(
+ uri,
+ attr
+ );
+
+ // 2) Set up Channel
+ var chan = NetUtil.newChannel({
+ uri: HTTP_TEST_URL + TEST_PATH,
+ loadingPrincipal: principal,
+ contentPolicyType: curTest.contentType,
+ securityFlags: Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
+ });
+ chan.QueryInterface(Ci.nsIHttpChannel);
+ chan.requestMethod = "GET";
+ chan.setRequestHeader("Authorization", "Basic user:pass", false);
+ return chan;
+}
+
+function serverHandler(metadata, response) {
+ // dummy implementation
+}
+
+function run_next_test() {
+ curTest = TESTS.shift();
+ if (!curTest) {
+ httpserver.stop(do_test_finished);
+ return;
+ }
+
+ setUpPrefs();
+
+ channel = setUpChannel();
+ channel.asyncOpen(new ChannelListener());
+}
+
+function run_test() {
+ do_get_profile();
+
+ // set up the test environment
+ httpserver = new HttpServer();
+ httpserver.registerPathHandler(TEST_PATH, serverHandler);
+ httpserver.start(-1);
+
+ run_next_test();
+ do_test_pending();
+}
diff --git a/dom/security/test/unit/test_isOriginPotentiallyTrustworthy.js b/dom/security/test/unit/test_isOriginPotentiallyTrustworthy.js
new file mode 100644
index 0000000000..b218f1438f
--- /dev/null
+++ b/dom/security/test/unit/test_isOriginPotentiallyTrustworthy.js
@@ -0,0 +1,51 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/*
+ * Tests the "Is origin potentially trustworthy?" logic from
+ * <https://w3c.github.io/webappsec-secure-contexts/#is-origin-trustworthy>.
+ */
+
+const { NetUtil } = ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
+
+Services.prefs.setCharPref(
+ "dom.securecontext.allowlist",
+ "example.net,example.org"
+);
+
+Services.prefs.setBoolPref("dom.securecontext.allowlist_onions", false);
+
+add_task(async function test_isOriginPotentiallyTrustworthy() {
+ for (let [uriSpec, expectedResult] of [
+ ["http://example.com/", false],
+ ["https://example.com/", true],
+ ["http://localhost/", true],
+ ["http://localhost.localhost/", true],
+ ["http://127.0.0.1/", true],
+ ["file:///", true],
+ ["resource:///", true],
+ ["moz-extension://", true],
+ ["wss://example.com/", true],
+ ["about:config", false],
+ ["http://example.net/", true],
+ ["ws://example.org/", true],
+ ["chrome://example.net/content/messenger.xul", false],
+ ["http://1234567890abcdef.onion/", false],
+ ]) {
+ let uri = NetUtil.newURI(uriSpec);
+ let principal = Services.scriptSecurityManager.createContentPrincipal(
+ uri,
+ {}
+ );
+ Assert.equal(principal.isOriginPotentiallyTrustworthy, expectedResult);
+ }
+ // And now let's test whether .onion sites are properly treated when
+ // allowlisted, see bug 1382359.
+ Services.prefs.setBoolPref("dom.securecontext.allowlist_onions", true);
+ let uri = NetUtil.newURI("http://1234567890abcdef.onion/");
+ let principal = Services.scriptSecurityManager.createContentPrincipal(
+ uri,
+ {}
+ );
+ Assert.equal(principal.isOriginPotentiallyTrustworthy, true);
+});
diff --git a/dom/security/test/unit/xpcshell.ini b/dom/security/test/unit/xpcshell.ini
new file mode 100644
index 0000000000..b5da5879e6
--- /dev/null
+++ b/dom/security/test/unit/xpcshell.ini
@@ -0,0 +1,11 @@
+[DEFAULT]
+head =
+
+[test_csp_reports.js]
+[test_isOriginPotentiallyTrustworthy.js]
+[test_deserialization_format_before_100.js]
+[test_csp_upgrade_insecure_request_header.js]
+[test_https_only_https_first_prefs.js]
+skip-if = debug # assertions in loadinfo; loadinfo is just not xpcshell test friendly
+[test_https_only_https_first_default_port.js]
+skip-if = debug # assertions in loadinfo; loadinfo is just not xpcshell test friendly