diff options
Diffstat (limited to '')
7 files changed, 1186 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..3df7a001d5 --- /dev/null +++ b/dom/security/test/unit/test_csp_reports.js @@ -0,0 +1,315 @@ +/* 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 + ); + }); + + // test scheme of ftp: + makeTest( + 8, + { "blocked-uri": "ftp://blocked.test/profile.png" }, + false, + function(csp) { + // shouldLoad creates and sends out the report here. + csp.shouldLoad( + Ci.nsIContentPolicy.TYPE_SCRIPT, + null, // nsICSPEventListener + NetUtil.newURI("ftp://blocked.test/profile.png"), + 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..287dc179c5 --- /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..0b2f0cd1dd --- /dev/null +++ b/dom/security/test/unit/test_https_only_https_first_prefs.js @@ -0,0 +1,356 @@ +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) { + // 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.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"; + 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 |