forked from mirrors/gecko-dev
296 lines
9.7 KiB
JavaScript
296 lines
9.7 KiB
JavaScript
"use strict";
|
|
|
|
add_setup(() => {
|
|
Services.prefs.setBoolPref("extensions.manifestV3.enabled", true);
|
|
Services.prefs.setBoolPref("extensions.dnr.enabled", true);
|
|
});
|
|
|
|
const server = createHttpServer({
|
|
hosts: ["example.com", "redir"],
|
|
});
|
|
server.registerPathHandler("/never_reached", (req, res) => {
|
|
Assert.ok(false, "Server should never have been reached");
|
|
});
|
|
server.registerPathHandler("/source", (req, res) => {
|
|
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
});
|
|
server.registerPathHandler("/destination", (req, res) => {
|
|
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
});
|
|
|
|
add_task(async function block_request_with_dnr() {
|
|
async function background() {
|
|
let onBeforeRequestPromise = new Promise(resolve => {
|
|
browser.webRequest.onBeforeRequest.addListener(resolve, {
|
|
urls: ["*://example.com/*"],
|
|
});
|
|
});
|
|
await browser.declarativeNetRequest.updateSessionRules({
|
|
addRules: [
|
|
{
|
|
id: 1,
|
|
condition: { requestDomains: ["example.com"] },
|
|
action: { type: "block" },
|
|
},
|
|
],
|
|
});
|
|
|
|
await browser.test.assertRejects(
|
|
fetch("http://example.com/never_reached"),
|
|
"NetworkError when attempting to fetch resource.",
|
|
"blocked by DNR rule"
|
|
);
|
|
// DNR is documented to take precedence over webRequest. We should still
|
|
// receive the webRequest event, however.
|
|
browser.test.log("Waiting for webRequest.onBeforeRequest...");
|
|
await onBeforeRequestPromise;
|
|
browser.test.log("Seen webRequest.onBeforeRequest!");
|
|
|
|
browser.test.notifyPass();
|
|
}
|
|
let extension = ExtensionTestUtils.loadExtension({
|
|
background,
|
|
temporarilyInstalled: true, // Needed for granted_host_permissions
|
|
allowInsecureRequests: true,
|
|
manifest: {
|
|
manifest_version: 3,
|
|
granted_host_permissions: true,
|
|
host_permissions: ["*://example.com/*"],
|
|
permissions: ["declarativeNetRequest", "webRequest"],
|
|
},
|
|
});
|
|
await extension.startup();
|
|
await extension.awaitFinish();
|
|
await extension.unload();
|
|
});
|
|
|
|
add_task(async function upgradeScheme_and_redirect_request_with_dnr() {
|
|
async function background() {
|
|
let onBeforeRequestSeen = [];
|
|
browser.webRequest.onBeforeRequest.addListener(
|
|
d => {
|
|
onBeforeRequestSeen.push(d.url);
|
|
// webRequest cancels, but DNR should actually be taking precedence.
|
|
return { cancel: true };
|
|
},
|
|
{ urls: ["*://example.com/*", "http://redir/here"] },
|
|
["blocking"]
|
|
);
|
|
await browser.declarativeNetRequest.updateSessionRules({
|
|
addRules: [
|
|
{
|
|
id: 1,
|
|
condition: { requestDomains: ["example.com"] },
|
|
action: { type: "upgradeScheme" },
|
|
},
|
|
{
|
|
id: 2,
|
|
condition: { requestDomains: ["example.com"], urlFilter: "|https:*" },
|
|
action: { type: "redirect", redirect: { url: "http://redir/here" } },
|
|
// The upgradeScheme and redirect actions have equal precedence. To
|
|
// make sure that the redirect action is executed when both rules
|
|
// match, we assign a higher priority to the redirect action.
|
|
priority: 2,
|
|
},
|
|
],
|
|
});
|
|
|
|
await browser.test.assertRejects(
|
|
fetch("http://example.com/never_reached"),
|
|
"NetworkError when attempting to fetch resource.",
|
|
"although initially redirected by DNR, ultimately blocked by webRequest"
|
|
);
|
|
// DNR is documented to take precedence over webRequest.
|
|
// So we should actually see redirects according to the DNR rules, and
|
|
// the webRequest listener should still be able to observe all requests.
|
|
browser.test.assertDeepEq(
|
|
[
|
|
"http://example.com/never_reached",
|
|
"https://example.com/never_reached",
|
|
"http://redir/here",
|
|
],
|
|
onBeforeRequestSeen,
|
|
"Expected onBeforeRequest events"
|
|
);
|
|
|
|
browser.test.notifyPass();
|
|
}
|
|
let extension = ExtensionTestUtils.loadExtension({
|
|
background,
|
|
temporarilyInstalled: true, // Needed for granted_host_permissions
|
|
manifest: {
|
|
manifest_version: 3,
|
|
granted_host_permissions: true,
|
|
host_permissions: ["*://example.com/*", "*://redir/*"],
|
|
permissions: [
|
|
"declarativeNetRequest",
|
|
"webRequest",
|
|
"webRequestBlocking",
|
|
],
|
|
},
|
|
});
|
|
await extension.startup();
|
|
await extension.awaitFinish();
|
|
await extension.unload();
|
|
});
|
|
|
|
add_task(async function block_request_with_webRequest_after_allow_with_dnr() {
|
|
async function background() {
|
|
let onBeforeRequestSeen = [];
|
|
browser.webRequest.onBeforeRequest.addListener(
|
|
d => {
|
|
onBeforeRequestSeen.push(d.url);
|
|
return { cancel: !d.url.includes("webRequestNoCancel") };
|
|
},
|
|
{ urls: ["*://example.com/*"] },
|
|
["blocking"]
|
|
);
|
|
// All DNR actions that do not end up canceling/redirecting the request:
|
|
await browser.declarativeNetRequest.updateSessionRules({
|
|
addRules: [
|
|
{
|
|
id: 1,
|
|
condition: { requestMethods: ["get"] },
|
|
action: { type: "allow" },
|
|
},
|
|
{
|
|
id: 2,
|
|
condition: { requestMethods: ["put"] },
|
|
action: {
|
|
type: "modifyHeaders",
|
|
requestHeaders: [{ operation: "set", header: "x", value: "y" }],
|
|
},
|
|
},
|
|
],
|
|
});
|
|
|
|
await browser.test.assertRejects(
|
|
fetch("http://example.com/never_reached?1", { method: "get" }),
|
|
"NetworkError when attempting to fetch resource.",
|
|
"despite DNR 'allow' rule, still blocked by webRequest"
|
|
);
|
|
await browser.test.assertRejects(
|
|
fetch("http://example.com/never_reached?2", { method: "put" }),
|
|
"NetworkError when attempting to fetch resource.",
|
|
"despite DNR 'modifyHeaders' rule, still blocked by webRequest"
|
|
);
|
|
// Just to rule out the request having been canceled by DNR instead of
|
|
// webRequest, repeat the requests and verify that they succeed.
|
|
await fetch("http://example.com/?webRequestNoCancel1", { method: "get" });
|
|
await fetch("http://example.com/?webRequestNoCancel2", { method: "put" });
|
|
|
|
browser.test.assertDeepEq(
|
|
[
|
|
"http://example.com/never_reached?1",
|
|
"http://example.com/never_reached?2",
|
|
"http://example.com/?webRequestNoCancel1",
|
|
"http://example.com/?webRequestNoCancel2",
|
|
],
|
|
onBeforeRequestSeen,
|
|
"Expected onBeforeRequest events"
|
|
);
|
|
|
|
browser.test.notifyPass();
|
|
}
|
|
let extension = ExtensionTestUtils.loadExtension({
|
|
background,
|
|
temporarilyInstalled: true, // Needed for granted_host_permissions
|
|
allowInsecureRequests: true,
|
|
manifest: {
|
|
manifest_version: 3,
|
|
granted_host_permissions: true,
|
|
host_permissions: ["*://example.com/*"],
|
|
permissions: [
|
|
"declarativeNetRequest",
|
|
"webRequest",
|
|
"webRequestBlocking",
|
|
],
|
|
},
|
|
});
|
|
await extension.startup();
|
|
await extension.awaitFinish();
|
|
await extension.unload();
|
|
});
|
|
|
|
add_task(async function redirect_with_webRequest_after_failing_dnr_redirect() {
|
|
async function background() {
|
|
// Maximum length of a UTL is 1048576 (network.standard-url.max-length).
|
|
const network_standard_url_max_length = 1048576;
|
|
// updateSessionRules does some validation on the limit (as seen by
|
|
// validate_action_redirect_transform in test_ext_dnr_session_rules.js),
|
|
// but it is still possible to pass validation and fail in practice when
|
|
// the existing URL + new component exceeds the limit.
|
|
const VERY_LONG_STRING = "x".repeat(network_standard_url_max_length - 20);
|
|
|
|
browser.webRequest.onBeforeRequest.addListener(
|
|
d => {
|
|
return { redirectUrl: "http://redir/destination?by-webrequest" };
|
|
},
|
|
{ urls: ["*://example.com/*"] },
|
|
["blocking"]
|
|
);
|
|
await browser.declarativeNetRequest.updateSessionRules({
|
|
addRules: [
|
|
{
|
|
id: 1,
|
|
condition: { requestDomains: ["example.com"] },
|
|
action: {
|
|
type: "redirect",
|
|
redirect: {
|
|
transform: {
|
|
host: "redir",
|
|
path: "/destination",
|
|
queryTransform: {
|
|
addOrReplaceParams: [
|
|
{ key: "dnr", value: VERY_LONG_STRING, replaceOnly: true },
|
|
],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
],
|
|
});
|
|
|
|
// Note: we are not expecting successful DNR redirects below, but in case
|
|
// that ever changes (e.g. due to VERY_LONG_STRING not resulting in an
|
|
// invalid URL), we will truncate the URL out of caution.
|
|
// VERY_LONG_STRING consists of many 'X'. Shorten to avoid logspam.
|
|
const shortx = s => s.replace(/x{10,}/g, xxx => `x{${xxx.length}}`);
|
|
|
|
browser.test.assertEq(
|
|
"http://redir/destination?1",
|
|
shortx((await fetch("http://example.com/never_reached?1")).url),
|
|
"Successful DNR redirect."
|
|
);
|
|
|
|
// DNR redirect failure is expected to be very rare, and only to occur when
|
|
// an extension intentionally explores the boundaries of the DNR API. When
|
|
// DNR fails, we fall back to allowing webRequest to take over.
|
|
browser.test.assertEq(
|
|
"http://redir/destination?by-webrequest",
|
|
shortx((await fetch("http://example.com/source?dnr")).url),
|
|
"When DNR fails, we fall back to webRequest redirect"
|
|
);
|
|
|
|
browser.test.notifyPass();
|
|
}
|
|
let extension = ExtensionTestUtils.loadExtension({
|
|
background,
|
|
temporarilyInstalled: true, // Needed for granted_host_permissions
|
|
allowInsecureRequests: true,
|
|
manifest: {
|
|
manifest_version: 3,
|
|
granted_host_permissions: true,
|
|
host_permissions: ["*://example.com/*"],
|
|
permissions: [
|
|
"declarativeNetRequest",
|
|
"webRequest",
|
|
"webRequestBlocking",
|
|
],
|
|
},
|
|
});
|
|
await extension.startup();
|
|
await extension.awaitFinish();
|
|
await extension.unload();
|
|
});
|