Backed out changeset 19de2822bc0c (bug 1711168) for causing Bug 1719063.

CLOSED TREE
This commit is contained in:
Alexandru Michis 2021-07-08 22:56:34 +03:00
Родитель 8fd90ddcea
Коммит 183b0cfc30
11 изменённых файлов: 53 добавлений и 316 удалений

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

@ -18,7 +18,6 @@
#include "nsAboutProtocolUtils.h"
#include "ThirdPartyUtil.h"
#include "mozilla/ContentPrincipal.h"
#include "mozilla/ExtensionPolicyService.h"
#include "mozilla/NullPrincipal.h"
#include "mozilla/dom/BlobURLProtocolHandler.h"
#include "mozilla/dom/ChromeUtils.h"
@ -571,32 +570,21 @@ nsresult BasePrincipal::CheckMayLoadHelper(nsIURI* aURI,
}
}
// Get the principal uri for the last flag check or error.
nsCOMPtr<nsIURI> prinURI;
rv = GetURI(getter_AddRefs(prinURI));
if (!(NS_SUCCEEDED(rv) && prinURI)) {
return NS_ERROR_DOM_BAD_URI;
}
// If Extension uris are web accessible by this principal it is allowed to
// load.
bool maybeWebAccessible = false;
NS_URIChainHasFlags(aURI, nsIProtocolHandler::WEBEXT_URI_WEB_ACCESSIBLE,
&maybeWebAccessible);
NS_ENSURE_SUCCESS(rv, rv);
if (maybeWebAccessible) {
bool isWebAccessible = false;
rv = ExtensionPolicyService::GetSingleton().SourceMayLoadExtensionURI(
prinURI, aURI, &isWebAccessible);
if (NS_SUCCEEDED(rv) && isWebAccessible) {
return NS_OK;
}
bool fetchableByAnyone;
rv = NS_URIChainHasFlags(aURI, nsIProtocolHandler::URI_FETCHABLE_BY_ANYONE,
&fetchableByAnyone);
if (NS_SUCCEEDED(rv) && fetchableByAnyone) {
return NS_OK;
}
if (aReport) {
nsScriptSecurityManager::ReportError(
"CheckSameOriginError", prinURI, aURI,
mOriginAttributes.mPrivateBrowsingId > 0, aInnerWindowID);
nsCOMPtr<nsIURI> prinURI;
rv = GetURI(getter_AddRefs(prinURI));
if (NS_SUCCEEDED(rv) && prinURI) {
nsScriptSecurityManager::ReportError(
"CheckSameOriginError", prinURI, aURI,
mOriginAttributes.mPrivateBrowsingId > 0, aInnerWindowID);
}
}
return NS_ERROR_DOM_BAD_URI;

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

@ -670,6 +670,21 @@ nsScriptSecurityManager::CheckLoadURIWithPrincipal(nsIPrincipal* aPrincipal,
return NS_ERROR_DOM_BAD_URI;
}
// Extensions may allow access to a web accessible resource.
bool maybeWebAccessible = false;
NS_URIChainHasFlags(targetBaseURI,
nsIProtocolHandler::WEBEXT_URI_WEB_ACCESSIBLE,
&maybeWebAccessible);
NS_ENSURE_SUCCESS(rv, rv);
if (maybeWebAccessible) {
bool isWebAccessible = false;
rv = ExtensionPolicyService::GetSingleton().SourceMayLoadExtensionURI(
sourceURI, targetBaseURI, &isWebAccessible);
if (!(NS_SUCCEEDED(rv) && isWebAccessible)) {
return NS_ERROR_DOM_BAD_URI;
}
}
// Check for uris that are only loadable by principals that subsume them
bool targetURIIsLoadableBySubsumers = false;
rv = NS_URIChainHasFlags(targetBaseURI,
@ -743,7 +758,6 @@ nsScriptSecurityManager::CheckLoadURIWithPrincipal(nsIPrincipal* aPrincipal,
bool schemesMatch =
scheme.Equals(otherScheme, nsCaseInsensitiveCStringComparator);
bool isSamePage = false;
bool isExtensionMismatch = false;
// about: URIs are special snowflakes.
if (scheme.EqualsLiteral("about") && schemesMatch) {
nsAutoCString moduleName, otherModuleName;
@ -791,13 +805,6 @@ nsScriptSecurityManager::CheckLoadURIWithPrincipal(nsIPrincipal* aPrincipal,
}
}
}
} else if (schemesMatch && scheme.EqualsLiteral("moz-extension")) {
// If it is not the same exension, we want to ensure we end up
// calling CheckLoadURIFlags
nsAutoCString host, otherHost;
currentURI->GetHost(host);
currentOtherURI->GetHost(otherHost);
isExtensionMismatch = !host.Equals(otherHost);
} else {
bool equalExceptRef = false;
rv = currentURI->EqualsExceptRef(currentOtherURI, &equalExceptRef);
@ -806,12 +813,10 @@ nsScriptSecurityManager::CheckLoadURIWithPrincipal(nsIPrincipal* aPrincipal,
// If schemes are not equal, or they're equal but the target URI
// is different from the source URI and doesn't always allow linking
// from the same scheme, or this is two different extensions, check
// if the URI flags of the current target URI allow the current
// source URI to link to it.
// from the same scheme, check if the URI flags of the current target
// URI allow the current source URI to link to it.
// The policy is specified by the protocol flags on both URIs.
if (!schemesMatch || (denySameSchemeLinks && !isSamePage) ||
isExtensionMismatch) {
if (!schemesMatch || (denySameSchemeLinks && !isSamePage)) {
return CheckLoadURIFlags(
currentURI, currentOtherURI, sourceBaseURI, targetBaseURI, aFlags,
aPrincipal->OriginAttributesRef().mPrivateBrowsingId > 0,
@ -886,25 +891,6 @@ nsresult nsScriptSecurityManager::CheckLoadURIFlags(
}
}
// If Extension uris are web accessible they have WEBEXT_URI_WEB_ACCESSIBLE.
bool maybeWebAccessible = false;
NS_URIChainHasFlags(aTargetURI, nsIProtocolHandler::WEBEXT_URI_WEB_ACCESSIBLE,
&maybeWebAccessible);
NS_ENSURE_SUCCESS(rv, rv);
if (maybeWebAccessible) {
bool isWebAccessible = false;
rv = ExtensionPolicyService::GetSingleton().SourceMayLoadExtensionURI(
aSourceURI, aTargetURI, &isWebAccessible);
if (NS_SUCCEEDED(rv) && isWebAccessible) {
return NS_OK;
}
if (reportErrors) {
ReportError(errorTag, aSourceURI, aTargetURI, aFromPrivateWindow,
aInnerWindowID);
}
return NS_ERROR_DOM_BAD_URI;
}
// Check for chrome target URI
bool targetURIIsUIResource = false;
rv = NS_URIChainHasFlags(aTargetURI, nsIProtocolHandler::URI_IS_UI_RESOURCE,

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

@ -268,8 +268,7 @@ interface WebExtensionPolicy {
dictionary WebAccessibleResourceInit {
required sequence<MatchGlobOrString> resources;
MatchPatternSetOrStringSequence? matches = null;
sequence<DOMString>? extensions = null;
MatchPatternSetOrStringSequence matches;
};
dictionary WebExtensionInit {

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

@ -1080,7 +1080,7 @@ nsresult nsContentSecurityManager::CheckAllowLoadInSystemPrivilegedContext(
}
/*
* Every protocol handler must set one of the six security flags
* Every protocol handler must set one of the five security flags
* defined in nsIProtocolHandler - if not - deny the load.
*/
nsresult nsContentSecurityManager::CheckChannelHasProtocolSecurityFlag(
@ -1105,9 +1105,6 @@ nsresult nsContentSecurityManager::CheckChannelHasProtocolSecurityFlag(
NS_ENSURE_SUCCESS(rv, rv);
uint32_t securityFlagsSet = 0;
if (flags & nsIProtocolHandler::WEBEXT_URI_WEB_ACCESSIBLE) {
securityFlagsSet += 1;
}
if (flags & nsIProtocolHandler::URI_LOADABLE_BY_ANYONE) {
securityFlagsSet += 1;
}

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

@ -272,6 +272,13 @@ interface nsIProtocolHandler : nsISupports
*/
const unsigned long URI_IS_POTENTIALLY_TRUSTWORTHY = (1<<17);
/**
* This URI may be fetched and the contents are visible to anyone. This is
* semantically equivalent to the resource being served with all-access CORS
* headers.
*/
const unsigned long URI_FETCHABLE_BY_ANYONE = (1 << 18);
/**
* If this flag is set, then the origin for this protocol is the full URI
* spec, not just the scheme + host + port.
@ -305,7 +312,7 @@ interface nsIProtocolHandler : nsISupports
/**
* This is an extension web accessible uri that is loadable if checked
* against an allow whitelist using ExtensionPolicyService::SourceMayLoadExtensionURI.
* against an allow whitelist.
*/
const unsigned long WEBEXT_URI_WEB_ACCESSIBLE = (1 << 24);
};

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

@ -368,12 +368,15 @@ nsresult ExtensionProtocolHandler::GetFlagsForURI(nsIURI* aURI,
URLInfo url(aURI);
if (auto* policy = EPS().GetByURL(url)) {
// In general a moz-extension URI is only loadable by chrome, but an
// allowlist subset are web-accessible (and cross-origin fetchable).
// The allowlist is checked using EPS.SourceMayLoadExtensionURI in
// BasePrincipal and nsScriptSecurityManager.
// In general a moz-extension URI is only loadable by chrome, but a
// whitelisted subset are web-accessible (and cross-origin fetchable). Check
// that whitelist. For Manifest V3 extensions, an additional whitelist
// for the source loading the url must be checked so we add the flag
// WEBEXT_URI_WEB_ACCESSIBLE, which is then checked in
// nsScriptSecurityManager.
if (policy->IsWebAccessiblePath(url.FilePath())) {
flags |= WEBEXT_URI_WEB_ACCESSIBLE;
flags |= URI_LOADABLE_BY_ANYONE | URI_FETCHABLE_BY_ANYONE |
WEBEXT_URI_WEB_ACCESSIBLE;
} else {
flags |= URI_DANGEROUS_TO_LOAD;
}

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

@ -306,18 +306,6 @@ const POSTPROCESSORS = {
context.logError(context.makeError(msg));
throw new Error(msg);
},
webAcessibleMatching(value, context) {
// Ensure each object has at least one of matches or extensions array.
for (let obj of value) {
if (!obj.matches && !obj.extensions) {
const msg = `web_accessible_resources requires one of "matches" or "extensions"`;
context.logError(context.makeError(msg));
throw new Error(msg);
}
}
return value;
},
};
// Parses a regular expression, with support for the Python extended

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

@ -143,24 +143,12 @@ WebAccessibleResource::WebAccessibleResource(
return;
}
if (!aInit.mMatches.IsNull()) {
if (aInit.mMatches.WasPassed()) {
MatchPatternOptions options;
options.mRestrictSchemes = true;
mMatches = ParseMatches(aGlobal, aInit.mMatches.Value(), options,
ErrorBehavior::CreateEmptyPattern, aRv);
}
if (!aInit.mExtensions.IsNull()) {
mExtensions = new AtomSet(aInit.mExtensions.Value());
}
}
bool WebAccessibleResource::IsExtensionMatch(const URLInfo& aURI) {
if (!mExtensions) {
return false;
}
WebExtensionPolicy* policy = EPS().GetByHost(aURI.Host());
return policy && mExtensions->Contains(policy->Id());
}
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebAccessibleResource)

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

@ -50,23 +50,16 @@ class WebAccessibleResource final : public nsISupports {
}
bool SourceMayAccessPath(const URLInfo& aURI, const nsAString& aPath) {
return mWebAccessiblePaths.Matches(aPath) &&
(IsHostMatch(aURI) || IsExtensionMatch(aURI));
return mWebAccessiblePaths.Matches(aPath) && mMatches &&
mMatches->Matches(aURI);
}
bool IsHostMatch(const URLInfo& aURI) {
return mMatches && mMatches->Matches(aURI);
}
bool IsExtensionMatch(const URLInfo& aURI);
protected:
virtual ~WebAccessibleResource() = default;
private:
MatchGlobSet mWebAccessiblePaths;
RefPtr<MatchPatternSet> mMatches;
RefPtr<AtomSet> mExtensions;
};
class WebExtensionPolicy final : public nsISupports,

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

@ -243,7 +243,6 @@
{
"min_manifest_version": 3,
"type": "array",
"postprocess": "webAcessibleMatching",
"items": {
"type": "object",
"properties": {
@ -252,14 +251,8 @@
"items": { "type": "string" }
},
"matches": {
"optional": true,
"type": "array",
"items": { "$ref": "MatchPatternRestricted" }
},
"extensions": {
"optional": true,
"type": "array",
"items": { "$ref": "ExtensionID" }
}
}
}

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

@ -1,5 +1,4 @@
"use strict";
Services.prefs.setBoolPref("extensions.manifestV3.enabled", true);
const server = createHttpServer({ hosts: ["example.com", "example.org"] });
@ -12,77 +11,10 @@ let image = atob(
const IMAGE_ARRAYBUFFER = Uint8Array.from(image, byte => byte.charCodeAt(0))
.buffer;
add_task(async function test_web_accessible_resources_matching() {
let extension = await ExtensionTestUtils.loadExtension({
manifest: {
manifest_version: 3,
web_accessible_resources: [
{
resources: ["/accessible.html"],
},
],
},
});
await Assert.rejects(
extension.startup(),
/web_accessible_resources requires one of "matches" or "extensions"/,
"web_accessible_resources object format incorrect"
);
extension = await ExtensionTestUtils.loadExtension({
manifest: {
manifest_version: 3,
web_accessible_resources: [
{
resources: ["/accessible.html"],
matches: ["http://example.com/data/*"],
},
],
},
});
await extension.startup();
ok(true, "web_accessible_resources with matches loads");
await extension.unload();
extension = await ExtensionTestUtils.loadExtension({
manifest: {
manifest_version: 3,
web_accessible_resources: [
{
resources: ["/accessible.html"],
extensions: ["foo@mochitest"],
},
],
},
});
await extension.startup();
ok(true, "web_accessible_resources with extensions loads");
await extension.unload();
extension = await ExtensionTestUtils.loadExtension({
manifest: {
manifest_version: 3,
web_accessible_resources: [
{
resources: ["/accessible.html"],
matches: ["http://example.com/data/*"],
extensions: ["foo@mochitest"],
},
],
},
});
await extension.startup();
ok(true, "web_accessible_resources with matches and extensions loads");
await extension.unload();
});
add_task(async function test_web_accessible_resources() {
async function contentScript() {
let canLoad = window.location.href.startsWith("http://example.com");
let urls = [
{
name: "iframe",
@ -131,11 +63,7 @@ add_task(async function test_web_accessible_resources() {
}
for (let test of urls) {
let loaded = await test_element_src(test.name, test.url);
browser.test.assertEq(
loaded,
test.shouldLoad,
`resource ${test.url} loaded`
);
browser.test.assertEq(loaded, test.shouldLoad, "resource loaded");
}
browser.test.notifyPass("web-accessible-resources");
}
@ -194,136 +122,3 @@ add_task(async function test_web_accessible_resources() {
await page.close();
await extension.unload();
});
add_task(async function test_web_accessible_resources_extensions() {
async function pageScript() {
function test_element_src(data) {
return new Promise(resolve => {
let elem = document.createElement(data.elem);
let elemContext = data.content_context ? elem.wrappedJSObject : elem;
elemContext.setAttribute("src", data.url);
elem.addEventListener(
"load",
() => {
browser.test.log(`got load event for ${data.url}`);
resolve(true);
},
{ once: true }
);
elem.addEventListener(
"error",
() => {
browser.test.log(`got error event for ${data.url}`);
resolve(false);
},
{ once: true }
);
document.body.appendChild(elem);
});
}
browser.test.onMessage.addListener(async msg => {
browser.test.log(`testing ${JSON.stringify(msg)}`);
let loaded = await test_element_src(msg);
browser.test.assertEq(loaded, msg.shouldLoad, `${msg.name} loaded`);
browser.test.sendMessage("web-accessible-resources");
});
}
let other = ExtensionTestUtils.loadExtension({
manifest: {
applications: { gecko: { id: "other@mochitest" } },
},
files: {
"page.js": pageScript,
"page.html": `<html><head>
<meta charset="utf-8">
<script src="page.js"></script>
</head></html>`,
},
});
let extension = ExtensionTestUtils.loadExtension({
manifest: {
manifest_version: 3,
applications: { gecko: { id: "this@mochitest" } },
content_scripts: [
{
matches: ["http://example.com/data/*"],
js: ["page.js"],
run_at: "document_idle",
},
],
web_accessible_resources: [
{
resources: ["/image.png"],
extensions: ["other@mochitest"],
},
],
},
files: {
"image.png": IMAGE_ARRAYBUFFER,
"inaccessible.png": IMAGE_ARRAYBUFFER,
"page.js": pageScript,
"page.html": `<html><head>
<meta charset="utf-8">
<script src="page.js"></script>
</head></html>`,
},
});
await extension.startup();
let extensionUrl = `moz-extension://${extension.uuid}/`;
await other.startup();
let pageUrl = `moz-extension://${other.uuid}/page.html`;
let page = await ExtensionTestUtils.loadContentPage(pageUrl);
other.sendMessage({
name: "accessible resource",
elem: "img",
url: `${extensionUrl}image.png`,
shouldLoad: true,
});
await other.awaitMessage("web-accessible-resources");
other.sendMessage({
name: "inaccessible resource",
elem: "img",
url: `${extensionUrl}inaccessible.png`,
shouldLoad: false,
});
await other.awaitMessage("web-accessible-resources");
await page.close();
// test that the extension may load it's own web accessible resource
page = await ExtensionTestUtils.loadContentPage(`${extensionUrl}page.html`);
extension.sendMessage({
name: "accessible resource",
elem: "img",
url: `${extensionUrl}image.png`,
shouldLoad: true,
});
await extension.awaitMessage("web-accessible-resources");
await page.close();
// test that a web page not in matches cannot load the resource
page = await ExtensionTestUtils.loadContentPage("http://example.com/data/");
extension.sendMessage({
name: "cannot access resource",
elem: "img",
url: `${extensionUrl}image.png`,
content_context: true,
shouldLoad: false,
});
await extension.awaitMessage("web-accessible-resources");
await extension.unload();
await other.unload();
});