From 36d22d82aa202bb199967e9512281e9a53db42c9 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 21:33:14 +0200 Subject: Adding upstream version 115.7.0esr. Signed-off-by: Daniel Baumann --- dom/security/test/https-only/browser.ini | 39 ++++ .../test/https-only/browser_background_redirect.js | 64 ++++++ .../test/https-only/browser_console_logging.js | 150 ++++++++++++++ .../test/https-only/browser_cors_mixedcontent.js | 124 ++++++++++++ dom/security/test/https-only/browser_hsts_host.js | 182 +++++++++++++++++ .../test/https-only/browser_httpsonly_prefs.js | 118 +++++++++++ .../browser_httpsonly_speculative_connect.js | 69 +++++++ .../test/https-only/browser_iframe_test.js | 223 +++++++++++++++++++++ .../test/https-only/browser_redirect_tainting.js | 39 ++++ .../browser_triggering_principal_exemption.js | 72 +++++++ .../test/https-only/browser_upgrade_exceptions.js | 86 ++++++++ .../test/https-only/browser_user_gesture.js | 55 +++++ .../https-only/browser_websocket_exceptions.js | 68 +++++++ .../test/https-only/file_background_redirect.sjs | 32 +++ .../file_break_endless_upgrade_downgrade_loop.sjs | 67 +++++++ .../test/https-only/file_console_logging.html | 16 ++ .../test/https-only/file_cors_mixedcontent.html | 42 ++++ dom/security/test/https-only/file_fragment.html | 47 +++++ .../test/https-only/file_fragment_noscript.html | 9 + .../file_http_background_auth_request.sjs | 16 ++ .../https-only/file_http_background_request.sjs | 15 ++ .../file_httpsonly_speculative_connect.html | 1 + dom/security/test/https-only/file_iframe_test.sjs | 58 ++++++ .../test/https-only/file_insecure_reload.sjs | 27 +++ dom/security/test/https-only/file_redirect.sjs | 37 ++++ .../test/https-only/file_redirect_tainting.sjs | 39 ++++ .../test/https-only/file_upgrade_insecure.html | 91 +++++++++ .../https-only/file_upgrade_insecure_server.sjs | 112 +++++++++++ .../test/https-only/file_upgrade_insecure_wsh.py | 6 + .../test/https-only/file_user_gesture.html | 11 + .../test/https-only/file_websocket_exceptions.html | 9 + .../file_websocket_exceptions_iframe.html | 30 +++ dom/security/test/https-only/hsts_headers.sjs | 24 +++ dom/security/test/https-only/mochitest.ini | 43 ++++ .../test_break_endless_upgrade_downgrade_loop.html | 89 ++++++++ dom/security/test/https-only/test_fragment.html | 72 +++++++ .../test_http_background_auth_request.html | 113 +++++++++++ .../https-only/test_http_background_request.html | 125 ++++++++++++ .../test/https-only/test_insecure_reload.html | 74 +++++++ .../test/https-only/test_redirect_upgrade.html | 58 ++++++ .../test/https-only/test_resource_upgrade.html | 122 +++++++++++ .../test/https-only/test_user_suggestion_box.html | 81 ++++++++ 42 files changed, 2755 insertions(+) create mode 100644 dom/security/test/https-only/browser.ini create mode 100644 dom/security/test/https-only/browser_background_redirect.js create mode 100644 dom/security/test/https-only/browser_console_logging.js create mode 100644 dom/security/test/https-only/browser_cors_mixedcontent.js create mode 100644 dom/security/test/https-only/browser_hsts_host.js create mode 100644 dom/security/test/https-only/browser_httpsonly_prefs.js create mode 100644 dom/security/test/https-only/browser_httpsonly_speculative_connect.js create mode 100644 dom/security/test/https-only/browser_iframe_test.js create mode 100644 dom/security/test/https-only/browser_redirect_tainting.js create mode 100644 dom/security/test/https-only/browser_triggering_principal_exemption.js create mode 100644 dom/security/test/https-only/browser_upgrade_exceptions.js create mode 100644 dom/security/test/https-only/browser_user_gesture.js create mode 100644 dom/security/test/https-only/browser_websocket_exceptions.js create mode 100644 dom/security/test/https-only/file_background_redirect.sjs create mode 100644 dom/security/test/https-only/file_break_endless_upgrade_downgrade_loop.sjs create mode 100644 dom/security/test/https-only/file_console_logging.html create mode 100644 dom/security/test/https-only/file_cors_mixedcontent.html create mode 100644 dom/security/test/https-only/file_fragment.html create mode 100644 dom/security/test/https-only/file_fragment_noscript.html create mode 100644 dom/security/test/https-only/file_http_background_auth_request.sjs create mode 100644 dom/security/test/https-only/file_http_background_request.sjs create mode 100644 dom/security/test/https-only/file_httpsonly_speculative_connect.html create mode 100644 dom/security/test/https-only/file_iframe_test.sjs create mode 100644 dom/security/test/https-only/file_insecure_reload.sjs create mode 100644 dom/security/test/https-only/file_redirect.sjs create mode 100644 dom/security/test/https-only/file_redirect_tainting.sjs create mode 100644 dom/security/test/https-only/file_upgrade_insecure.html create mode 100644 dom/security/test/https-only/file_upgrade_insecure_server.sjs create mode 100644 dom/security/test/https-only/file_upgrade_insecure_wsh.py create mode 100644 dom/security/test/https-only/file_user_gesture.html create mode 100644 dom/security/test/https-only/file_websocket_exceptions.html create mode 100644 dom/security/test/https-only/file_websocket_exceptions_iframe.html create mode 100644 dom/security/test/https-only/hsts_headers.sjs create mode 100644 dom/security/test/https-only/mochitest.ini create mode 100644 dom/security/test/https-only/test_break_endless_upgrade_downgrade_loop.html create mode 100644 dom/security/test/https-only/test_fragment.html create mode 100644 dom/security/test/https-only/test_http_background_auth_request.html create mode 100644 dom/security/test/https-only/test_http_background_request.html create mode 100644 dom/security/test/https-only/test_insecure_reload.html create mode 100644 dom/security/test/https-only/test_redirect_upgrade.html create mode 100644 dom/security/test/https-only/test_resource_upgrade.html create mode 100644 dom/security/test/https-only/test_user_suggestion_box.html (limited to 'dom/security/test/https-only') diff --git a/dom/security/test/https-only/browser.ini b/dom/security/test/https-only/browser.ini new file mode 100644 index 0000000000..12dadaf6ca --- /dev/null +++ b/dom/security/test/https-only/browser.ini @@ -0,0 +1,39 @@ +[DEFAULT] +prefs = + dom.security.https_first=false + +[browser_console_logging.js] +support-files = + file_console_logging.html +[browser_upgrade_exceptions.js] +[browser_httpsonly_prefs.js] +[browser_cors_mixedcontent.js] +support-files = + file_cors_mixedcontent.html +[browser_iframe_test.js] +skip-if = + os == 'linux' && bits == 64 # Bug 1735565 + os == 'win' && bits == 64 # Bug 1735565 + os == "win" && os_version == "6.1" # Skip on Azure - frequent failure +support-files = + file_iframe_test.sjs +[browser_triggering_principal_exemption.js] +[browser_background_redirect.js] +support-files = + file_background_redirect.sjs +[browser_user_gesture.js] +support-files = + file_user_gesture.html +[browser_hsts_host.js] +support-files = + hsts_headers.sjs + file_fragment_noscript.html +[browser_httpsonly_speculative_connect.js] +support-files = file_httpsonly_speculative_connect.html +[browser_websocket_exceptions.js] +skip-if = (toolkit == 'android') # WebSocket tests are not supported on Android Yet. Bug 1566168. +support-files = + file_websocket_exceptions.html + file_websocket_exceptions_iframe.html +[browser_redirect_tainting.js] +support-files = file_redirect_tainting.sjs diff --git a/dom/security/test/https-only/browser_background_redirect.js b/dom/security/test/https-only/browser_background_redirect.js new file mode 100644 index 0000000000..d51624d26d --- /dev/null +++ b/dom/security/test/https-only/browser_background_redirect.js @@ -0,0 +1,64 @@ +"use strict"; +/* Description of the test: + * We load a page which gets upgraded to https. HTTPS-Only Mode then + * sends an 'http' background request which we redirect (using the + * web extension API) to 'same-origin' https. We ensure the HTTPS-Only + * Error page does not occur, but we https page gets loaded. + */ + +let extension = null; + +add_task(async function test_https_only_background_request_redirect() { + // A longer timeout is necessary for this test since we have to wait + // at least 3 seconds for the https-only background request to happen. + requestLongerTimeout(10); + + await SpecialPowers.pushPrefEnv({ + set: [["dom.security.https_only_mode", true]], + }); + + extension = ExtensionTestUtils.loadExtension({ + manifest: { + permissions: ["webRequest", "webRequestBlocking", "http://example.com/*"], + }, + background() { + let { browser } = this; + browser.webRequest.onBeforeRequest.addListener( + details => { + if (details.url === "http://example.com/") { + browser.test.sendMessage("redir-handled"); + let redirectUrl = "https://example.com/"; + return { redirectUrl }; + } + return undefined; + }, + { urls: ["http://example.com/*"] }, + ["blocking"] + ); + }, + }); + + await extension.startup(); + + await BrowserTestUtils.withNewTab("about:blank", async function (browser) { + let loaded = BrowserTestUtils.browserLoaded(browser, false, null, true); + BrowserTestUtils.loadURIString( + browser, + "http://example.com/browser/dom/security/test/https-only/file_background_redirect.sjs?start" + ); + + await extension.awaitMessage("redir-handled"); + + await loaded; + + await SpecialPowers.spawn(browser, [], async function () { + let innerHTML = content.document.body.innerHTML; + ok( + innerHTML.includes("Test Page for Bug 1683015 loaded"), + "No https-only error page" + ); + }); + }); + + await extension.unload(); +}); diff --git a/dom/security/test/https-only/browser_console_logging.js b/dom/security/test/https-only/browser_console_logging.js new file mode 100644 index 0000000000..7f993a7fda --- /dev/null +++ b/dom/security/test/https-only/browser_console_logging.js @@ -0,0 +1,150 @@ +// Bug 1625448 - HTTPS Only Mode - Tests for console logging +// https://bugzilla.mozilla.org/show_bug.cgi?id=1625448 +// This test makes sure that the various console messages from the HTTPS-Only +// mode get logged to the console. +"use strict"; + +// Test Cases +// description: Description of what the subtests expects. +// expectLogLevel: Expected log-level of a message. +// expectIncludes: Expected substrings the message should contain. +let tests = [ + { + description: "Top-Level upgrade should get logged", + expectLogLevel: Ci.nsIConsoleMessage.warn, + expectIncludes: [ + "HTTPS-Only Mode: Upgrading insecure request", + "to use", + "file_console_logging.html", + ], + }, + { + description: "iFrame upgrade failure should get logged", + expectLogLevel: Ci.nsIConsoleMessage.error, + expectIncludes: [ + "HTTPS-Only Mode: Upgrading insecure request", + "failed", + "file_console_logging.html", + ], + }, + { + description: "WebSocket upgrade should get logged", + expectLogLevel: Ci.nsIConsoleMessage.warn, + expectIncludes: [ + "HTTPS-Only Mode: Upgrading insecure request", + "to use", + "ws://does.not.exist", + ], + }, + { + description: "Sub-Resource upgrade for file_1 should get logged", + expectLogLevel: Ci.nsIConsoleMessage.warn, + expectIncludes: ["Upgrading insecure", "request", "file_1.jpg"], + }, + { + description: "Sub-Resource upgrade for file_2 should get logged", + expectLogLevel: Ci.nsIConsoleMessage.warn, + expectIncludes: ["Upgrading insecure", "request", "to use", "file_2.jpg"], + }, + { + description: "Exempt request for file_exempt should get logged", + expectLogLevel: Ci.nsIConsoleMessage.info, + expectIncludes: [ + "Not upgrading insecure request", + "because it is exempt", + "file_exempt.jpg", + ], + }, + { + description: "Sub-Resource upgrade failure for file_2 should get logged", + expectLogLevel: Ci.nsIConsoleMessage.error, + expectIncludes: ["Upgrading insecure request", "failed", "file_2.jpg"], + }, +]; + +const testPathUpgradeable = getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + "http://example.com" +); +// DNS errors are not logged as HTTPS-Only Mode upgrade failures, so we have to +// upgrade to a domain that exists but fails. +const testPathNotUpgradeable = getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + "http://self-signed.example.com" +); +const kTestURISuccess = testPathUpgradeable + "file_console_logging.html"; +const kTestURIFail = testPathNotUpgradeable + "file_console_logging.html"; +const kTestURIExempt = testPathUpgradeable + "file_exempt.jpg"; + +const UPGRADE_DISPLAY_CONTENT = + "security.mixed_content.upgrade_display_content"; + +add_task(async function () { + // A longer timeout is necessary for this test than the plain mochitests + // due to opening a new tab with the web console. + requestLongerTimeout(4); + + // Enable HTTPS-Only Mode and register console-listener + await SpecialPowers.pushPrefEnv({ + set: [["dom.security.https_only_mode", true]], + }); + Services.console.registerListener(on_new_message); + // 1. Upgrade page to https:// + BrowserTestUtils.loadURIString(gBrowser.selectedBrowser, kTestURISuccess); + // 2. Make an exempt http:// request + let xhr = new XMLHttpRequest(); + xhr.open("GET", kTestURIExempt, true); + xhr.channel.loadInfo.httpsOnlyStatus |= Ci.nsILoadInfo.HTTPS_ONLY_EXEMPT; + xhr.send(); + // 3. Make Websocket request + new WebSocket("ws://does.not.exist"); + + await BrowserTestUtils.waitForCondition(() => tests.length === 0); + + // Clean up + Services.console.unregisterListener(on_new_message); +}); + +function on_new_message(msgObj) { + const message = msgObj.message; + const logLevel = msgObj.logLevel; + + // Bools about message and pref + const isMCL2Enabled = Services.prefs.getBoolPref(UPGRADE_DISPLAY_CONTENT); + const isHTTPSOnlyModeLog = message.includes("HTTPS-Only Mode:"); + const isMCLog = message.includes("Mixed Content:"); + + // Check for messages about HTTPS-only upgrades (those should be unrelated to mixed content upgrades) + // or for mixed content upgrades which should only occur if security.mixed_content.upgrade_display_content is enabled + // (unrelated to https-only logs). + if ( + (isHTTPSOnlyModeLog && !isMCLog) || + (isMCLog && isMCL2Enabled && !isHTTPSOnlyModeLog) + ) { + for (let i = 0; i < tests.length; i++) { + const testCase = tests[i]; + // If security.mixed_content.upgrade_display_content is enabled, the mixed content control mechanism is upgrading file2.jpg + // and HTTPS-Only mode is not failing upgrading file2.jpg, so it won't be logged. + // so skip last test case + if ( + testCase.description == + "Sub-Resource upgrade failure for file_2 should get logged" && + isMCL2Enabled + ) { + tests.splice(i, 1); + continue; + } + // Check if log-level matches + if (logLevel !== testCase.expectLogLevel) { + continue; + } + // Check if all substrings are included + if (testCase.expectIncludes.some(str => !message.includes(str))) { + continue; + } + ok(true, testCase.description); + tests.splice(i, 1); + break; + } + } +} diff --git a/dom/security/test/https-only/browser_cors_mixedcontent.js b/dom/security/test/https-only/browser_cors_mixedcontent.js new file mode 100644 index 0000000000..e92be28f54 --- /dev/null +++ b/dom/security/test/https-only/browser_cors_mixedcontent.js @@ -0,0 +1,124 @@ +// Bug 1659505 - Https-Only: CORS and MixedContent tests +// https://bugzilla.mozilla.org/bug/1659505 +"use strict"; + +// > How does this test work? +// We open a page, that makes two fetch-requests to example.com (same-origin) +// and example.org (cross-origin). When both fetch-calls have either failed or +// succeeded, the site dispatches an event with the results. + +add_task(async function () { + // HTTPS-Only Mode disabled + await runTest({ + description: "Load site with HTTP and HOM disabled", + topLevelScheme: "http", + + expectedSameOrigin: "success", // ok + expectedCrossOrigin: "error", // CORS + }); + await runTest({ + description: "Load site with HTTPS and HOM disabled", + topLevelScheme: "https", + + expectedSameOrigin: "error", // Mixed Content + expectedCrossOrigin: "error", // Mixed Content + }); + + // HTTPS-Only Mode disabled and MixedContent blocker disabled + await SpecialPowers.pushPrefEnv({ + set: [["security.mixed_content.block_active_content", false]], + }); + await runTest({ + description: "Load site with HTTPS; HOM and MixedContent blocker disabled", + topLevelScheme: "https", + + expectedSameOrigin: "error", // CORS + expectedCrossOrigin: "error", // CORS + }); + await SpecialPowers.popPrefEnv(); + + // HTTPS-Only Mode enabled, no exception + await SpecialPowers.pushPrefEnv({ + set: [["dom.security.https_only_mode", true]], + }); + await runTest({ + description: "Load site with HTTP and HOM enabled", + topLevelScheme: "http", + + expectedSameOrigin: "success", // ok + expectedCrossOrigin: "error", // CORS + }); + + // HTTPS-Only enabled, with exception + await SpecialPowers.pushPermissions([ + { + type: "https-only-load-insecure", + allow: true, + context: "http://example.com", + }, + ]); + + await runTest({ + description: "Load site with HTTP, HOM enabled but site exempt", + topLevelScheme: "http", + + expectedSameOrigin: "success", // ok + expectedCrossOrigin: "error", // CORS + }); + + await SpecialPowers.popPermissions(); + await SpecialPowers.pushPermissions([ + { + type: "https-only-load-insecure", + allow: true, + context: "https://example.com", + }, + ]); + await runTest({ + description: "Load site with HTTPS, HOM enabled but site exempt", + topLevelScheme: "https", + + expectedSameOrigin: "error", // Mixed Content + expectedCrossOrigin: "error", // Mixed Content + }); + + // Remove permission again (has to be done manually for some reason?) + await SpecialPowers.popPermissions(); +}); + +const SERVER_URL = scheme => + `${scheme}://example.com/browser/dom/security/test/https-only/file_cors_mixedcontent.html`; + +async function runTest(test) { + await BrowserTestUtils.withNewTab("about:blank", async function (browser) { + let loaded = BrowserTestUtils.browserLoaded(browser); + + BrowserTestUtils.loadURIString(browser, SERVER_URL(test.topLevelScheme)); + + await loaded; + + // eslint-disable-next-line no-shadow + await SpecialPowers.spawn(browser, [test], async function (test) { + const promise = new Promise(resolve => { + content.addEventListener("FetchEnded", resolve, { + once: true, + }); + }); + + content.dispatchEvent(new content.Event("StartFetch")); + + const { detail } = await promise; + + is( + detail.comResult, + test.expectedSameOrigin, + `${test.description} (same-origin)` + ); + is( + detail.orgResult, + test.expectedCrossOrigin, + `${test.description} (cross-origin)` + ); + }); + }); +} diff --git a/dom/security/test/https-only/browser_hsts_host.js b/dom/security/test/https-only/browser_hsts_host.js new file mode 100644 index 0000000000..7adb365b94 --- /dev/null +++ b/dom/security/test/https-only/browser_hsts_host.js @@ -0,0 +1,182 @@ +// Bug 1722489 - HTTPS-Only Mode - Tests evaluation order +// https://bugzilla.mozilla.org/show_bug.cgi?id=1722489 +// This test ensures that an http request to an hsts host +// gets upgraded by hsts and not by https-only. +"use strict"; + +// Set bools to track that tests ended. +let readMessage = false; +let testFinished = false; +// Visit a secure site that sends an HSTS header to set up the rest of the +// test. +add_task(async function see_hsts_header() { + let setHstsUrl = + getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + "https://example.com" + ) + "hsts_headers.sjs"; + Services.obs.addObserver(observer, "http-on-examine-response"); + await BrowserTestUtils.loadURIString(gBrowser.selectedBrowser, setHstsUrl); + + await BrowserTestUtils.waitForCondition(() => readMessage); + // Clean up + Services.obs.removeObserver(observer, "http-on-examine-response"); +}); + +// Test that HTTPS_Only is not performed if HSTS host is visited. +add_task(async function () { + // A longer timeout is necessary for this test than the plain mochitests + // due to opening a new tab with the web console. + requestLongerTimeout(4); + + // Enable HTTPS-Only Mode and register console-listener + await SpecialPowers.pushPrefEnv({ + set: [["dom.security.https_only_mode", true]], + }); + + Services.console.registerListener(onNewMessage); + const RESOURCE_LINK = + getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + "http://example.com" + ) + "hsts_headers.sjs"; + + // 1. Upgrade page to https:// + await BrowserTestUtils.loadURIString(gBrowser.selectedBrowser, RESOURCE_LINK); + + await BrowserTestUtils.waitForCondition(() => testFinished); + + // Clean up + Services.console.unregisterListener(onNewMessage); + + await SpecialPowers.popPrefEnv(); +}); + +// Test that when clicking on #fragment with a different scheme (http vs https) +// DOES cause an actual navigation with HSTS, even though https-only mode is +// enabled. +add_task(async function () { + await SpecialPowers.pushPrefEnv({ + set: [ + ["dom.security.https_only_mode", true], + [ + "dom.security.https_only_mode_break_upgrade_downgrade_endless_loop", + false, + ], + ], + }); + + const TEST_PAGE = + "http://example.com/browser/dom/security/test/https-only/file_fragment_noscript.html"; + + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: TEST_PAGE, + waitForLoad: true, + }, + async function (browser) { + const UPGRADED_URL = TEST_PAGE.replace("http:", "https:"); + + await SpecialPowers.spawn(browser, [UPGRADED_URL], async function (url) { + is(content.window.location.href, url); + + content.window.addEventListener("scroll", () => { + ok(false, "scroll event should not trigger"); + }); + + let beforeUnload = new Promise(resolve => { + content.window.addEventListener("beforeunload", resolve, { + once: true, + }); + }); + + content.window.document.querySelector("#clickMeButton").click(); + + // Wait for unload event. + await beforeUnload; + }); + + await BrowserTestUtils.browserLoaded(browser); + + await SpecialPowers.spawn(browser, [UPGRADED_URL], async function (url) { + is(content.window.location.href, url + "#foo"); + }); + } + ); + + await SpecialPowers.popPrefEnv(); +}); + +add_task(async function () { + // Reset HSTS header + readMessage = false; + let clearHstsUrl = + getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + "https://example.com" + ) + "hsts_headers.sjs?reset"; + + Services.obs.addObserver(observer, "http-on-examine-response"); + // reset hsts header + await BrowserTestUtils.loadURIString(gBrowser.selectedBrowser, clearHstsUrl); + await BrowserTestUtils.waitForCondition(() => readMessage); + // Clean up + Services.obs.removeObserver(observer, "http-on-examine-response"); +}); + +function observer(subject, topic, state) { + info("observer called with " + topic); + if (topic == "http-on-examine-response") { + onExamineResponse(subject); + } +} + +function onExamineResponse(subject) { + let channel = subject.QueryInterface(Ci.nsIHttpChannel); + // If message was already read or is not related to "example.com", + // don't examine it. + if (!channel.URI.spec.includes("example.com") || readMessage) { + return; + } + info("onExamineResponse with " + channel.URI.spec); + if (channel.URI.spec.includes("reset")) { + try { + let hsts = channel.getResponseHeader("Strict-Transport-Security"); + is(hsts, "max-age=0", "HSTS header is not set"); + } catch (e) { + ok(false, "HSTS header still set"); + } + readMessage = true; + return; + } + try { + let hsts = channel.getResponseHeader("Strict-Transport-Security"); + let csp = channel.getResponseHeader("Content-Security-Policy"); + // Check that HSTS and CSP upgrade headers are set + is(hsts, "max-age=60", "HSTS header is set"); + is(csp, "upgrade-insecure-requests", "CSP header is set"); + } catch (e) { + ok(false, "No header set"); + } + readMessage = true; +} + +function onNewMessage(msgObj) { + const message = msgObj.message; + // ensure that request is not upgraded HTTPS-Only. + if (message.includes("Upgrading insecure request")) { + ok(false, "Top-Level upgrade shouldn't get logged"); + testFinished = true; + } else if ( + message.includes("Upgrading insecure speculative TCP connection") + ) { + // TODO: Check assertion + // https://bugzilla.mozilla.org/show_bug.cgi?id=1735683 + ok(true, "Top-Level upgrade shouldn't get logged"); + testFinished = true; + } else if (gBrowser.selectedBrowser.currentURI.scheme === "https") { + ok(true, "Top-Level upgrade shouldn't get logged"); + testFinished = true; + } +} diff --git a/dom/security/test/https-only/browser_httpsonly_prefs.js b/dom/security/test/https-only/browser_httpsonly_prefs.js new file mode 100644 index 0000000000..467ab490e7 --- /dev/null +++ b/dom/security/test/https-only/browser_httpsonly_prefs.js @@ -0,0 +1,118 @@ +"use strict"; + +async function runPrefTest( + aHTTPSOnlyPref, + aHTTPSOnlyPrefPBM, + aExecuteFromPBM, + aDesc, + aAssertURLStartsWith +) { + await SpecialPowers.pushPrefEnv({ + set: [ + ["dom.security.https_only_mode", aHTTPSOnlyPref], + ["dom.security.https_only_mode_pbm", aHTTPSOnlyPrefPBM], + ], + }); + + await BrowserTestUtils.withNewTab("about:blank", async function (browser) { + await ContentTask.spawn( + browser, + { aExecuteFromPBM, aDesc, aAssertURLStartsWith }, + // eslint-disable-next-line no-shadow + async function ({ aExecuteFromPBM, aDesc, aAssertURLStartsWith }) { + const responseURL = await new Promise(resolve => { + let xhr = new XMLHttpRequest(); + xhr.timeout = 1200; + xhr.open("GET", "http://example.com"); + if (aExecuteFromPBM) { + xhr.channel.loadInfo.originAttributes = { + privateBrowsingId: 1, + }; + } + xhr.onreadystatechange = () => { + // We don't care about the result and it's possible that + // the requests might even succeed in some testing environments + if ( + xhr.readyState !== XMLHttpRequest.OPENED || + xhr.readyState !== XMLHttpRequest.UNSENT + ) { + // Let's make sure this function does not get called anymore + xhr.onreadystatechange = undefined; + resolve(xhr.responseURL); + } + }; + xhr.send(); + }); + ok(responseURL.startsWith(aAssertURLStartsWith), aDesc); + } + ); + }); +} + +add_task(async function () { + requestLongerTimeout(2); + + await runPrefTest( + false, + false, + false, + "Setting no prefs should not upgrade", + "http://" + ); + + await runPrefTest( + true, + false, + false, + "Setting aHTTPSOnlyPref should upgrade", + "https://" + ); + + await runPrefTest( + false, + true, + false, + "Setting aHTTPSOnlyPrefPBM should not upgrade", + "http://" + ); + + await runPrefTest( + false, + false, + true, + "Setting aPBM should not upgrade", + "http://" + ); + + await runPrefTest( + true, + true, + false, + "Setting aHTTPSOnlyPref and aHTTPSOnlyPrefPBM should should upgrade", + "https://" + ); + + await runPrefTest( + true, + false, + true, + "Setting aHTTPSOnlyPref and aPBM should upgrade", + "https://" + ); + + await runPrefTest( + false, + true, + true, + "Setting aHTTPSOnlyPrefPBM and aPBM should upgrade", + "https://" + ); + + await runPrefTest( + true, + true, + true, + "Setting aHTTPSOnlyPref and aHTTPSOnlyPrefPBM and aPBM should upgrade", + "https://" + ); +}); diff --git a/dom/security/test/https-only/browser_httpsonly_speculative_connect.js b/dom/security/test/https-only/browser_httpsonly_speculative_connect.js new file mode 100644 index 0000000000..664d624ab8 --- /dev/null +++ b/dom/security/test/https-only/browser_httpsonly_speculative_connect.js @@ -0,0 +1,69 @@ +"use strict"; + +const TEST_PATH_HTTP = getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + "http://example.org" +); + +let console_messages = [ + { + description: "Speculative Connection should get logged", + expectLogLevel: Ci.nsIConsoleMessage.warn, + expectIncludes: [ + "Upgrading insecure speculative TCP connection", + "to use", + "example.org", + "file_httpsonly_speculative_connect.html", + ], + }, + { + description: "Upgrade should get logged", + expectLogLevel: Ci.nsIConsoleMessage.warn, + expectIncludes: [ + "Upgrading insecure request", + "to use", + "example.org", + "file_httpsonly_speculative_connect.html", + ], + }, +]; + +function on_new_console_messages(msgObj) { + const message = msgObj.message; + const logLevel = msgObj.logLevel; + + if (message.includes("HTTPS-Only Mode:")) { + for (let i = 0; i < console_messages.length; i++) { + const testCase = console_messages[i]; + // Check if log-level matches + if (logLevel !== testCase.expectLogLevel) { + continue; + } + // Check if all substrings are included + if (testCase.expectIncludes.some(str => !message.includes(str))) { + continue; + } + ok(true, testCase.description); + console_messages.splice(i, 1); + break; + } + } +} + +add_task(async function () { + requestLongerTimeout(4); + + await SpecialPowers.pushPrefEnv({ + set: [["dom.security.https_only_mode", true]], + }); + Services.console.registerListener(on_new_console_messages); + + await BrowserTestUtils.loadURIString( + gBrowser.selectedBrowser, + `${TEST_PATH_HTTP}file_httpsonly_speculative_connect.html` + ); + + await BrowserTestUtils.waitForCondition(() => console_messages.length === 0); + + Services.console.unregisterListener(on_new_console_messages); +}); diff --git a/dom/security/test/https-only/browser_iframe_test.js b/dom/security/test/https-only/browser_iframe_test.js new file mode 100644 index 0000000000..b57a56a8fc --- /dev/null +++ b/dom/security/test/https-only/browser_iframe_test.js @@ -0,0 +1,223 @@ +// Bug 1658264 - Https-Only: HTTPS-Only and iFrames +// https://bugzilla.mozilla.org/show_bug.cgi?id=1658264 +"use strict"; + +// > How does this test work? +// We're sending a request to file_iframe_test.sjs with various +// browser-configurations. The sjs-file returns a website with two iFrames +// loading the same sjs-file again. One iFrame is same origin (example.com) and +// the other cross-origin (example.org) Each request gets saved in a semicolon +// seperated list of strings. The sjs-file gets initialized with the +// query-string "setup" and the result string can be polled with "results". Each +// string has this format: {top/com/org}-{queryString}-{scheme}. In the end +// we're just checking if all expected requests were recorded and had the +// correct scheme. Requests that are meant to fail should explicitly not be +// contained in the list of results. + +// The test loads all tabs and evaluates when all have finished loading +// it may take quite a long time. +// This requires more twice as much as the default 45 seconds per test: +requestLongerTimeout(2); +SimpleTest.requestCompleteLog(); + +add_task(async function () { + await setup(); + + // Using this variable to parallelize and collect tests + let testSet = []; + + /* + * HTTPS-Only Mode disabled + */ + await SpecialPowers.pushPrefEnv({ + set: [["dom.security.https_only_mode", false]], + }); + + // Top-Level scheme: HTTP + // NOTE(freddyb): Test case temporarily disabled. See bug 1735565 + /*testSet.push( + runTest({ + queryString: "test1.1", + topLevelScheme: "http", + + expectedTopLevel: "http", + expectedSameOrigin: "http", + expectedCrossOrigin: "http", + }) + );*/ + // Top-Level scheme: HTTPS + testSet.push( + runTest({ + queryString: "test1.2", + topLevelScheme: "https", + + expectedTopLevel: "https", + expectedSameOrigin: "fail", + expectedCrossOrigin: "fail", + }) + ); + + await Promise.all(testSet); + testSet = []; + /* + * HTTPS-Only Mode enabled, no exception + */ + await SpecialPowers.pushPrefEnv({ + set: [["dom.security.https_only_mode", true]], + }); + + // Top-Level scheme: HTTP + testSet.push( + runTest({ + queryString: "test2.1", + topLevelScheme: "http", + + expectedTopLevel: "https", + expectedSameOrigin: "https", + expectedCrossOrigin: "https", + }) + ); + // Top-Level scheme: HTTPS + testSet.push( + runTest({ + queryString: "test2.2", + topLevelScheme: "https", + + expectedTopLevel: "https", + expectedSameOrigin: "https", + expectedCrossOrigin: "https", + }) + ); + + await Promise.all(testSet); + testSet = []; + + /* + * HTTPS-Only enabled, with exceptions + * for http://example.org and http://example.com + */ + // Exempting example.org (cross-site) should not affect anything + await SpecialPowers.pushPermissions([ + { + type: "https-only-load-insecure", + allow: true, + context: "http://example.org", + }, + ]); + await SpecialPowers.pushPermissions([ + { + type: "https-only-load-insecure", + allow: true, + context: "http://example.com", + }, + ]); + + // Top-Level scheme: HTTP + await runTest({ + queryString: "test3.1", + topLevelScheme: "http", + + expectedTopLevel: "http", + expectedSameOrigin: "http", + expectedCrossOrigin: "http", + }); + + await SpecialPowers.popPermissions(); + await SpecialPowers.pushPermissions([ + { + type: "https-only-load-insecure", + allow: true, + context: "https://example.com", + }, + ]); + // Top-Level scheme: HTTPS + await runTest({ + queryString: "test3.2", + topLevelScheme: "https", + + expectedTopLevel: "https", + expectedSameOrigin: "fail", + expectedCrossOrigin: "fail", + }); + + // Remove permissions again (has to be done manually for some reason?) + await SpecialPowers.popPermissions(); + await SpecialPowers.popPermissions(); + + await evaluate(); +}); + +const SERVER_URL = scheme => + `${scheme}://example.com/browser/dom/security/test/https-only/file_iframe_test.sjs?`; +let shouldContain = []; +let shouldNotContain = []; + +async function setup() { + info(`TEST-CASE-setup - A`); + const response = await fetch(SERVER_URL("https") + "setup"); + info(`TEST-CASE-setup - B`); + const txt = await response.text(); + info(`TEST-CASE-setup - C`); + if (txt != "ok") { + ok(false, "Failed to setup test server."); + finish(); + } +} + +async function evaluate() { + info(`TEST-CASE-evaluate - A`); + const response = await fetch(SERVER_URL("https") + "results"); + info(`TEST-CASE-evaluate - B`); + const requestResults = (await response.text()).split(";"); + info(`TEST-CASE-evaluate - C`); + + shouldContain.map(str => + ok(requestResults.includes(str), `Results should contain '${str}'.`) + ); + shouldNotContain.map(str => + ok(!requestResults.includes(str), `Results shouldn't contain '${str}'.`) + ); +} + +async function runTest(test) { + const queryString = test.queryString; + info(`TEST-CASE-${test.queryString} - runTest BEGIN`); + await BrowserTestUtils.withNewTab("about:blank", async function (browser) { + let loaded = BrowserTestUtils.browserLoaded( + browser, + false, // includeSubFrames + SERVER_URL(test.expectedTopLevel) + queryString, + false // maybeErrorPage + ); + BrowserTestUtils.loadURIString( + browser, + SERVER_URL(test.topLevelScheme) + queryString + ); + info(`TEST-CASE-${test.queryString} - Before 'await loaded'`); + await loaded; + info(`TEST-CASE-${test.queryString} - After 'await loaded'`); + }); + info(`TEST-CASE-${test.queryString} - After 'await withNewTab'`); + + if (test.expectedTopLevel !== "fail") { + shouldContain.push(`top-${queryString}-${test.expectedTopLevel}`); + } else { + shouldNotContain.push(`top-${queryString}-http`); + shouldNotContain.push(`top-${queryString}-https`); + } + + if (test.expectedSameOrigin !== "fail") { + shouldContain.push(`com-${queryString}-${test.expectedSameOrigin}`); + } else { + shouldNotContain.push(`com-${queryString}-http`); + shouldNotContain.push(`com-${queryString}-https`); + } + + if (test.expectedCrossOrigin !== "fail") { + shouldContain.push(`org-${queryString}-${test.expectedCrossOrigin}`); + } else { + shouldNotContain.push(`org-${queryString}-http`); + shouldNotContain.push(`org-${queryString}-https`); + } + info(`TEST-CASE-${test.queryString} - runTest END`); +} diff --git a/dom/security/test/https-only/browser_redirect_tainting.js b/dom/security/test/https-only/browser_redirect_tainting.js new file mode 100644 index 0000000000..0823ec4658 --- /dev/null +++ b/dom/security/test/https-only/browser_redirect_tainting.js @@ -0,0 +1,39 @@ +/* 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/. */ + +// Test steps: +// 1. Load file_redirect_tainting.sjs?html. +// 2. The server returns an html which loads an image at http://example.net. +// 3. The image request will be upgraded to HTTPS since HTTPS-only mode is on. +// 4. In file_redirect_tainting.sjs, we set "Access-Control-Allow-Origin" to +// the value of the Origin header. +// 5. If the vlaue does not match, the image won't be loaded. +async function do_test() { + let requestUrl = `https://example.com/browser/dom/security/test/https-only/file_redirect_tainting.sjs?html`; + + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: requestUrl, + waitForLoad: true, + }, + async function (browser) { + let imageLoaded = await SpecialPowers.spawn(browser, [], function () { + let image = content.document.getElementById("test_image"); + return image && image.complete && image.naturalHeight !== 0; + }); + await Assert.ok(imageLoaded, "test_image should be loaded"); + } + ); +} + +add_task(async function test_https_only_redirect_tainting() { + await SpecialPowers.pushPrefEnv({ + set: [["dom.security.https_only_mode", true]], + }); + + await do_test(); + + await SpecialPowers.popPrefEnv(); +}); diff --git a/dom/security/test/https-only/browser_triggering_principal_exemption.js b/dom/security/test/https-only/browser_triggering_principal_exemption.js new file mode 100644 index 0000000000..09a867a63b --- /dev/null +++ b/dom/security/test/https-only/browser_triggering_principal_exemption.js @@ -0,0 +1,72 @@ +// Bug 1662359 - Don't upgrade subresources whose triggering principal is exempt from HTTPS-Only mode. +// https://bugzilla.mozilla.org/bug/1662359 +"use strict"; + +const TRIGGERING_PAGE = "http://example.org"; +const LOADED_RESOURCE = "http://example.com"; + +add_task(async function () { + // Enable HTTPS-Only Mode + await SpecialPowers.pushPrefEnv({ + set: [["dom.security.https_only_mode", true]], + }); + + await runTest( + "Request with not exempt triggering principal should get upgraded.", + "https://" + ); + + // Now exempt the triggering page + await SpecialPowers.pushPermissions([ + { + type: "https-only-load-insecure", + allow: true, + context: TRIGGERING_PAGE, + }, + ]); + + await runTest( + "Request with exempt triggering principal should not get upgraded.", + "http://" + ); + + await SpecialPowers.popPermissions(); +}); + +async function runTest(desc, startsWith) { + const responseURL = await new Promise(resolve => { + let xhr = new XMLHttpRequest(); + xhr.open("GET", LOADED_RESOURCE); + + // Replace loadinfo with one whose triggeringPrincipal is a content + // principal for TRIGGERING_PAGE. + const triggeringPrincipal = + Services.scriptSecurityManager.createContentPrincipalFromOrigin( + TRIGGERING_PAGE + ); + let dummyURI = Services.io.newURI(LOADED_RESOURCE); + let dummyChannel = NetUtil.newChannel({ + uri: dummyURI, + triggeringPrincipal, + loadingPrincipal: xhr.channel.loadInfo.loadingPrincipal, + securityFlags: xhr.channel.loadInfo.securityFlags, + contentPolicyType: xhr.channel.loadInfo.externalContentPolicyType, + }); + xhr.channel.loadInfo = dummyChannel.loadInfo; + + xhr.onreadystatechange = () => { + // We don't care about the result, just if Firefox upgraded the URL + // internally. + if ( + xhr.readyState !== XMLHttpRequest.OPENED || + xhr.readyState !== XMLHttpRequest.UNSENT + ) { + // Let's make sure this function doesn't get called anymore + xhr.onreadystatechange = undefined; + resolve(xhr.responseURL); + } + }; + xhr.send(); + }); + ok(responseURL.startsWith(startsWith), desc); +} diff --git a/dom/security/test/https-only/browser_upgrade_exceptions.js b/dom/security/test/https-only/browser_upgrade_exceptions.js new file mode 100644 index 0000000000..8611b32a0f --- /dev/null +++ b/dom/security/test/https-only/browser_upgrade_exceptions.js @@ -0,0 +1,86 @@ +// Bug 1625448 - HTTPS Only Mode - Exceptions for loopback and local IP addresses +// https://bugzilla.mozilla.org/show_bug.cgi?id=1631384 +// This test ensures that various configurable upgrade exceptions work +"use strict"; + +add_task(async function () { + requestLongerTimeout(2); + + await SpecialPowers.pushPrefEnv({ + set: [["dom.security.https_only_mode", true]], + }); + + // Loopback test + await runTest( + "Loopback IP addresses should always be exempt from upgrades (localhost)", + "http://localhost", + "http://" + ); + await runTest( + "Loopback IP addresses should always be exempt from upgrades (127.0.0.1)", + "http://127.0.0.1", + "http://" + ); + // Default local-IP and onion tests + await runTest( + "Local IP addresses should be exempt from upgrades by default", + "http://10.0.250.250", + "http://" + ); + await runTest( + "Hosts ending with .onion should be be exempt from HTTPS-Only upgrades by default", + "http://grocery.shopping.for.one.onion", + "http://" + ); + + await SpecialPowers.pushPrefEnv({ + set: [ + ["dom.security.https_only_mode.upgrade_local", true], + ["dom.security.https_only_mode.upgrade_onion", true], + ], + }); + + // Local-IP and onion tests with upgrade enabled + await runTest( + "Local IP addresses should get upgraded when 'dom.security.https_only_mode.upgrade_local' is set to true", + "http://10.0.250.250", + "https://" + ); + await runTest( + "Hosts ending with .onion should get upgraded when 'dom.security.https_only_mode.upgrade_onion' is set to true", + "http://grocery.shopping.for.one.onion", + "https://" + ); + // Local-IP request with HTTPS_ONLY_EXEMPT flag + await runTest( + "The HTTPS_ONLY_EXEMPT flag should overrule upgrade-prefs", + "http://10.0.250.250", + "http://", + true + ); +}); + +async function runTest(desc, url, startsWith, exempt = false) { + const responseURL = await new Promise(resolve => { + let xhr = new XMLHttpRequest(); + xhr.timeout = 1200; + xhr.open("GET", url); + if (exempt) { + xhr.channel.loadInfo.httpsOnlyStatus |= Ci.nsILoadInfo.HTTPS_ONLY_EXEMPT; + } + xhr.onreadystatechange = () => { + // We don't care about the result and it's possible that + // the requests might even succeed in some testing environments + if ( + xhr.readyState !== XMLHttpRequest.OPENED || + xhr.readyState !== XMLHttpRequest.UNSENT + ) { + // Let's make sure this function doesn't get caled anymore + xhr.onreadystatechange = undefined; + resolve(xhr.responseURL); + } + }; + xhr.send(); + }); + ok(responseURL.startsWith(startsWith), desc); +} diff --git a/dom/security/test/https-only/browser_user_gesture.js b/dom/security/test/https-only/browser_user_gesture.js new file mode 100644 index 0000000000..198b01bdb6 --- /dev/null +++ b/dom/security/test/https-only/browser_user_gesture.js @@ -0,0 +1,55 @@ +// Bug 1725026 - HTTPS Only Mode - Test if a load triggered by a user gesture +// https://bugzilla.mozilla.org/show_bug.cgi?id=1725026 +// Test if a load triggered by a user gesture can be upgraded to HTTPS +// successfully. + +"use strict"; + +const testPathUpgradeable = getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + "http://example.com" +); + +const kTestURI = testPathUpgradeable + "file_user_gesture.html"; + +add_task(async function () { + // Enable HTTPS-Only Mode and register console-listener + await SpecialPowers.pushPrefEnv({ + set: [["dom.security.https_only_mode", true]], + }); + + await BrowserTestUtils.withNewTab("about:blank", async function (browser) { + const loaded = BrowserTestUtils.browserLoaded(browser, false, null, true); + // 1. Upgrade a page to https:// + BrowserTestUtils.loadURIString(browser, kTestURI); + await loaded; + await ContentTask.spawn(browser, {}, async args => { + ok( + content.document.location.href.startsWith("https://"), + "Should be https" + ); + + // 2. Trigger a load by clicking button. + // The scheme of the link url is `http` and the load should be able to + // upgraded to `https` because of HTTPS-only mode. + let button = content.document.getElementById("httpLinkButton"); + await EventUtils.synthesizeMouseAtCenter( + button, + { type: "mousedown" }, + content + ); + await EventUtils.synthesizeMouseAtCenter( + button, + { type: "mouseup" }, + content + ); + await ContentTaskUtils.waitForCondition(() => { + return content.document.location.href.startsWith("https://"); + }); + ok( + content.document.location.href.startsWith("https://"), + "Should be https" + ); + }); + }); +}); diff --git a/dom/security/test/https-only/browser_websocket_exceptions.js b/dom/security/test/https-only/browser_websocket_exceptions.js new file mode 100644 index 0000000000..f68a834cee --- /dev/null +++ b/dom/security/test/https-only/browser_websocket_exceptions.js @@ -0,0 +1,68 @@ +"use strict"; + +const TEST_PATH_HTTP = getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + "http://localhost:9898" +); + +let WEBSOCKET_DOC_URL = `${TEST_PATH_HTTP}file_websocket_exceptions.html`; + +add_task(async function () { + // Here is a sequence of how this test works: + // 1. Dynamically inject a localhost iframe + // 2. Add an exemption for localhost + // 3. Fire up Websocket + // Generally local IP addresses are exempt from https-only, but if we do not add + // an exemption for localhost, then the TriggeringPrincipal of the WebSocket is + // `not` exempt and we would upgrade ws to wss. + + await SpecialPowers.pushPrefEnv({ + set: [ + ["dom.security.https_only_mode", true], + ["network.proxy.allow_hijacking_localhost", true], + ], + }); + + await BrowserTestUtils.withNewTab("about:blank", async function (browser) { + let loaded = BrowserTestUtils.browserLoaded(browser); + + BrowserTestUtils.loadURIString(browser, WEBSOCKET_DOC_URL); + await loaded; + + await SpecialPowers.spawn(browser, [], async function () { + // Part 1: + let myIframe = content.document.createElement("iframe"); + content.document.body.appendChild(myIframe); + myIframe.src = + "http://localhost:9898/browser/dom/security/test/https-only/file_websocket_exceptions_iframe.html"; + + myIframe.onload = async function () { + // Part 2: + await SpecialPowers.pushPermissions([ + { + type: "https-only-load-insecure", + allow: true, + context: "http://localhost:9898", + }, + ]); + // Part 3. + myIframe.contentWindow.postMessage({ myMessage: "runWebSocket" }, "*"); + }; + + const promise = new Promise(resolve => { + content.addEventListener("WebSocketEnded", resolve, { + once: true, + }); + }); + + const { detail } = await promise; + + is(detail.state, "onopen", "sanity: websocket loaded"); + ok( + detail.url.startsWith("ws://example.com/tests"), + "exempt websocket should not be upgraded to wss://" + ); + }); + }); + await SpecialPowers.popPermissions(); +}); diff --git a/dom/security/test/https-only/file_background_redirect.sjs b/dom/security/test/https-only/file_background_redirect.sjs new file mode 100644 index 0000000000..45e01797e3 --- /dev/null +++ b/dom/security/test/https-only/file_background_redirect.sjs @@ -0,0 +1,32 @@ +// Custom *.sjs file specifically for the needs of Bug 1683015 +"use strict"; + +let { setTimeout } = ChromeUtils.importESModule( + "resource://gre/modules/Timer.sys.mjs" +); + +async function handleRequest(request, response) { + // avoid confusing cache behaviour + response.setHeader("Cache-Control", "no-cache", false); + response.setHeader("Content-Type", "text/html", false); + + let query = request.queryString; + if (request.scheme === "https" && query === "start") { + // Simulating long repsonse time by processing the https request + // using a 5 seconds delay. Note that the http background request + // gets send after 3 seconds + response.processAsync(); + setTimeout(() => { + response.setStatusLine("1.1", 200, "OK"); + response.write( + "Test Page for Bug 1683015 loaded" + ); + response.finish(); + }, 5000); /* wait 5 seconds */ + return; + } + + // we should never get here, but just in case return something unexpected + response.setStatusLine("1.1", 404, "Not Found"); + response.write("SHOULDN'T DISPLAY THIS PAGE"); +} diff --git a/dom/security/test/https-only/file_break_endless_upgrade_downgrade_loop.sjs b/dom/security/test/https-only/file_break_endless_upgrade_downgrade_loop.sjs new file mode 100644 index 0000000000..a8a9083ef3 --- /dev/null +++ b/dom/security/test/https-only/file_break_endless_upgrade_downgrade_loop.sjs @@ -0,0 +1,67 @@ +// Custom *.sjs file specifically for the needs of Bug 1691888 +"use strict"; + +const REDIRECT_META = ` + + + + + + META REDIRECT + + `; + +const REDIRECT_JS = ` + + + JS REDIRECT + + + `; + +const REDIRECT_302 = + "http://example.com/tests/dom/security/test/https-only/file_break_endless_upgrade_downgrade_loop.sjs?test3b"; + +const REDIRECT_302_DIFFERENT_PATH = + "http://example.com/tests/dom/security/test/https-only/file_user_gesture.html"; + +function handleRequest(request, response) { + // avoid confusing cache behaviour + response.setHeader("Cache-Control", "no-cache", false); + response.setHeader("Content-Type", "text/html", false); + + // if the scheme is not https, meaning that the initial request did not + // get upgraded, then we rather fall through and display unexpected content. + if (request.scheme === "https") { + let query = request.queryString; + + if (query === "test1a") { + response.write(REDIRECT_META); + return; + } + + if (query === "test2a") { + response.write(REDIRECT_JS); + return; + } + + if (query === "test3a") { + response.setStatusLine("1.1", 302, "Found"); + response.setHeader("Location", REDIRECT_302, false); + return; + } + + if (query === "test4a") { + response.setStatusLine("1.1", 302, "Found"); + response.setHeader("Location", REDIRECT_302_DIFFERENT_PATH, false); + return; + } + } + + // we should never get here, just in case, + // let's return something unexpected + response.write("DO NOT DISPLAY THIS"); +} diff --git a/dom/security/test/https-only/file_console_logging.html b/dom/security/test/https-only/file_console_logging.html new file mode 100644 index 0000000000..94e07cddd9 --- /dev/null +++ b/dom/security/test/https-only/file_console_logging.html @@ -0,0 +1,16 @@ + + + + + Bug 1625448 - HTTPS Only Mode - Tests for console logging + + + + + + + + + + + diff --git a/dom/security/test/https-only/file_cors_mixedcontent.html b/dom/security/test/https-only/file_cors_mixedcontent.html new file mode 100644 index 0000000000..50d32954ef --- /dev/null +++ b/dom/security/test/https-only/file_cors_mixedcontent.html @@ -0,0 +1,42 @@ + + + + + + + + + + +

Https-Only: CORS and MixedContent tests

+

Bug 1659505

+ + + diff --git a/dom/security/test/https-only/file_fragment.html b/dom/security/test/https-only/file_fragment.html new file mode 100644 index 0000000000..49cff76f9a --- /dev/null +++ b/dom/security/test/https-only/file_fragment.html @@ -0,0 +1,47 @@ + + + + + Click me +
space
+ foo +
space
+ + diff --git a/dom/security/test/https-only/file_fragment_noscript.html b/dom/security/test/https-only/file_fragment_noscript.html new file mode 100644 index 0000000000..609961fd98 --- /dev/null +++ b/dom/security/test/https-only/file_fragment_noscript.html @@ -0,0 +1,9 @@ + + + + Click me +
space
+ foo +
space
+ + diff --git a/dom/security/test/https-only/file_http_background_auth_request.sjs b/dom/security/test/https-only/file_http_background_auth_request.sjs new file mode 100644 index 0000000000..80fc8ffcf7 --- /dev/null +++ b/dom/security/test/https-only/file_http_background_auth_request.sjs @@ -0,0 +1,16 @@ +// Custom *.sjs file specifically for the needs of Bug 1665062 + +function handleRequest(request, response) { + // avoid confusing cache behaviors + response.setHeader("Cache-Control", "no-cache", false); + + if (request.scheme === "https") { + response.setHeader("Content-Type", "text/html;charset=utf-8", false); + response.setStatusLine(request.httpVersion, 401, "Unauthorized"); + response.setHeader("WWW-Authenticate", 'Basic realm="bug1665062"'); + return; + } + + // we should never get here; just in case, return something unexpected + response.write("do'h"); +} diff --git a/dom/security/test/https-only/file_http_background_request.sjs b/dom/security/test/https-only/file_http_background_request.sjs new file mode 100644 index 0000000000..ef0e2ce5bd --- /dev/null +++ b/dom/security/test/https-only/file_http_background_request.sjs @@ -0,0 +1,15 @@ +// Custom *.sjs file specifically for the needs of Bug 1663396 + +function handleRequest(request, response) { + // avoid confusing cache behaviors + response.setHeader("Cache-Control", "no-cache", false); + + if (request.scheme === "https") { + // Simulating a timeout by processing the https request + // async and *never* return anything! + response.processAsync(); + return; + } + // we should never get here; just in case, return something unexpected + response.write("do'h"); +} diff --git a/dom/security/test/https-only/file_httpsonly_speculative_connect.html b/dom/security/test/https-only/file_httpsonly_speculative_connect.html new file mode 100644 index 0000000000..46a10401f9 --- /dev/null +++ b/dom/security/test/https-only/file_httpsonly_speculative_connect.html @@ -0,0 +1 @@ +dummy file for speculative https-only upgrade test diff --git a/dom/security/test/https-only/file_iframe_test.sjs b/dom/security/test/https-only/file_iframe_test.sjs new file mode 100644 index 0000000000..611870c87c --- /dev/null +++ b/dom/security/test/https-only/file_iframe_test.sjs @@ -0,0 +1,58 @@ +// Bug 1658264 - HTTPS-Only and iFrames +// see browser_iframe_test.js + +const IFRAME_CONTENT = ` + + + + Helo Friend! +`; +const DOCUMENT_CONTENT = q => ` + + + + + + + +`; + +function handleRequest(request, response) { + // avoid confusing cache behaviors + response.setHeader("Cache-Control", "no-cache", false); + let queryString = request.queryString; + const queryScheme = request.scheme; + + // Setup the state with an empty string and return "ok" + if (queryString == "setup") { + setState("receivedQueries", ""); + response.write("ok"); + return; + } + + let receivedQueries = getState("receivedQueries"); + + // Return result-string + if (queryString == "results") { + response.write(receivedQueries); + return; + } + + // Add semicolon to seperate strings + if (receivedQueries !== "") { + receivedQueries += ";"; + } + + // Requests from iFrames start with com or org + if (queryString.startsWith("com-") || queryString.startsWith("org-")) { + receivedQueries += queryString; + setState("receivedQueries", `${receivedQueries}-${queryScheme}`); + response.write(IFRAME_CONTENT); + return; + } + + // Everything else has to be a top-level request + receivedQueries += `top-${queryString}`; + setState("receivedQueries", `${receivedQueries}-${queryScheme}`); + response.write(DOCUMENT_CONTENT(queryString)); +} diff --git a/dom/security/test/https-only/file_insecure_reload.sjs b/dom/security/test/https-only/file_insecure_reload.sjs new file mode 100644 index 0000000000..a48ec4e20f --- /dev/null +++ b/dom/security/test/https-only/file_insecure_reload.sjs @@ -0,0 +1,27 @@ +// https://bugzilla.mozilla.org/show_bug.cgi?id=1702001 + +// An onload postmessage to window opener +const ON_LOAD = ` + + + send onload message... + + + `; + +// When an https request is sent, cause a timeout so that the https-only error +// page is displayed. +function handleRequest(request, response) { + response.setHeader("Cache-Control", "no-cache", false); + if (request.scheme === "https") { + // Simulating a timeout by processing the https request + // async and *never* return anything! + response.processAsync(); + return; + } + if (request.scheme === "http") { + response.write(ON_LOAD); + } +} diff --git a/dom/security/test/https-only/file_redirect.sjs b/dom/security/test/https-only/file_redirect.sjs new file mode 100644 index 0000000000..c66cbaa226 --- /dev/null +++ b/dom/security/test/https-only/file_redirect.sjs @@ -0,0 +1,37 @@ +// https://bugzilla.mozilla.org/show_bug.cgi?id=1613063 + +// Step 1. Send request with redirect queryString (eg. file_redirect.sjs?302) +// Step 2. Server responds with corresponding redirect code to http://example.com/../file_redirect.sjs?check +// Step 3. Response from ?check indicates whether the redirected request was secure or not. + +const RESPONSE_SECURE = "secure-ok"; +const RESPONSE_INSECURE = "secure-error"; +const RESPONSE_ERROR = "unexpected-query"; + +function handleRequest(request, response) { + response.setHeader("Cache-Control", "no-cache", false); + + const query = request.queryString; + + // Send redirect header + if ((query >= 301 && query <= 303) || query == 307) { + const loc = + "http://example.com/tests/dom/security/test/https-only/file_redirect.sjs?check"; + response.setStatusLine(request.httpVersion, query, "Moved"); + response.setHeader("Location", loc, false); + return; + } + + // Check if scheme is http:// oder https:// + if (query == "check") { + const secure = + request.scheme == "https" ? RESPONSE_SECURE : RESPONSE_INSECURE; + response.setStatusLine(request.httpVersion, 200, "OK"); + response.write(secure); + return; + } + + // This should not happen + response.setStatusLine(request.httpVersion, 500, "OK"); + response.write(RESPONSE_ERROR); +} diff --git a/dom/security/test/https-only/file_redirect_tainting.sjs b/dom/security/test/https-only/file_redirect_tainting.sjs new file mode 100644 index 0000000000..9ecca88d6d --- /dev/null +++ b/dom/security/test/https-only/file_redirect_tainting.sjs @@ -0,0 +1,39 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +// small red image +const IMG_BYTES = atob( + "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12" + + "P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==" +); + +const body = ` + + + + +`; + +function handleRequest(request, response) { + if (request.queryString === "html") { + response.setStatusLine(request.httpVersion, 200, "OK"); + response.setHeader("Content-Type", "text/html"); + response.write(body); + return; + } + + response.setStatusLine(request.httpVersion, 200, "OK"); + response.setHeader("Content-Type", "image/png"); + let origin = request.getHeader("Origin"); + response.setHeader("Access-Control-Allow-Origin", origin); + response.write(IMG_BYTES); +} diff --git a/dom/security/test/https-only/file_upgrade_insecure.html b/dom/security/test/https-only/file_upgrade_insecure.html new file mode 100644 index 0000000000..d230a4fd9d --- /dev/null +++ b/dom/security/test/https-only/file_upgrade_insecure.html @@ -0,0 +1,91 @@ + + + + + Bug 1613063 - HTTPS Only Mode + + + + + + + + + + + + + + + + + + + + + + + + +
foo
+ + + + + + + + + + + + +
+ + +
+ + + + diff --git a/dom/security/test/https-only/file_upgrade_insecure_server.sjs b/dom/security/test/https-only/file_upgrade_insecure_server.sjs new file mode 100644 index 0000000000..7cf590141c --- /dev/null +++ b/dom/security/test/https-only/file_upgrade_insecure_server.sjs @@ -0,0 +1,112 @@ +// SJS file for HTTPS-Only Mode mochitests +// Bug 1613063 - HTTPS Only Mode + +const TOTAL_EXPECTED_REQUESTS = 11; + +const IFRAME_CONTENT = + "" + + "" + + "" + + "Bug 1613063 - HTTPS Only Mode" + + "" + + "" + + "" + + "" + + ""; + +const expectedQueries = [ + "script", + "style", + "img", + "iframe", + "form", + "xhr", + "media", + "object", + "font", + "img-redir", + "nested-img", +]; + +function handleRequest(request, response) { + // avoid confusing cache behaviors + response.setHeader("Cache-Control", "no-cache", false); + var queryString = request.queryString; + + // initialize server variables and save the object state + // of the initial request, which returns async once the + // server has processed all requests. + if (queryString == "queryresult") { + setState("totaltests", TOTAL_EXPECTED_REQUESTS.toString()); + setState("receivedQueries", ""); + response.processAsync(); + setObjectState("queryResult", response); + return; + } + + // handle img redirect (https->http) + if (queryString == "redirect-image") { + var newLocation = + "http://example.com/tests/dom/security/test/https-only/file_upgrade_insecure_server.sjs?img-redir"; + response.setStatusLine("1.1", 302, "Found"); + response.setHeader("Location", newLocation, false); + return; + } + + // just in case error handling for unexpected queries + if (!expectedQueries.includes(queryString)) { + response.write("unexpected-response"); + return; + } + + // make sure all the requested queries are indeed https + queryString += request.scheme == "https" ? "-ok" : "-error"; + + var receivedQueries = getState("receivedQueries"); + + // images, scripts, etc. get queried twice, do not + // confuse the server by storing the preload as + // well as the actual load. If either the preload + // or the actual load is not https, then we would + // append "-error" in the array and the test would + // fail at the end. + if (receivedQueries.includes(queryString)) { + return; + } + + // append the result to the total query string array + if (receivedQueries != "") { + receivedQueries += ","; + } + receivedQueries += queryString; + setState("receivedQueries", receivedQueries); + + // keep track of how many more requests the server + // is expecting + var totaltests = parseInt(getState("totaltests")); + totaltests -= 1; + setState("totaltests", totaltests.toString()); + + // return content (img) for the nested iframe to test + // that subresource requests within nested contexts + // get upgraded as well. We also have to return + // the iframe context in case of an error so we + // can test both, using upgrade-insecure as well + // as the base case of not using upgrade-insecure. + if (queryString == "iframe-ok" || queryString == "iframe-error") { + response.write(IFRAME_CONTENT); + } + + // if we have received all the requests, we return + // the result back. + if (totaltests == 0) { + getObjectState("queryResult", function (queryResponse) { + if (!queryResponse) { + return; + } + receivedQueries = getState("receivedQueries"); + queryResponse.write(receivedQueries); + queryResponse.finish(); + }); + } +} diff --git a/dom/security/test/https-only/file_upgrade_insecure_wsh.py b/dom/security/test/https-only/file_upgrade_insecure_wsh.py new file mode 100644 index 0000000000..b7159c742b --- /dev/null +++ b/dom/security/test/https-only/file_upgrade_insecure_wsh.py @@ -0,0 +1,6 @@ +def web_socket_do_extra_handshake(request): + pass + + +def web_socket_transfer_data(request): + pass diff --git a/dom/security/test/https-only/file_user_gesture.html b/dom/security/test/https-only/file_user_gesture.html new file mode 100644 index 0000000000..ac67064bf0 --- /dev/null +++ b/dom/security/test/https-only/file_user_gesture.html @@ -0,0 +1,11 @@ + + + + + Bug 1725026 - HTTPS Only Mode - Test if a load triggered by a user gesture can be upgraded to HTTPS + + + + + diff --git a/dom/security/test/https-only/file_websocket_exceptions.html b/dom/security/test/https-only/file_websocket_exceptions.html new file mode 100644 index 0000000000..6c6ba07480 --- /dev/null +++ b/dom/security/test/https-only/file_websocket_exceptions.html @@ -0,0 +1,9 @@ + + + + + + + Dummy file which gets iframe injected
+ + diff --git a/dom/security/test/https-only/file_websocket_exceptions_iframe.html b/dom/security/test/https-only/file_websocket_exceptions_iframe.html new file mode 100644 index 0000000000..23c6af2d45 --- /dev/null +++ b/dom/security/test/https-only/file_websocket_exceptions_iframe.html @@ -0,0 +1,30 @@ + + + + + + + + Https-Only: WebSocket exemption test in iframe
+ + diff --git a/dom/security/test/https-only/hsts_headers.sjs b/dom/security/test/https-only/hsts_headers.sjs new file mode 100644 index 0000000000..72e82caaf3 --- /dev/null +++ b/dom/security/test/https-only/hsts_headers.sjs @@ -0,0 +1,24 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +function handleRequest(request, response) { + if (request.queryString === "reset") { + // Reset the HSTS policy, prevent influencing other tests + response.setStatusLine(request.httpVersion, 200, "OK"); + response.setHeader("Strict-Transport-Security", "max-age=0"); + response.write("Resetting HSTS"); + return; + } + let hstsHeader = "max-age=60"; + response.setHeader("Strict-Transport-Security", hstsHeader); + response.setHeader("Cache-Control", "no-cache", false); + response.setHeader("Content-Type", "text/html", false); + // Set header for csp upgrade + response.setHeader( + "Content-Security-Policy", + "upgrade-insecure-requests", + false + ); + response.setStatusLine(request.httpVersion, 200); + response.write("

Ok!

"); +} diff --git a/dom/security/test/https-only/mochitest.ini b/dom/security/test/https-only/mochitest.ini new file mode 100644 index 0000000000..050e9de184 --- /dev/null +++ b/dom/security/test/https-only/mochitest.ini @@ -0,0 +1,43 @@ +[DEFAULT] +support-files = + file_redirect.sjs + file_upgrade_insecure.html + file_upgrade_insecure_server.sjs + file_upgrade_insecure_wsh.py +prefs = + dom.security.https_first=false + security.mixed_content.upgrade_display_content=false + +[test_resource_upgrade.html] +scheme=https +skip-if = + http3 +[test_redirect_upgrade.html] +scheme=https +fail-if = xorigin +skip-if = + http3 +[test_http_background_request.html] +support-files = file_http_background_request.sjs +skip-if = + http3 +[test_http_background_auth_request.html] +support-files = file_http_background_auth_request.sjs +skip-if = + http3 +[test_break_endless_upgrade_downgrade_loop.html] +skip-if = + (toolkit == 'android') # no support for error pages, Bug 1697866 + http3 +support-files = + file_break_endless_upgrade_downgrade_loop.sjs + file_user_gesture.html +[test_user_suggestion_box.html] +skip-if = + toolkit == 'android' # no https-only errorpage support in android + http3 +[test_fragment.html] +support-files = file_fragment.html +[test_insecure_reload.html] +support-files = file_insecure_reload.sjs +skip-if = toolkit == 'android' # no https-only errorpage support in android diff --git a/dom/security/test/https-only/test_break_endless_upgrade_downgrade_loop.html b/dom/security/test/https-only/test_break_endless_upgrade_downgrade_loop.html new file mode 100644 index 0000000000..847a71f378 --- /dev/null +++ b/dom/security/test/https-only/test_break_endless_upgrade_downgrade_loop.html @@ -0,0 +1,89 @@ + + + +Bug 1691888: Break endless upgrade downgrade loops when using https-only + + + + + + + + diff --git a/dom/security/test/https-only/test_fragment.html b/dom/security/test/https-only/test_fragment.html new file mode 100644 index 0000000000..52a63764fb --- /dev/null +++ b/dom/security/test/https-only/test_fragment.html @@ -0,0 +1,72 @@ + + + +Bug 1694932: Https-only mode reloads the page in certain cases when there should be just a fragment navigation + + + + + + + + diff --git a/dom/security/test/https-only/test_http_background_auth_request.html b/dom/security/test/https-only/test_http_background_auth_request.html new file mode 100644 index 0000000000..7fc800c388 --- /dev/null +++ b/dom/security/test/https-only/test_http_background_auth_request.html @@ -0,0 +1,113 @@ + + + + Bug 1665062 - HTTPS-Only: Do not cancel channel if auth is in progress + + + + + + + + diff --git a/dom/security/test/https-only/test_http_background_request.html b/dom/security/test/https-only/test_http_background_request.html new file mode 100644 index 0000000000..5dff0a5fdb --- /dev/null +++ b/dom/security/test/https-only/test_http_background_request.html @@ -0,0 +1,125 @@ + + + + Bug 1663396: Test HTTPS-Only-Mode top-level background request not leaking sensitive info + + + + + + + + diff --git a/dom/security/test/https-only/test_insecure_reload.html b/dom/security/test/https-only/test_insecure_reload.html new file mode 100644 index 0000000000..d143c9080b --- /dev/null +++ b/dom/security/test/https-only/test_insecure_reload.html @@ -0,0 +1,74 @@ + + + +Bug 1702001: Https-only mode does not reload pages after clicking "Continue to HTTP Site", when url contains navigation + + + + + + + + diff --git a/dom/security/test/https-only/test_redirect_upgrade.html b/dom/security/test/https-only/test_redirect_upgrade.html new file mode 100644 index 0000000000..59f02f96d0 --- /dev/null +++ b/dom/security/test/https-only/test_redirect_upgrade.html @@ -0,0 +1,58 @@ + + + + + + HTTPS-Only Mode - XHR Redirect Upgrade + + + + + +

HTTPS-Only Mode

+

Upgrade Test for insecure XHR redirects.

+ Bug 1613063 + + + + diff --git a/dom/security/test/https-only/test_resource_upgrade.html b/dom/security/test/https-only/test_resource_upgrade.html new file mode 100644 index 0000000000..a5fb9b6cad --- /dev/null +++ b/dom/security/test/https-only/test_resource_upgrade.html @@ -0,0 +1,122 @@ + + + + + + HTTPS-Only Mode - Resource Upgrade + + + + + + +

HTTPS-Only Mode

+

Upgrade Test for various resources

+ Bug 1613063 + + + + + + diff --git a/dom/security/test/https-only/test_user_suggestion_box.html b/dom/security/test/https-only/test_user_suggestion_box.html new file mode 100644 index 0000000000..1feabcd003 --- /dev/null +++ b/dom/security/test/https-only/test_user_suggestion_box.html @@ -0,0 +1,81 @@ + + + + Bug 1665057 - Add www button on https-only error page + + + + + + + -- cgit v1.2.3