mirror of
				https://github.com/mozilla/gecko-dev.git
				synced 2025-11-04 02:09:05 +02:00 
			
		
		
		
	
		
			
				
	
	
		
			359 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			359 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
/* -*- 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);
 | 
						|
 | 
						|
// We don't normally allow localhost channels to be proxied, but this
 | 
						|
// is easier than updating all the certs and/or domains.
 | 
						|
Services.prefs.setBoolPref("network.proxy.allow_hijacking_localhost", true);
 | 
						|
registerCleanupFunction(() => {
 | 
						|
  Services.prefs.clearUserPref("network.proxy.allow_hijacking_localhost");
 | 
						|
});
 | 
						|
 | 
						|
const server = createHttpServer({
 | 
						|
  // We need the 127.0.0.1 proxy because the sec-fetch headers are not sent to
 | 
						|
  // "127.0.0.1:<any port other than 80 or 443>".
 | 
						|
  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://<placeholder>");
 | 
						|
  }
 | 
						|
 | 
						|
  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://<placeholder>",
 | 
						|
      },
 | 
						|
      {
 | 
						|
        "sec-fetch-site": "cross-site",
 | 
						|
        "sec-fetch-mode": "cors",
 | 
						|
        "sec-fetch-dest": "empty",
 | 
						|
        origin: "moz-extension://<placeholder>",
 | 
						|
      },
 | 
						|
    ],
 | 
						|
    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://<placeholder>",
 | 
						|
      },
 | 
						|
    ],
 | 
						|
    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://<placeholder>",
 | 
						|
      },
 | 
						|
      {
 | 
						|
        "sec-fetch-site": "cross-site",
 | 
						|
        "sec-fetch-mode": "cors",
 | 
						|
        "sec-fetch-dest": "empty",
 | 
						|
        origin: "moz-extension://<placeholder>",
 | 
						|
      },
 | 
						|
    ],
 | 
						|
    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://<placeholder>",
 | 
						|
      },
 | 
						|
    ],
 | 
						|
    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",
 | 
						|
      },
 | 
						|
    ],
 | 
						|
  });
 | 
						|
});
 |