diff options
Diffstat (limited to 'toolkit/components/extensions/test/xpcshell/test_ext_webRequest_restrictedHeaders.js')
-rw-r--r-- | toolkit/components/extensions/test/xpcshell/test_ext_webRequest_restrictedHeaders.js | 252 |
1 files changed, 252 insertions, 0 deletions
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_webRequest_restrictedHeaders.js b/toolkit/components/extensions/test/xpcshell/test_ext_webRequest_restrictedHeaders.js new file mode 100644 index 0000000000..471bae0493 --- /dev/null +++ b/toolkit/components/extensions/test/xpcshell/test_ext_webRequest_restrictedHeaders.js @@ -0,0 +1,252 @@ +/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set sts=2 sw=2 et tw=80: */ +"use strict"; + +Services.prefs.setBoolPref("extensions.manifestV3.enabled", true); + +AddonTestUtils.init(this); +AddonTestUtils.createAppInfo( + "xpcshell@tests.mozilla.org", + "XPCShell", + "1", + "43" +); + +const server = createHttpServer({ + hosts: ["example.net"], +}); +server.registerPathHandler("/test/response-header", (req, res) => { + let headerName; + let headerValue; + if (req.queryString) { + let params = new URLSearchParams(req.queryString); + headerName = params.get("name"); + headerValue = params.get("value"); + res.setHeader(headerName, headerValue, false); + res.setHeader("test", `${headerName}=${headerValue}`, false); + } + res.write(""); +}); + +const extensionData = { + useAddonManager: "temporary", + background() { + const { manifest_version } = browser.runtime.getManifest(); + let headerToSet = undefined; + browser.test.onMessage.addListener(function (msg, arg) { + if (msg !== "header-to-set") { + return; + } + headerToSet = arg; + browser.test.sendMessage("header-to-set:done"); + }); + browser.webRequest.onHeadersReceived.addListener( + e => { + browser.test.log(`onHeadersReceived ${e.requestId} ${e.url}`); + if (headerToSet === undefined) { + browser.test.fail( + "extension called before headerToSet option was set" + ); + } + if (typeof headerToSet?.name == "string") { + const existingHeader = e.responseHeaders.filter( + i => i.name.toLowerCase() === headerToSet.name + )[0]; + e.responseHeaders = e.responseHeaders.filter( + i => i.name.toLowerCase() != headerToSet.name + ); + // Omit the header if the value isn't set, change the header otherwise. + if (headerToSet.value != null) { + e.responseHeaders.push({ + name: headerToSet.name, + value: headerToSet.value, + }); + } + browser.test.log( + `Test Extension MV${manifest_version} (${browser.runtime.id}) sets responseHeader: "${headerToSet.name}"="${headerToSet.value}" (was originally set to "${existingHeader?.value})"` + ); + } + return { responseHeaders: e.responseHeaders }; + }, + { urls: ["*://example.net/test/*"] }, + ["blocking", "responseHeaders"] + ); + browser.webRequest.onCompleted.addListener( + e => { + browser.test.log(`onCompletedReceived ${e.requestId} ${e.url}`); + const responseHeaders = e.responseHeaders.filter( + i => i.name.toLowerCase() === headerToSet.name + ); + + browser.test.sendMessage( + "on-completed:response-headers", + responseHeaders + ); + }, + { urls: ["*://example.net/test/*"] }, + ["responseHeaders"] + ); + browser.test.sendMessage("bgpage:ready"); + }, +}; + +const extDataMV2 = { + ...extensionData, + manifest: { + manifest_version: 2, + permissions: ["webRequest", "webRequestBlocking", "*://example.net/test/*"], + }, +}; + +const extDataMV3 = { + ...extensionData, + manifest: { + manifest_version: 3, + permissions: ["webRequest", "webRequestBlocking"], + host_permissions: ["*://example.net/test/*"], + granted_host_permissions: true, + }, +}; + +add_setup(async () => { + await AddonTestUtils.promiseStartupManager(); +}); + +async function test_restricted_response_headers_changes({ + firstExtData, + secondExtData, + headerName, + firstExtHeaderChange, + secondExtHeaderChange, + siteHeaderValue, + expectedHeaderValue, +}) { + const ext1 = ExtensionTestUtils.loadExtension(firstExtData); + const ext2 = secondExtData && ExtensionTestUtils.loadExtension(secondExtData); + + await ext1.startup(); + await ext1.awaitMessage("bgpage:ready"); + + await ext2?.startup(); + await ext2?.awaitMessage("bgpage:ready"); + + ext1.sendMessage("header-to-set", { + name: headerName, + value: firstExtHeaderChange, + }); + await ext1.awaitMessage("header-to-set:done"); + ext2?.sendMessage("header-to-set", { + name: headerName, + value: secondExtHeaderChange, + }); + await ext2?.awaitMessage("header-to-set:done"); + + if (siteHeaderValue) { + await ExtensionTestUtils.fetch( + "http://example.net/", + `http://example.net/test/response-header?name=${headerName}&value=${siteHeaderValue}` + ); + } else { + await ExtensionTestUtils.fetch( + "http://example.net/", + "http://example.net/test/response-header" + ); + } + + const [finalSiteHeaders] = await Promise.all([ + ext1.awaitMessage("on-completed:response-headers"), + ext2?.awaitMessage("on-completed:response-headers"), + ]); + + Assert.deepEqual( + finalSiteHeaders, + expectedHeaderValue + ? [{ name: headerName, value: expectedHeaderValue }] + : [], + "Got the expected response header" + ); + + await ext1.unload(); + await ext2?.unload(); +} + +add_task(async function test_changes_to_restricted_response_headers() { + const testCases = [ + { + headerName: "cross-origin-embedder-policy", + siteHeaderValue: "require-corp", + firstExtHeaderChange: "credentialless", + secondExtHeaderChange: "unsafe-none", + }, + { + headerName: "cross-origin-opener-policy", + siteHeaderValue: "same-origin", + firstExtHeaderChange: "same-origin-allow-popups", + secondExtHeaderChange: "unsafe-none", + }, + { + headerName: "cross-origin-resource-policy", + siteHeaderValue: "same-origin", + firstExtHeaderChange: "same-site", + secondExtHeaderChange: "cross-origin", + }, + { + headerName: "x-frame-options", + siteHeaderValue: "deny", + firstExtHeaderChange: "sameorigin", + secondExtHeaderChange: "allow-from=http://example.com", + }, + { + headerName: "access-control-allow-credentials", + siteHeaderValue: "true", + firstExtHeaderChange: "false", + secondExtHeaderChange: "false", + }, + { + headerName: "access-control-allow-methods", + siteHeaderValue: "*", + firstExtHeaderChange: "", + secondExtHeaderChange: "GET", + }, + ]; + + for (const testCase of testCases) { + info( + `Test MV3 extension disallowed to change restricted header if already set by the website: "${testCase.headerName}"="${testCase.siteHeaderValue}` + ); + await test_restricted_response_headers_changes({ + ...testCase, + firstExtData: extDataMV3, + // Expect the value set by the server to be preserved. + expectedHeaderValue: testCase.siteHeaderValue, + }); + } + + for (const testCase of testCases) { + info( + `Test MV3 extension disallowed to change restricted header also if not set by the website: "${testCase.headerName}` + ); + await test_restricted_response_headers_changes({ + ...testCase, + siteHeaderValue: null, + firstExtData: extDataMV3, + // Expect the value set by the server to be preserved. + expectedHeaderValue: null, + }); + } + + for (const testCase of testCases) { + info( + `Test MV2 extension allowed to change restricted header if already set by the website: ${JSON.stringify( + testCase.siteHeader + )}` + ); + await test_restricted_response_headers_changes({ + ...testCase, + firstExtData: extDataMV3, + secondExtData: extDataMV2, + // Expect the value set by the server to be preserved. + expectedHeaderValue: testCase.secondExtHeaderChange, + }); + } +}); |