Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

/* -*- 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/test/response-header?name=${headerName}&value=${siteHeaderValue}`
);
} else {
await ExtensionTestUtils.fetch(
);
}
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,
});
}
});