diff options
Diffstat (limited to 'security/manager/ssl/tests/mochitest/stricttransportsecurity')
14 files changed, 568 insertions, 0 deletions
diff --git a/security/manager/ssl/tests/mochitest/stricttransportsecurity/.eslintrc.js b/security/manager/ssl/tests/mochitest/stricttransportsecurity/.eslintrc.js new file mode 100644 index 000000000..74b7a1a07 --- /dev/null +++ b/security/manager/ssl/tests/mochitest/stricttransportsecurity/.eslintrc.js @@ -0,0 +1,9 @@ +"use strict"; + +module.exports = { // eslint-disable-line no-undef + // mochitest-chrome tests also exist in this directory, but don't appear to + // use anything not also available to plain mochitests. Since plain mochitests + // are the majority, we take the safer option and only extend the + // mochitest-plain eslintrc file. + "extends": "../../../../../../testing/mochitest/mochitest.eslintrc.js" +}; diff --git a/security/manager/ssl/tests/mochitest/stricttransportsecurity/chrome.ini b/security/manager/ssl/tests/mochitest/stricttransportsecurity/chrome.ini new file mode 100644 index 000000000..b2ce8fef0 --- /dev/null +++ b/security/manager/ssl/tests/mochitest/stricttransportsecurity/chrome.ini @@ -0,0 +1,6 @@ +[DEFAULT] +tags = psm +skip-if = os == 'android' +support-files = page_blank.html + +[test_sts_privatebrowsing_perwindowpb.html] diff --git a/security/manager/ssl/tests/mochitest/stricttransportsecurity/mochitest.ini b/security/manager/ssl/tests/mochitest/stricttransportsecurity/mochitest.ini new file mode 100644 index 000000000..100f45840 --- /dev/null +++ b/security/manager/ssl/tests/mochitest/stricttransportsecurity/mochitest.ini @@ -0,0 +1,13 @@ +[DEFAULT] +tags = psm +support-files = + nosts_bootstrap.html + nosts_bootstrap.html^headers^ + plain_bootstrap.html + plain_bootstrap.html^headers^ + subdom_bootstrap.html + subdom_bootstrap.html^headers^ + verify.sjs + +[test_stricttransportsecurity.html] +skip-if = toolkit == 'android' #TIMED_OUT diff --git a/security/manager/ssl/tests/mochitest/stricttransportsecurity/moz.build b/security/manager/ssl/tests/mochitest/stricttransportsecurity/moz.build new file mode 100644 index 000000000..cd73f7716 --- /dev/null +++ b/security/manager/ssl/tests/mochitest/stricttransportsecurity/moz.build @@ -0,0 +1,10 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +MOCHITEST_MANIFESTS += ['mochitest.ini'] + +MOCHITEST_CHROME_MANIFESTS += ['chrome.ini'] + diff --git a/security/manager/ssl/tests/mochitest/stricttransportsecurity/nosts_bootstrap.html b/security/manager/ssl/tests/mochitest/stricttransportsecurity/nosts_bootstrap.html new file mode 100644 index 000000000..f9b0f9381 --- /dev/null +++ b/security/manager/ssl/tests/mochitest/stricttransportsecurity/nosts_bootstrap.html @@ -0,0 +1,26 @@ +<!-- 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/. --> + +<!DOCTYPE HTML> +<html> + <head> + <title>STS test iframe</title> + <script> + "use strict"; + let windowRef = window; + window.addEventListener("load", function() { + windowRef.parent.postMessage("BOOTSTRAP plain", + "http://mochi.test:8888"); + }, false); + </script> + </head> + <body> + <!-- This frame should be loaded over HTTPS to set the STS header. --> + This frame was loaded using + <script> + document.write(document.location.protocol); + </script> + and set the STS header to force this site and allow subdomain upgrading. + </body> +</html> diff --git a/security/manager/ssl/tests/mochitest/stricttransportsecurity/nosts_bootstrap.html^headers^ b/security/manager/ssl/tests/mochitest/stricttransportsecurity/nosts_bootstrap.html^headers^ new file mode 100644 index 000000000..9e23c73b7 --- /dev/null +++ b/security/manager/ssl/tests/mochitest/stricttransportsecurity/nosts_bootstrap.html^headers^ @@ -0,0 +1 @@ +Cache-Control: no-cache diff --git a/security/manager/ssl/tests/mochitest/stricttransportsecurity/page_blank.html b/security/manager/ssl/tests/mochitest/stricttransportsecurity/page_blank.html new file mode 100644 index 000000000..db5d31a96 --- /dev/null +++ b/security/manager/ssl/tests/mochitest/stricttransportsecurity/page_blank.html @@ -0,0 +1,5 @@ +<html> +<body> + PAGE BLANK +</body> +</html> diff --git a/security/manager/ssl/tests/mochitest/stricttransportsecurity/plain_bootstrap.html b/security/manager/ssl/tests/mochitest/stricttransportsecurity/plain_bootstrap.html new file mode 100644 index 000000000..f9b0f9381 --- /dev/null +++ b/security/manager/ssl/tests/mochitest/stricttransportsecurity/plain_bootstrap.html @@ -0,0 +1,26 @@ +<!-- 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/. --> + +<!DOCTYPE HTML> +<html> + <head> + <title>STS test iframe</title> + <script> + "use strict"; + let windowRef = window; + window.addEventListener("load", function() { + windowRef.parent.postMessage("BOOTSTRAP plain", + "http://mochi.test:8888"); + }, false); + </script> + </head> + <body> + <!-- This frame should be loaded over HTTPS to set the STS header. --> + This frame was loaded using + <script> + document.write(document.location.protocol); + </script> + and set the STS header to force this site and allow subdomain upgrading. + </body> +</html> diff --git a/security/manager/ssl/tests/mochitest/stricttransportsecurity/plain_bootstrap.html^headers^ b/security/manager/ssl/tests/mochitest/stricttransportsecurity/plain_bootstrap.html^headers^ new file mode 100644 index 000000000..a46bf65bd --- /dev/null +++ b/security/manager/ssl/tests/mochitest/stricttransportsecurity/plain_bootstrap.html^headers^ @@ -0,0 +1,2 @@ +Cache-Control: no-cache +Strict-Transport-Security: max-age=60 diff --git a/security/manager/ssl/tests/mochitest/stricttransportsecurity/subdom_bootstrap.html b/security/manager/ssl/tests/mochitest/stricttransportsecurity/subdom_bootstrap.html new file mode 100644 index 000000000..24a364158 --- /dev/null +++ b/security/manager/ssl/tests/mochitest/stricttransportsecurity/subdom_bootstrap.html @@ -0,0 +1,26 @@ +<!-- 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/. --> + +<!DOCTYPE HTML> +<html> + <head> + <title>STS test iframe</title> + <script> + "use strict"; + let windowRef = window; + window.addEventListener("load", function() { + windowRef.parent.postMessage("BOOTSTRAP subdom", + "http://mochi.test:8888"); + }, false); + </script> + </head> + <body> + <!-- This frame should be loaded over HTTPS to set the STS header. --> + This frame was loaded using + <script> + document.write(document.location.protocol); + </script> + and set the STS header to force this site and allow subdomain upgrading. + </body> +</html> diff --git a/security/manager/ssl/tests/mochitest/stricttransportsecurity/subdom_bootstrap.html^headers^ b/security/manager/ssl/tests/mochitest/stricttransportsecurity/subdom_bootstrap.html^headers^ new file mode 100644 index 000000000..e54fc6a1d --- /dev/null +++ b/security/manager/ssl/tests/mochitest/stricttransportsecurity/subdom_bootstrap.html^headers^ @@ -0,0 +1,2 @@ +Cache-Control: no-cache +Strict-Transport-Security: max-age=60; includeSubDomains diff --git a/security/manager/ssl/tests/mochitest/stricttransportsecurity/test_stricttransportsecurity.html b/security/manager/ssl/tests/mochitest/stricttransportsecurity/test_stricttransportsecurity.html new file mode 100644 index 000000000..675688638 --- /dev/null +++ b/security/manager/ssl/tests/mochitest/stricttransportsecurity/test_stricttransportsecurity.html @@ -0,0 +1,120 @@ +<!-- 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/. --> + +<!DOCTYPE HTML> +<html> +<head> + <title>opens additional content that should be converted to https</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> + + <script class="testbody" type="text/javascript"> + "use strict"; + + SimpleTest.waitForExplicitFinish(); + + const STSPATH = "/tests/security/manager/ssl/tests/mochitest/stricttransportsecurity"; + + // initialized manually here + var testsleft = {'plain': 4, 'subdom': 4}; + var roundsLeft = 2; + + var testframes = { + 'samedom': + {'url': "http://example.com" + STSPATH + "/verify.sjs", + 'expected': {'plain': 'SECURE', 'subdom': 'SECURE'}}, + 'subdom': + {'url': "http://test1.example.com" + STSPATH + "/verify.sjs", + 'expected': {'plain': 'INSECURE', 'subdom': 'SECURE'}}, + 'otherdom': + {'url': "http://example.org" + STSPATH + "/verify.sjs", + 'expected': {'plain': 'INSECURE', 'subdom': 'INSECURE'}}, + 'alreadysecure': + {'url': "https://test2.example.com" + STSPATH + "/verify.sjs", + 'expected': {'plain': 'SECURE', 'subdom': 'SECURE'}}, + }; + + function startRound(round) { + let frame = document.createElement("iframe"); + frame.setAttribute('id', 'ifr_bootstrap'); + frame.setAttribute('src', "https://example.com" + STSPATH + "/" + round + "_bootstrap.html"); + document.body.appendChild(frame); + } + + function endRound(round) { + // remove all the iframes in the document + document.body.removeChild(document.getElementById('ifr_bootstrap')); + for (let test in testframes) { + document.body.removeChild(document.getElementById('ifr_' + test)); + } + + // clean up the STS state + SpecialPowers.cleanUpSTSData("http://example.com"); + } + + function loadVerifyFrames(round) { + for (let test in testframes) { + let frame = document.createElement("iframe"); + frame.setAttribute('id', 'ifr_' + test); + frame.setAttribute('src', testframes[test].url + '?id=' + test); + document.body.appendChild(frame); + } + } + + /* Messages received are in this format: + * (BOOTSTRAP|SECURE|INSECURE) testid + * For example: "BOOTSTRAP plain" + * or: "INSECURE otherdom" + */ + function onMessageReceived(event) { + let result = event.data.split(/\s+/); + if (result.length != 2) { + SimpleTest.ok(false, event.data); + return; + } + + // figure out which round of tests we're in + let round = (roundsLeft == 2) ? "plain" : "subdom"; + + if (result[0] === "BOOTSTRAP") { + loadVerifyFrames(round); + return; + } + + // check if the result (SECURE/INSECURE) is expected for this round/test combo + SimpleTest.is(result[0], testframes[result[1]].expected[round], + "in ROUND " + round + ", test " + result[1]); + testsleft[round]--; + + // check if there are more tests to run. + if (testsleft[round] < 1) { + // if not, advance to next round + endRound(round); + roundsLeft--; + + // defer this so it doesn't muck with the stack too much. + if (roundsLeft == 1) { + setTimeout(function () { + startRound("subdom"); + }, 0); + } + } + + if (roundsLeft < 1) { + SimpleTest.finish(); + } + } + + // listen for calls back from the sts-setting iframe and then + // the verification frames. + window.addEventListener("message", onMessageReceived, false); + window.addEventListener("load", () => { startRound("plain"); }, false); + </script> +</head> + +<body> + This test will load some iframes and do some tests. + +</body> +</html> diff --git a/security/manager/ssl/tests/mochitest/stricttransportsecurity/test_sts_privatebrowsing_perwindowpb.html b/security/manager/ssl/tests/mochitest/stricttransportsecurity/test_sts_privatebrowsing_perwindowpb.html new file mode 100644 index 000000000..882030b67 --- /dev/null +++ b/security/manager/ssl/tests/mochitest/stricttransportsecurity/test_sts_privatebrowsing_perwindowpb.html @@ -0,0 +1,275 @@ +<!-- 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/. --> + +<!DOCTYPE HTML> +<html> +<head> + <title>opens additional content that should be converted to https</title> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css"/> + + <script class="testbody" type="text/javascript"> + "use strict"; + + // We define |content| as a global as a hack to prevent use of |content| + // inside a ContentTask tripping ESLint no-undef checks. + /* global content */ + + SimpleTest.waitForExplicitFinish(); + + const Cc = Components.classes; + const Ci = Components.interfaces; + const Cu = Components.utils; + const STSPATH = "/tests/security/manager/ssl/tests/mochitest/stricttransportsecurity"; + const NUM_TEST_FRAMES = 4; + const CONTENT_PAGE = + "http://mochi.test:8888/chrome/security/manager/ssl/tests/mochitest/stricttransportsecurity/page_blank.html"; + + Cu.import("resource://testing-common/BrowserTestUtils.jsm"); + Cu.import("resource://testing-common/ContentTask.jsm"); + Cu.import("resource://gre/modules/Task.jsm"); + + // This is how many sub-tests (testframes) in each round. + // When the round begins, this will be initialized. + var testsleftinround = 0; + var currentround = ""; + var mainWindow = + window.QueryInterface(Ci.nsIInterfaceRequestor). + getInterface(Ci.nsIWebNavigation).QueryInterface(Ci.nsIDocShellTreeItem). + rootTreeItem.QueryInterface(Ci.nsIInterfaceRequestor). + getInterface(Ci.nsIDOMWindow); + + SpecialPowers.Services.prefs.setIntPref("browser.startup.page", 0); + + var testframes = { + 'samedom': + {'url': "http://example.com" + STSPATH + "/verify.sjs", + 'expected': {'plain': 'SECURE', + 'subdom': 'SECURE', + 'nosts': 'INSECURE'}}, + 'subdom': + {'url': "http://test1.example.com" + STSPATH + "/verify.sjs", + 'expected': {'plain': 'INSECURE', + 'subdom': 'SECURE', + 'nosts': 'INSECURE'}}, + 'otherdom': + {'url': "http://example.org" + STSPATH + "/verify.sjs", + 'expected': {'plain': 'INSECURE', + 'subdom': 'INSECURE', + 'nosts': 'INSECURE'}}, + 'alreadysecure': + {'url': "https://test2.example.com" + STSPATH + "/verify.sjs", + 'expected': {'plain': 'SECURE', + 'subdom': 'SECURE', + 'nosts': 'SECURE'}}, + }; + + function whenDelayedStartupFinished(aWindow, aCallback) { + SpecialPowers.Services.obs.addObserver(function observer(aSubject, aTopic) { + if (aWindow == aSubject) { + SpecialPowers.Services.obs.removeObserver(observer, aTopic); + SimpleTest.executeSoon(aCallback); + } + }, "browser-delayed-startup-finished", false); + } + + function testOnWindow(aIsPrivate, aCallback) { + let win = mainWindow.OpenBrowserWindow({private: aIsPrivate}); + + Task.spawn(function* () { + yield new Promise(resolve => whenDelayedStartupFinished(win, resolve)); + + let browser = win.gBrowser.selectedBrowser; + yield BrowserTestUtils.loadURI(browser, CONTENT_PAGE); + yield BrowserTestUtils.browserLoaded(browser); + + aCallback(win); + }); + } + + function startRound(win, isPrivate, round) { + currentround = round; + testsleftinround = NUM_TEST_FRAMES; + SimpleTest.info("TESTS LEFT IN ROUND " + currentround + ": " + testsleftinround); + + let browser = win.gBrowser.selectedBrowser; + let src = "https://example.com" + STSPATH + "/" + round + "_bootstrap.html"; + + ContentTask.spawn(browser, src, function* (contentSrc) { + let frame = content.document.createElement("iframe"); + frame.setAttribute('id', 'ifr_bootstrap'); + frame.setAttribute('src', contentSrc); + + return new Promise(resolve => { + frame.addEventListener("load", resolve); + content.document.body.appendChild(frame); + }); + }).then(() => { + onMessageReceived(win, isPrivate, "BOOTSTRAP " + round); + }); + } + + function loadVerifyFrames(win, isPrivate, round) { + loadVerifyFrame(win, isPrivate, testframes.samedom, 'samedom', function() { + loadVerifyFrame(win, isPrivate, testframes.subdom, 'subdom', function() { + loadVerifyFrame(win, isPrivate, testframes.otherdom, 'otherdom', function() { + loadVerifyFrame(win, isPrivate, testframes.alreadysecure, 'alreadysecure'); + }); + }); + }); + } + + function loadVerifyFrame(win, isPrivate, test, testName, aCallback) { + let id = 'ifr_' + testName; + let src = test.url + '?id=' + testName; + let browser = win.gBrowser.selectedBrowser; + + ContentTask.spawn(browser, [id, src], function* ([contentId, contentSrc]) { + let frame = content.document.createElement("iframe"); + frame.setAttribute('id', contentId); + frame.setAttribute('src', contentSrc); + + return new Promise(resolve => { + frame.addEventListener("load", () => { + resolve(frame.contentDocument.location.protocol); + }); + + content.document.body.appendChild(frame); + }); + }).then(scheme => { + if (scheme == 'https:') { + onMessageReceived(win, isPrivate, "SECURE " + testName); + } else { + onMessageReceived(win, isPrivate, "INSECURE " + testName); + } + + if (aCallback) { + aCallback(); + } + }); + } + + /** + * Messages received are in this format: + * (BOOTSTRAP|SECURE|INSECURE) testid + * For example: "BOOTSTRAP subdom" + * or: "INSECURE otherdom" + */ + function onMessageReceived(win, isPrivate, data) { + let result = data.split(/\s+/); + if (result.length != 2) { + SimpleTest.ok(false, data); + return; + } + + if (result[0] === "BOOTSTRAP") { + loadVerifyFrames(win, isPrivate, currentround); + return; + } + + // check if the result (SECURE/INSECURE) is expected for this round/test + // combo + dump_STSState(isPrivate); + SimpleTest.is(result[0], testframes[result[1]].expected[currentround], + "in ROUND " + currentround + + ", test " + result[1]); + testsleftinround--; + + // if this round is complete... + if (testsleftinround < 1) { + SimpleTest.info("DONE WITH ROUND " + currentround); + // remove all the iframes in the document + let browser = win.gBrowser.selectedBrowser; + ContentTask.spawn(browser, testframes, function* (contentTestFrames) { + content.document.body.removeChild( + content.document.getElementById('ifr_bootstrap')); + for (let test in contentTestFrames) { + content.document.body.removeChild( + content.document.getElementById('ifr_' + test)); + } + }).then(() => { + currentround = ""; + + if (!isPrivate) { + clean_up_sts_state(isPrivate); + } + // Close test window. + win.close(); + // And advance to the next test. + // Defer this so it doesn't muck with the stack too much. + SimpleTest.executeSoon(nextTest); + }); + } + } + + function test_sts_before_private_mode() { + testOnWindow(false, function(win) { + SimpleTest.info("In public window"); + dump_STSState(false); + startRound(win, false, 'plain'); + }); + } + + function test_sts_in_private_mode() { + testOnWindow(true, function(win) { + SimpleTest.info("In private window"); + dump_STSState(true); + startRound(win, true, 'subdom'); + }); + } + + function test_sts_after_exiting_private_mode() { + testOnWindow(false, function(win) { + SimpleTest.info("In a new public window"); + dump_STSState(false); + startRound(win, false, 'nosts'); + }); + } + + function clean_up_sts_state(isPrivate) { + // erase all signs that this test ran. + SimpleTest.info("Cleaning up STS data"); + let flags = isPrivate ? Ci.nsISocketProvider.NO_PERMANENT_STORAGE : 0; + SpecialPowers.cleanUpSTSData("http://example.com", flags); + dump_STSState(isPrivate); + } + + function dump_STSState(isPrivate) { + let sss = Cc["@mozilla.org/ssservice;1"] + .getService(Ci.nsISiteSecurityService); + let flags = isPrivate ? Ci.nsISocketProvider.NO_PERMANENT_STORAGE : 0; + SimpleTest.info("State of example.com: " + sss.isSecureHost(Ci.nsISiteSecurityService.HEADER_HSTS, "example.com", flags)); + } + + // These are executed in the order presented. + // 0. test that STS works before entering private browsing mode. + // (load sts-bootstrapped "plain" tests) + // ... clear any STS data ... + // 1. test that STS works in private browsing mode + // (load sts-bootstrapped "subdomain" tests) + // 2. test that after exiting private browsing, STS data is forgotten + // (verified with non-sts-bootstrapped pages) + // ... clear any STS data ... + var tests = [ + test_sts_before_private_mode, + test_sts_in_private_mode, + test_sts_after_exiting_private_mode + ]; + + function finish() { + SpecialPowers.Services.prefs.clearUserPref("browser.startup.page"); + SimpleTest.finish(); + } + function nextTest() { + SimpleTest.executeSoon(tests.length ? tests.shift() : finish); + } + window.addEventListener('load', nextTest, false); + </script> +</head> + +<body> + This test will load some iframes and do some tests. +</body> +</html> diff --git a/security/manager/ssl/tests/mochitest/stricttransportsecurity/verify.sjs b/security/manager/ssl/tests/mochitest/stricttransportsecurity/verify.sjs new file mode 100644 index 000000000..e5e4eb5f6 --- /dev/null +++ b/security/manager/ssl/tests/mochitest/stricttransportsecurity/verify.sjs @@ -0,0 +1,47 @@ +/* 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/. */ + +// SJS file that serves un-cacheable responses for STS tests that postMessage +// to the parent saying whether or not they were loaded securely. + +function handleRequest(request, response) +{ + var query = {}; + request.queryString.split('&').forEach(function (val) { + var [name, value] = val.split('='); + query[name] = unescape(value); + }); + + response.setHeader("Cache-Control", "no-cache", false); + response.setHeader("Content-Type", "text/html", false); + + if ('id' in query) { + var outstr = [ + " <!DOCTYPE html>", + " <html> <head> <title>subframe for STS</title>", + " <script type='text/javascript'>", + " var self = window;", + " window.addEventListener('load', function() {", + " if (document.location.protocol === 'https:') {", + " self.parent.postMessage('SECURE " + query['id'] + "',", + " 'http://mochi.test:8888');", + " } else {", + " self.parent.postMessage('INSECURE " + query['id'] + "',", + " 'http://mochi.test:8888');", + " }", + " }, false);", + " </script>", + " </head>", + " <body>", + " STS state verification frame loaded via", + " <script>", + " document.write(document.location.protocol);", + " </script>", + " </body>", + " </html>"].join("\n"); + response.write(outstr); + } else { + response.write("ERROR: no id provided"); + } +} |