diff options
Diffstat (limited to 'dom/security/test/unit')
-rw-r--r-- | dom/security/test/unit/test_csp_reports.js | 231 | ||||
-rw-r--r-- | dom/security/test/unit/test_csp_upgrade_insecure_request_header.js | 107 | ||||
-rw-r--r-- | dom/security/test/unit/test_isOriginPotentiallyTrustworthy.js | 47 | ||||
-rw-r--r-- | dom/security/test/unit/xpcshell.ini | 7 |
4 files changed, 392 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 000000000..6c88fb1e1 --- /dev/null +++ b/dom/security/test/unit/test_csp_reports.js @@ -0,0 +1,231 @@ +/* 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/. */ + +var Cc = Components.classes; +var Ci = Components.interfaces; +var Cu = Components.utils; +var Cr = Components.results; + +Cu.import('resource://gre/modules/NetUtil.jsm'); +Cu.import("resource://gre/modules/Services.jsm"); +Cu.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"; +const REPORT_SERVER_PATH = "/report"; + +/** + * 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) + do_check_eq(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-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"); + + let ssm = Cc["@mozilla.org/scriptsecuritymanager;1"] + .getService(Ci.nsIScriptSecurityManager); + principal = ssm.createCodebasePrincipal(selfuri, {}); + csp.setRequestContext(null, principal); + + // 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() { + var selfuri = NetUtil.newURI(REPORT_SERVER_URI + + ":" + REPORT_SERVER_PORT + + "/foo/self"); + + // test that inline script violations cause a report. + makeTest(0, {"blocked-uri": "self"}, false, + function(csp) { + let inlineOK = true; + inlineOK = csp.getAllowsInline(Ci.nsIContentPolicy.TYPE_SCRIPT, + "", // aNonce + false, // aParserCreated + "", // aContent + 0); // aLineNumber + + // this is not a report only policy, so it better block inline scripts + do_check_false(inlineOK); + }); + + // test that eval violations cause a report. + makeTest(1, {"blocked-uri": "self", + // JSON script-sample is UTF8 encoded + "script-sample" : "\xc2\xa3\xc2\xa5\xc2\xb5\xe5\x8c\x97\xf0\xa0\x9d\xb9"}, 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 + do_check_false(evalOK); + // ... and cause reports to go out + do_check_true(oReportViolation.value); + + if (oReportViolation.value) { + // force the logging, since the getter doesn't. + csp.logViolationDetails(Ci.nsIContentSecurityPolicy.VIOLATION_TYPE_EVAL, + 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); + } + }); + + makeTest(2, {"blocked-uri": "http://blocked.test"}, false, + function(csp) { + // shouldLoad creates and sends out the report here. + csp.shouldLoad(Ci.nsIContentPolicy.TYPE_SCRIPT, + NetUtil.newURI("http://blocked.test/foo.js"), + null, null, null, null); + }); + + // test that inline script violations cause a report in report-only policy + makeTest(3, {"blocked-uri": "self"}, true, + function(csp) { + let inlineOK = true; + inlineOK = csp.getAllowsInline(Ci.nsIContentPolicy.TYPE_SCRIPT, + "", // aNonce + false, // aParserCreated + "", // aContent + 0); // aLineNumber + + // this is a report only policy, so it better allow inline scripts + do_check_true(inlineOK); + }); + + // test that eval violations cause a report in report-only policy + makeTest(4, {"blocked-uri": "self"}, true, + function(csp) { + let evalOK = true, oReportViolation = {'value': false}; + evalOK = csp.getAllowsEval(oReportViolation); + + // this is a report only policy, so it better allow eval + do_check_true(evalOK); + // ... but still cause reports to go out + do_check_true(oReportViolation.value); + + if (oReportViolation.value) { + // force the logging, since the getter doesn't. + csp.logViolationDetails(Ci.nsIContentSecurityPolicy.VIOLATION_TYPE_INLINE_SCRIPT, + selfuri.asciiSpec, + "script sample", + 4); + } + }); + + // 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, + NetUtil.newURI("data:image/png;base64," + base64data), + null, null, null, null); + }); + + // 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, + NetUtil.newURI("intent://mymaps.com/maps?um=1&ie=UTF-8&fb=1&sll"), + null, null, null, null); + }); + + // test fragment removal + var selfSpec = REPORT_SERVER_URI + ":" + REPORT_SERVER_PORT + "/foo/self/foo.js"; + makeTest(7, {"blocked-uri": selfSpec}, false, + function(csp) { + var uri = NetUtil + // shouldLoad creates and sends out the report here. + csp.shouldLoad(Ci.nsIContentPolicy.TYPE_SCRIPT, + NetUtil.newURI(selfSpec + "#bar"), + null, null, null, null); + }); + + // test scheme of ftp: + makeTest(8, {"blocked-uri": "ftp://blocked.test"}, false, + function(csp) { + // shouldLoad creates and sends out the report here. + csp.shouldLoad(Ci.nsIContentPolicy.TYPE_SCRIPT, + NetUtil.newURI("ftp://blocked.test/profile.png"), + null, null, null, null); + }); +} 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 000000000..8be873234 --- /dev/null +++ b/dom/security/test/unit/test_csp_upgrade_insecure_request_header.js @@ -0,0 +1,107 @@ +var Cu = Components.utils; +var Ci = Components.interfaces; +var Cc = Components.classes; + +Cu.import("resource://testing-common/httpd.js"); +Cu.import("resource://gre/modules/NetUtil.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +var prefs = Cc["@mozilla.org/preferences-service;1"]. + getService(Ci.nsIPrefBranch); + +// 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. +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_IMG", + expectingHeader: false, + contentType: Ci.nsIContentPolicy.TYPE_IMG + }, +]; + +function ChannelListener() { +} + +ChannelListener.prototype = { + onStartRequest: function(request, context) { }, + onDataAvailable: function(request, context, stream, offset, count) { + do_throw("Should not get any data!"); + }, + onStopRequest: function(request, context, 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); + do_check_eq(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.asyncOpen2(new ChannelListener()); +} + +function run_test() { + // 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_isOriginPotentiallyTrustworthy.js b/dom/security/test/unit/test_isOriginPotentiallyTrustworthy.js new file mode 100644 index 000000000..7de8faa8f --- /dev/null +++ b/dom/security/test/unit/test_isOriginPotentiallyTrustworthy.js @@ -0,0 +1,47 @@ +/* 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 { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components; + +Cu.import("resource://gre/modules/NetUtil.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +XPCOMUtils.defineLazyServiceGetter(this, "gScriptSecurityManager", + "@mozilla.org/scriptsecuritymanager;1", + "nsIScriptSecurityManager"); + +XPCOMUtils.defineLazyServiceGetter(this, "gContentSecurityManager", + "@mozilla.org/contentsecuritymanager;1", + "nsIContentSecurityManager"); + +var prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch); +prefs.setCharPref("dom.securecontext.whitelist", "example.net,example.org"); + +add_task(function* test_isOriginPotentiallyTrustworthy() { + for (let [uriSpec, expectedResult] of [ + ["http://example.com/", false], + ["https://example.com/", true], + ["http://localhost/", true], + ["http://127.0.0.1/", true], + ["file:///", true], + ["resource:///", true], + ["app://", true], + ["moz-extension://", true], + ["wss://example.com/", true], + ["about:config", false], + ["urn:generic", false], + ["http://example.net/", true], + ["ws://example.org/", true], + ["chrome://example.net/content/messenger.xul", false], + ]) { + let uri = NetUtil.newURI(uriSpec); + let principal = gScriptSecurityManager.getCodebasePrincipal(uri); + Assert.equal(gContentSecurityManager.isOriginPotentiallyTrustworthy(principal), + expectedResult); + } +}); diff --git a/dom/security/test/unit/xpcshell.ini b/dom/security/test/unit/xpcshell.ini new file mode 100644 index 000000000..7e1d4a0ed --- /dev/null +++ b/dom/security/test/unit/xpcshell.ini @@ -0,0 +1,7 @@ +[DEFAULT] +head = +tail = + +[test_csp_reports.js] +[test_isOriginPotentiallyTrustworthy.js] +[test_csp_upgrade_insecure_request_header.js] |