Bug 1725173: Add sec-fetch tests for extension content scripts. r=ckerschb,robwu

Differential Revision: https://phabricator.services.mozilla.com/D122361
This commit is contained in:
Niklas Goegge 2021-09-29 09:42:49 +00:00
Родитель 524e95c9e4
Коммит d9e823a6f5
2 изменённых файлов: 113 добавлений и 12 удалений

Просмотреть файл

@ -6,6 +6,7 @@
#include "SecFetch.h"
#include "nsIHttpChannel.h"
#include "nsContentUtils.h"
#include "nsIRedirectHistoryEntry.h"
#include "nsIReferrerInfo.h"
#include "mozIThirdPartyUtil.h"
@ -113,6 +114,25 @@ nsCString MapInternalContentPolicyTypeToDest(nsContentPolicyType aType) {
MOZ_CRASH("Unhandled nsContentPolicyType value");
}
// Helper function to determine if a ExpandedPrincipal is of the same-origin as
// a URI in the sec-fetch context.
void IsExpandedPrincipalSameOrigin(
nsCOMPtr<nsIExpandedPrincipal> aExpandedPrincipal, nsIURI* aURI,
bool aIsPrivateWin, bool* aRes) {
*aRes = false;
for (const auto& principal : aExpandedPrincipal->AllowList()) {
// Ignore extension principals to continue treating
// "moz-extension:"-requests as not "same-origin".
if (!mozilla::BasePrincipal::Cast(principal)->AddonPolicy()) {
// A ExpandedPrincipal usually has at most one ContentPrincipal, so we can
// check IsSameOrigin on it here and return early.
mozilla::BasePrincipal::Cast(principal)->IsSameOrigin(aURI, aIsPrivateWin,
aRes);
return;
}
}
}
// Helper function to determine whether a request (including involved
// redirects) is same-origin in the context of SecFetch.
bool IsSameOrigin(nsIHttpChannel* aHTTPChannel) {
@ -131,9 +151,15 @@ bool IsSameOrigin(nsIHttpChannel* aHTTPChannel) {
bool isPrivateWin = loadInfo->GetOriginAttributes().mPrivateBrowsingId > 0;
bool isSameOrigin = false;
nsresult rv = loadInfo->TriggeringPrincipal()->IsSameOrigin(
channelURI, isPrivateWin, &isSameOrigin);
mozilla::Unused << NS_WARN_IF(NS_FAILED(rv));
if (nsContentUtils::IsExpandedPrincipal(loadInfo->TriggeringPrincipal())) {
nsCOMPtr<nsIExpandedPrincipal> ep =
do_QueryInterface(loadInfo->TriggeringPrincipal());
IsExpandedPrincipalSameOrigin(ep, channelURI, isPrivateWin, &isSameOrigin);
} else {
nsresult rv = loadInfo->TriggeringPrincipal()->IsSameOrigin(
channelURI, isPrivateWin, &isSameOrigin);
mozilla::Unused << NS_WARN_IF(NS_FAILED(rv));
}
// if the initial request is not same-origin, we can return here
// because we already know it's not a same-origin request
@ -147,8 +173,8 @@ bool IsSameOrigin(nsIHttpChannel* aHTTPChannel) {
for (nsIRedirectHistoryEntry* entry : loadInfo->RedirectChain()) {
entry->GetPrincipal(getter_AddRefs(redirectPrincipal));
if (redirectPrincipal) {
rv = redirectPrincipal->IsSameOrigin(channelURI, isPrivateWin,
&isSameOrigin);
nsresult rv = redirectPrincipal->IsSameOrigin(channelURI, isPrivateWin,
&isSameOrigin);
mozilla::Unused << NS_WARN_IF(NS_FAILED(rv));
if (!isSameOrigin) {
return false;

Просмотреть файл

@ -5,7 +5,12 @@
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"],
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) => {
@ -26,6 +31,26 @@ server.registerPathHandler("/return_headers", (request, response) => {
response.write(JSON.stringify(headers));
});
async function contentScript() {
const results = await Promise.all([
// A cross-origin request from the content script.
// Sending requests with CORS from content scripts is currently not possible
// (Bug 1605197)
//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()),
]);
browser.test.sendMessage("content_results", results);
}
async function runSecFetchTest(test) {
let data = {
async background() {
@ -40,6 +65,15 @@ async function runSecFetchTest(test) {
},
manifest: {
manifest_version: 2,
content_scripts: [
{
matches: ["http://127.0.0.2/*"],
js: ["content_script.js"],
},
],
},
files: {
"content_script.js": contentScript,
},
};
@ -48,7 +82,7 @@ async function runSecFetchTest(test) {
const site = "http://127.0.0.1";
if (test.permission) {
data.manifest.permissions = [`${site}/*`];
data.manifest.permissions = ["http://127.0.0.2/*", `${site}/*`];
}
let extension = ExtensionTestUtils.loadExtension(data);
@ -56,29 +90,70 @@ async function runSecFetchTest(test) {
extension.sendMessage(site);
let backgroundResults = await extension.awaitMessage("background_results");
Assert.deepEqual(backgroundResults, test.expectedHeaders);
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_background_fetch_without_permission() {
add_task(async function test_fetch_without_permissions() {
await runSecFetchTest({
permission: false,
expectedHeaders: {
expectedBackgroundHeaders: {
"sec-fetch-site": "cross-site",
"sec-fetch-mode": "cors",
"sec-fetch-dest": "empty",
},
expectedContentHeaders: [
{
"sec-fetch-site": "cross-site",
"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_background_fetch_with_permission() {
add_task(async function test_fetch_with_permissions() {
await runSecFetchTest({
permission: true,
expectedHeaders: {
expectedBackgroundHeaders: {
"sec-fetch-site": "same-origin",
"sec-fetch-mode": "cors",
"sec-fetch-dest": "empty",
},
expectedContentHeaders: [
{
"sec-fetch-site": "cross-site",
"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",
},
],
});
});