diff options
Diffstat (limited to '')
-rw-r--r-- | toolkit/components/extensions/test/mochitest/test_ext_webrequest_hsts.html | 252 |
1 files changed, 252 insertions, 0 deletions
diff --git a/toolkit/components/extensions/test/mochitest/test_ext_webrequest_hsts.html b/toolkit/components/extensions/test/mochitest/test_ext_webrequest_hsts.html new file mode 100644 index 0000000000..e66b5c471a --- /dev/null +++ b/toolkit/components/extensions/test/mochitest/test_ext_webrequest_hsts.html @@ -0,0 +1,252 @@ +<!DOCTYPE HTML> + +<html> +<head> +<meta charset="utf-8"> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <script src="/tests/SimpleTest/ExtensionTestUtils.js"></script> + <script type="text/javascript" src="head.js"></script> + <script type="text/javascript" src="head_webrequest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +<script> +"use strict"; + +function getExtension() { + async function background() { + let expect; + let urls = ["*://*.example.org/tests/*"]; + browser.webRequest.onBeforeRequest.addListener(details => { + browser.test.assertEq(expect.shift(), "onBeforeRequest"); + }, {urls}, ["blocking"]); + browser.webRequest.onBeforeSendHeaders.addListener(details => { + browser.test.assertEq(expect.shift(), "onBeforeSendHeaders"); + }, {urls}, ["blocking", "requestHeaders"]); + browser.webRequest.onSendHeaders.addListener(details => { + browser.test.assertEq(expect.shift(), "onSendHeaders"); + }, {urls}, ["requestHeaders"]); + + async function testSecurityInfo(securityInfo, options) { + if (options.certificateChain) { + // Some of the tests here only produce a single cert in the chain. + browser.test.assertTrue(securityInfo.certificates.length >= 1, "have certificate chain"); + } else { + browser.test.assertTrue(securityInfo.certificates.length == 1, "no certificate chain"); + } + let cert = securityInfo.certificates[0]; + let now = Date.now(); + browser.test.assertTrue(Number.isInteger(cert.validity.start), "cert start is integer"); + browser.test.assertTrue(Number.isInteger(cert.validity.end), "cert end is integer"); + browser.test.assertTrue(cert.validity.start < now, "cert start validity is correct"); + browser.test.assertTrue(now < cert.validity.end, "cert end validity is correct"); + if (options.rawDER) { + for (let cert of securityInfo.certificates) { + browser.test.assertTrue(!!cert.rawDER.length, "have rawDER"); + } + } + } + + function stripQuery(url) { + // In this whole test we are not interested in the query part of the URL. + // Most tests include a cache buster (bustcache) param in the URL. + return url.split("?")[0]; + } + + browser.webRequest.onHeadersReceived.addListener(async (details) => { + browser.test.assertEq(expect.shift(), "onHeadersReceived"); + + // We expect all requests to have been upgraded at this point. + browser.test.assertTrue(details.url.startsWith("https"), "connection is https"); + let securityInfo = await browser.webRequest.getSecurityInfo(details.requestId, {}); + browser.test.assertTrue(securityInfo && securityInfo.state == "secure", + "security info reflects https"); + await testSecurityInfo(securityInfo, {}); + securityInfo = await browser.webRequest.getSecurityInfo(details.requestId, {certificateChain: true}); + await testSecurityInfo(securityInfo, {certificateChain: true}); + securityInfo = await browser.webRequest.getSecurityInfo(details.requestId, {rawDER: true}); + await testSecurityInfo(securityInfo, {rawDER: true}); + securityInfo = await browser.webRequest.getSecurityInfo(details.requestId, {certificateChain: true, rawDER: true}); + await testSecurityInfo(securityInfo, {certificateChain: true, rawDER: true}); + + browser.test.sendMessage("hsts", securityInfo.hsts); + let headers = details.responseHeaders || []; + for (let header of headers) { + if (header.name.toLowerCase() === "strict-transport-security") { + return; + } + } + if (details.url.includes("addHsts")) { + headers.push({ + name: "Strict-Transport-Security", + value: "max-age=31536000000", + }); + } + return {responseHeaders: headers}; + }, {urls}, ["blocking", "responseHeaders"]); + browser.webRequest.onBeforeRedirect.addListener(details => { + browser.test.assertEq(expect.shift(), "onBeforeRedirect"); + }, {urls}); + browser.webRequest.onResponseStarted.addListener(details => { + browser.test.assertEq(expect.shift(), "onResponseStarted"); + }, {urls}); + browser.webRequest.onCompleted.addListener(details => { + browser.test.assertEq(expect.shift(), "onCompleted"); + browser.test.sendMessage("onCompleted", stripQuery(details.url)); + }, {urls}); + browser.webRequest.onErrorOccurred.addListener(details => { + browser.test.notifyFail(`onErrorOccurred ${JSON.stringify(details)}`); + }, {urls}); + + async function onUpdated(tabId, tabInfo, tab) { + if (tabInfo.status !== "complete" || tab.url === "about:blank") { + return; + } + browser.tabs.remove(tabId); + browser.tabs.onUpdated.removeListener(onUpdated); + browser.test.sendMessage("tabs-done", stripQuery(tab.url)); + } + browser.test.onMessage.addListener((url, expected) => { + expect = expected; + browser.tabs.onUpdated.addListener(onUpdated); + browser.tabs.create({url}); + }); + } + + let manifest = { + "permissions": [ + "tabs", + "webRequest", + "webRequestBlocking", + "<all_urls>", + ], + }; + return ExtensionTestUtils.loadExtension({ + manifest, + background, + }); +} + +add_setup(async () => { + // In bug 1605515, we repeatedly saw a missing onHeadersReceived event, + // possibly related to bug 1595610. As a workaround, clear the cache. + await SpecialPowers.spawnChrome([], async () => { + Services.cache2.clear(); + await new Promise(resolve => { + Services.clearData.deleteData(Ci.nsIClearDataService.CLEAR_HSTS, resolve); + }); + }); +}); + +// This test makes a request against a server that redirects with a 302. +add_task(async function test_hsts_request() { + const testPath = "example.org/tests/toolkit/components/extensions/test/mochitest"; + + let extension = getExtension(); + await extension.startup(); + + // simple redirect + let sample = "https://example.org/tests/toolkit/components/extensions/test/mochitest/file_sample.html"; + extension.sendMessage( + `https://${testPath}/redirect_auto.sjs?redirect_uri=${sample}?bustcache1=${Math.random()}`, + ["onBeforeRequest", "onBeforeSendHeaders", "onSendHeaders", + "onHeadersReceived", "onBeforeRedirect", "onBeforeRequest", + "onBeforeSendHeaders", "onSendHeaders", "onHeadersReceived", + "onResponseStarted", "onCompleted"]); + is(await extension.awaitMessage("hsts"), false, "First request to this host, not receiving a hsts header"); + is(await extension.awaitMessage("hsts"), false, "second (redirected) reqiest to the same host, still no knowledge about the hosts hsts preference"); + // Note: stripQuery strips query string added by redirect_auto. + is(await extension.awaitMessage("tabs-done"), sample, "redirection ok"); + is(await extension.awaitMessage("onCompleted"), sample, "redirection ok"); + + // priming hsts + extension.sendMessage( + `https://${testPath}/hsts.sjs`, + ["onBeforeRequest", "onBeforeSendHeaders", "onSendHeaders", + "onHeadersReceived", "onResponseStarted", "onCompleted"]); + is(await extension.awaitMessage("hsts"), false, "First request to this host, receiving hsts header and saving the hosts STS preference for the next request"); + is(await extension.awaitMessage("tabs-done"), + "https://example.org/tests/toolkit/components/extensions/test/mochitest/hsts.sjs", + "hsts primed"); + is(await extension.awaitMessage("onCompleted"), + "https://example.org/tests/toolkit/components/extensions/test/mochitest/hsts.sjs"); + + // test upgrade + extension.sendMessage( + `http://${testPath}/hsts.sjs`, + ["onBeforeRequest", "onBeforeRedirect", "onBeforeRequest", + "onBeforeSendHeaders", "onSendHeaders", "onHeadersReceived", + "onResponseStarted", "onCompleted"]); + is(await extension.awaitMessage("hsts"), true, "second (redirected) reqiest to the same host, we know about the hsts status of the host this time"); + is(await extension.awaitMessage("tabs-done"), + "https://example.org/tests/toolkit/components/extensions/test/mochitest/hsts.sjs", + "hsts upgraded"); + is(await extension.awaitMessage("onCompleted"), + "https://example.org/tests/toolkit/components/extensions/test/mochitest/hsts.sjs"); + + await extension.unload(); +}); + +// This test makes a priming request and adds the STS header, then tests the upgrade. +add_task(async function test_hsts_header() { + const testPath = "test1.example.org/tests/toolkit/components/extensions/test/mochitest"; + + let extension = getExtension(); + await extension.startup(); + + // priming hsts, this time there is no STS header, onHeadersReceived adds it. + let completed = extension.awaitMessage("onCompleted"); + let tabdone = extension.awaitMessage("tabs-done"); + extension.sendMessage( + `https://${testPath}/file_sample.html?bustcache2=${Math.random()}&addHsts=true`, + ["onBeforeRequest", "onBeforeSendHeaders", "onSendHeaders", + "onHeadersReceived", "onResponseStarted", "onCompleted"]); + is(await extension.awaitMessage("hsts"), false, "First reqeuest to this host, we don't know about the hosts STS setting yet"); + is(await tabdone, `https://${testPath}/file_sample.html`, "priming request done"); + is(await completed, `https://${testPath}/file_sample.html`, "priming request done"); + + // test upgrade from http to https due to onHeadersReceived adding STS header + completed = extension.awaitMessage("onCompleted"); + tabdone = extension.awaitMessage("tabs-done"); + extension.sendMessage( + `http://${testPath}/file_sample.html?bustcache3=${Math.random()}`, + ["onBeforeRequest", "onBeforeRedirect", "onBeforeRequest", + "onBeforeSendHeaders", "onSendHeaders", "onHeadersReceived", + "onResponseStarted", "onCompleted"]); + is(await extension.awaitMessage("hsts"), true, "We have received an hsts header last request via oneadersReceived"); + is(await tabdone, `https://${testPath}/file_sample.html`, "hsts upgraded"); + is(await completed, `https://${testPath}/file_sample.html`, "request upgraded"); + + await extension.unload(); +}); + +add_task(async function test_nonBlocking_securityInfo() { + let extension = ExtensionTestUtils.loadExtension({ + manifest: { + "permissions": [ + "webRequest", + "<all_urls>", + ], + }, + async background() { + let tab; + browser.webRequest.onHeadersReceived.addListener(async (details) => { + let securityInfo = await browser.webRequest.getSecurityInfo(details.requestId, {}); + browser.test.assertTrue(!securityInfo, "securityInfo undefined on http request"); + browser.tabs.remove(tab.id); + browser.test.notifyPass("success"); + }, {urls: ["<all_urls>"], types: ["main_frame"]}); + tab = await browser.tabs.create({ + url: `https://example.org/tests/toolkit/components/extensions/test/mochitest/file_sample.html?bustcache4=${Math.random()}`, + }); + }, + }); + await extension.startup(); + + await extension.awaitFinish("success"); + await extension.unload(); +}); +</script> +</head> +<body> + +</body> +</html> |