From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- .../extensions/test/xpcshell/test_ext_secfetch.js | 352 +++++++++++++++++++++ 1 file changed, 352 insertions(+) create mode 100644 toolkit/components/extensions/test/xpcshell/test_ext_secfetch.js (limited to 'toolkit/components/extensions/test/xpcshell/test_ext_secfetch.js') diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_secfetch.js b/toolkit/components/extensions/test/xpcshell/test_ext_secfetch.js new file mode 100644 index 0000000000..e8b3dcfca8 --- /dev/null +++ b/toolkit/components/extensions/test/xpcshell/test_ext_secfetch.js @@ -0,0 +1,352 @@ +/* -*- 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); + +const server = createHttpServer({ + // We need the 127.0.0.1 proxy because the sec-fetch headers are not sent to + // "127.0.0.1:". + hosts: ["127.0.0.1", "127.0.0.2"], +}); + +server.registerPathHandler("/page.html", (request, response) => { + response.setStatusLine(request.httpVersion, 200, "OK"); + response.setHeader("Access-Control-Allow-Origin", "*"); +}); + +server.registerPathHandler("/return_headers", (request, response) => { + response.setStatusLine(request.httpVersion, 200, "OK"); + response.setHeader("Content-Type", "text/plain"); + response.setHeader("Access-Control-Allow-Origin", "*"); + if (request.method === "OPTIONS") { + // Handle CORS preflight request. + response.setHeader("Access-Control-Allow-Methods", "GET, PUT"); + return; + } + + let headers = {}; + for (let header of [ + "sec-fetch-site", + "sec-fetch-dest", + "sec-fetch-mode", + "sec-fetch-user", + ]) { + if (request.hasHeader(header)) { + headers[header] = request.getHeader(header); + } + } + + if (request.hasHeader("origin")) { + headers.origin = request + .getHeader("origin") + .replace(/moz-extension:\/\/[^\/]+/, "moz-extension://"); + } + + response.write(JSON.stringify(headers)); +}); + +async function contentScript() { + let content_fetch; + if (browser.runtime.getManifest().manifest_version === 2) { + content_fetch = content.fetch; + } else { + // In MV3, there is no content variable. + browser.test.assertEq(typeof content, "undefined", "no .content in MV3"); + // In MV3, window.fetch is the original fetch with the page's principal. + content_fetch = window.fetch.bind(window); + } + let results = await Promise.allSettled([ + // A cross-origin request from the content script. + fetch("http://127.0.0.1/return_headers").then(res => res.json()), + // A cross-origin request that behaves as if it was sent by the content it + // self. + content_fetch("http://127.0.0.1/return_headers").then(res => res.json()), + // A same-origin request that behaves as if it was sent by the content it + // self. + content_fetch("http://127.0.0.2/return_headers").then(res => res.json()), + // A same-origin request from the content script. + fetch("http://127.0.0.2/return_headers").then(res => res.json()), + // Non GET or HEAD request, triggers CORS preflight. + fetch("http://127.0.0.2/return_headers", { method: "PUT" }).then(res => + res.json() + ), + ]); + + results = results.map(({ value, reason }) => value ?? reason.message); + + browser.test.sendMessage("content_results", results); +} + +async function runSecFetchTest(test) { + let data = { + async background() { + let site = await new Promise(resolve => { + browser.test.onMessage.addListener(msg => { + resolve(msg); + }); + }); + + let results = await Promise.all([ + fetch(`${site}/return_headers`).then(res => res.json()), + // Non GET or HEAD request, triggers CORS preflight. + fetch(`${site}/return_headers`, { method: "PUT" }).then(res => + res.json() + ), + ]); + browser.test.sendMessage("background_results", results); + }, + manifest: { + manifest_version: test.manifest_version, + content_scripts: [ + { + matches: ["http://127.0.0.2/*"], + js: ["content_script.js"], + }, + ], + }, + files: { + "content_script.js": contentScript, + }, + }; + + if (data.manifest.manifest_version == 3) { + // Automatically grant permissions so that the content script can run. + data.manifest.granted_host_permissions = true; + // Needed to use granted_host_permissions in tests: + data.temporarilyInstalled = true; + // Work-around for bug 1766752: + data.manifest.host_permissions = ["http://127.0.0.2/*"]; + // (note: ^ host_permissions may be replaced/extended below). + } + + // The sec-fetch-* headers are only send to potentially trust worthy origins. + // We use 127.0.0.1 to avoid setting up an https server. + const site = "http://127.0.0.1"; + + if (test.permission) { + // MV3 requires permissions to be set in permissions. ExtensionTestCommon + // will replace host_permissions with permissions in MV2. + data.manifest.host_permissions = ["http://127.0.0.2/*", `${site}/*`]; + } + + let extension = ExtensionTestUtils.loadExtension(data); + await extension.startup(); + + extension.sendMessage(site); + let backgroundResults = await extension.awaitMessage("background_results"); + Assert.deepEqual(backgroundResults, test.expectedBackgroundHeaders); + + let contentPage = await ExtensionTestUtils.loadContentPage( + `http://127.0.0.2/page.html` + ); + let contentResults = await extension.awaitMessage("content_results"); + Assert.deepEqual(contentResults, test.expectedContentHeaders); + await contentPage.close(); + + await extension.unload(); +} + +add_task(async function test_fetch_without_permissions_mv2() { + await runSecFetchTest({ + manifest_version: 2, + permission: false, + expectedBackgroundHeaders: [ + { + "sec-fetch-site": "cross-site", + "sec-fetch-mode": "cors", + "sec-fetch-dest": "empty", + origin: "moz-extension://", + }, + { + "sec-fetch-site": "cross-site", + "sec-fetch-mode": "cors", + "sec-fetch-dest": "empty", + origin: "moz-extension://", + }, + ], + expectedContentHeaders: [ + // TODO bug 1605197: Support cors without permissions in MV2. + "NetworkError when attempting to fetch resource.", + // Expectation: + // { + // "sec-fetch-site": "cross-site", + // "sec-fetch-mode": "cors", + // "sec-fetch-dest": "empty", + // }, + { + "sec-fetch-site": "cross-site", + "sec-fetch-mode": "cors", + "sec-fetch-dest": "empty", + origin: "http://127.0.0.2", + }, + { + "sec-fetch-site": "same-origin", + "sec-fetch-mode": "cors", + "sec-fetch-dest": "empty", + }, + { + "sec-fetch-site": "same-origin", + "sec-fetch-mode": "cors", + "sec-fetch-dest": "empty", + }, + { + "sec-fetch-site": "same-origin", + "sec-fetch-mode": "cors", + "sec-fetch-dest": "empty", + }, + ], + }); +}); + +add_task(async function test_fetch_with_permissions_mv2() { + await runSecFetchTest({ + manifest_version: 2, + permission: true, + expectedBackgroundHeaders: [ + { + "sec-fetch-site": "same-origin", + "sec-fetch-mode": "cors", + "sec-fetch-dest": "empty", + }, + { + "sec-fetch-site": "same-origin", + "sec-fetch-mode": "cors", + "sec-fetch-dest": "empty", + origin: "moz-extension://", + }, + ], + expectedContentHeaders: [ + { + "sec-fetch-site": "cross-site", + "sec-fetch-mode": "cors", + "sec-fetch-dest": "empty", + }, + { + "sec-fetch-site": "cross-site", + "sec-fetch-mode": "cors", + "sec-fetch-dest": "empty", + origin: "http://127.0.0.2", + }, + { + "sec-fetch-site": "same-origin", + "sec-fetch-mode": "cors", + "sec-fetch-dest": "empty", + }, + { + "sec-fetch-site": "same-origin", + "sec-fetch-mode": "cors", + "sec-fetch-dest": "empty", + }, + { + "sec-fetch-site": "same-origin", + "sec-fetch-mode": "cors", + "sec-fetch-dest": "empty", + }, + ], + }); +}); + +add_task(async function test_fetch_without_permissions_mv3() { + await runSecFetchTest({ + manifest_version: 3, + permission: false, + expectedBackgroundHeaders: [ + // Same as in test_fetch_without_permissions_mv2. + { + "sec-fetch-site": "cross-site", + "sec-fetch-mode": "cors", + "sec-fetch-dest": "empty", + origin: "moz-extension://", + }, + { + "sec-fetch-site": "cross-site", + "sec-fetch-mode": "cors", + "sec-fetch-dest": "empty", + origin: "moz-extension://", + }, + ], + expectedContentHeaders: [ + { + "sec-fetch-site": "cross-site", + "sec-fetch-mode": "cors", + "sec-fetch-dest": "empty", + origin: "http://127.0.0.2", + }, + { + "sec-fetch-site": "cross-site", + "sec-fetch-mode": "cors", + "sec-fetch-dest": "empty", + origin: "http://127.0.0.2", + }, + { + "sec-fetch-site": "same-origin", + "sec-fetch-mode": "cors", + "sec-fetch-dest": "empty", + }, + { + "sec-fetch-site": "same-origin", + "sec-fetch-mode": "cors", + "sec-fetch-dest": "empty", + }, + { + "sec-fetch-site": "same-origin", + "sec-fetch-mode": "cors", + "sec-fetch-dest": "empty", + origin: "http://127.0.0.2", + }, + ], + }); +}); + +add_task(async function test_fetch_with_permissions_mv3() { + await runSecFetchTest({ + manifest_version: 3, + permission: true, + expectedBackgroundHeaders: [ + { + // Same as in test_fetch_with_permissions_mv2. + "sec-fetch-site": "same-origin", + "sec-fetch-mode": "cors", + "sec-fetch-dest": "empty", + }, + { + "sec-fetch-site": "same-origin", + "sec-fetch-mode": "cors", + "sec-fetch-dest": "empty", + origin: "moz-extension://", + }, + ], + expectedContentHeaders: [ + // All expectations the same as in test_fetch_without_permissions_mv3. + { + "sec-fetch-site": "cross-site", + "sec-fetch-mode": "cors", + "sec-fetch-dest": "empty", + origin: "http://127.0.0.2", + }, + { + "sec-fetch-site": "cross-site", + "sec-fetch-mode": "cors", + "sec-fetch-dest": "empty", + origin: "http://127.0.0.2", + }, + { + "sec-fetch-site": "same-origin", + "sec-fetch-mode": "cors", + "sec-fetch-dest": "empty", + }, + { + "sec-fetch-site": "same-origin", + "sec-fetch-mode": "cors", + "sec-fetch-dest": "empty", + }, + { + "sec-fetch-site": "same-origin", + "sec-fetch-mode": "cors", + "sec-fetch-dest": "empty", + origin: "http://127.0.0.2", + }, + ], + }); +}); -- cgit v1.2.3