mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 05:11:16 +00:00
Bug 1521573 web_accessible incognito support, r=kmag,smaug
Prevent web_accessible_resources resources loading in private contexts when extension does not have permission. Differential Revision: https://phabricator.services.mozilla.com/D17138 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
a68eff5db8
commit
3e429f056f
@ -932,6 +932,19 @@ nsresult nsScriptSecurityManager::CheckLoadURIFlags(
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Used by ExtensionProtocolHandler to prevent loading extension resources
|
||||
// in private contexts if the extension does not have permission.
|
||||
if (aFromPrivateWindow) {
|
||||
rv = DenyAccessIfURIHasFlags(
|
||||
aTargetURI, nsIProtocolHandler::URI_DISALLOW_IN_PRIVATE_CONTEXT);
|
||||
if (NS_FAILED(rv)) {
|
||||
if (reportErrors) {
|
||||
ReportError(errorTag, aSourceURI, aTargetURI, aFromPrivateWindow);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for chrome target URI
|
||||
bool hasFlags = false;
|
||||
rv = NS_URIChainHasFlags(aTargetURI, nsIProtocolHandler::URI_IS_UI_RESOURCE,
|
||||
|
@ -330,4 +330,9 @@ interface nsIProtocolHandler : nsISupports
|
||||
* The URIs for this protocol can be loaded by extensions.
|
||||
*/
|
||||
const unsigned long URI_LOADABLE_BY_EXTENSIONS = (1 << 21);
|
||||
|
||||
/**
|
||||
* The URIs for this protocol can not be loaded into private contexts.
|
||||
*/
|
||||
const unsigned long URI_DISALLOW_IN_PRIVATE_CONTEXT = (1 << 22);
|
||||
};
|
||||
|
@ -341,20 +341,27 @@ static inline ExtensionPolicyService& EPS() {
|
||||
|
||||
nsresult ExtensionProtocolHandler::GetFlagsForURI(nsIURI* aURI,
|
||||
uint32_t* aFlags) {
|
||||
// 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.
|
||||
bool loadableByAnyone = false;
|
||||
uint32_t flags =
|
||||
URI_STD | URI_IS_LOCAL_RESOURCE | URI_IS_POTENTIALLY_TRUSTWORTHY;
|
||||
|
||||
URLInfo url(aURI);
|
||||
if (auto* policy = EPS().GetByURL(url)) {
|
||||
loadableByAnyone = policy->IsPathWebAccessible(url.FilePath());
|
||||
// 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.
|
||||
if (policy->IsPathWebAccessible(url.FilePath())) {
|
||||
flags |= URI_LOADABLE_BY_ANYONE | URI_FETCHABLE_BY_ANYONE;
|
||||
} else {
|
||||
flags |= URI_DANGEROUS_TO_LOAD;
|
||||
}
|
||||
|
||||
// Disallow in private windows if the extension does not have permission.
|
||||
if (!policy->PrivateBrowsingAllowed()) {
|
||||
flags |= URI_DISALLOW_IN_PRIVATE_CONTEXT;
|
||||
}
|
||||
}
|
||||
|
||||
*aFlags =
|
||||
URI_STD | URI_IS_LOCAL_RESOURCE | URI_IS_POTENTIALLY_TRUSTWORTHY |
|
||||
(loadableByAnyone ? (URI_LOADABLE_BY_ANYONE | URI_FETCHABLE_BY_ANYONE)
|
||||
: URI_DANGEROUS_TO_LOAD);
|
||||
*aFlags = flags;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -122,6 +122,8 @@ skip-if = os == 'android' || verify # bug 1489771
|
||||
skip-if = os == 'android'
|
||||
[test_ext_web_accessible_resources.html]
|
||||
skip-if = os == 'android' && debug # bug 1397615
|
||||
[test_ext_web_accessible_incognito.html]
|
||||
skip-if = os == 'android' # bug 1397615 and bug 1513544
|
||||
[test_ext_webnavigation.html]
|
||||
skip-if = os == 'android' && debug # bug 1397615
|
||||
[test_ext_webnavigation_filters.html]
|
||||
|
@ -0,0 +1,175 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test the web_accessible_resources incognito</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/AddTask.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/ExtensionTestUtils.js"></script>
|
||||
<script type="text/javascript" src="head.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<script type="text/javascript">
|
||||
"use strict";
|
||||
|
||||
let image = atob("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAA" +
|
||||
"ACnej3aAAAAAXRSTlMAQObYZgAAAApJREFUCNdjYAAAAAIAAeIhvDMAAAAASUVORK5CYII=");
|
||||
const IMAGE_ARRAYBUFFER = Uint8Array.from(image, byte => byte.charCodeAt(0)).buffer;
|
||||
|
||||
async function testImageLoading(src, expectedAction) {
|
||||
let imageLoadingPromise = new Promise((resolve, reject) => {
|
||||
let cleanupListeners;
|
||||
let testImage = new window.Image();
|
||||
// Set the src via wrappedJSObject so the load is triggered with the
|
||||
// content page's principal rather than ours.
|
||||
testImage.wrappedJSObject.setAttribute("src", src);
|
||||
|
||||
let loadListener = () => {
|
||||
cleanupListeners();
|
||||
resolve(expectedAction === "loaded");
|
||||
};
|
||||
|
||||
let errorListener = (event) => {
|
||||
cleanupListeners();
|
||||
resolve(expectedAction === "blocked");
|
||||
browser.test.log(`+++ image loading ${event.error}`);
|
||||
};
|
||||
|
||||
cleanupListeners = () => {
|
||||
testImage.removeEventListener("load", loadListener);
|
||||
testImage.removeEventListener("error", errorListener);
|
||||
};
|
||||
|
||||
testImage.addEventListener("load", loadListener);
|
||||
testImage.addEventListener("error", errorListener);
|
||||
});
|
||||
|
||||
let success = await imageLoadingPromise;
|
||||
browser.runtime.sendMessage({name: "image-loading", expectedAction, success});
|
||||
}
|
||||
|
||||
function testScript() {
|
||||
window.postMessage("test-script-loaded", "*");
|
||||
}
|
||||
|
||||
add_task(async function test_web_accessible_resources_incognito() {
|
||||
await SpecialPowers.pushPrefEnv({set: [
|
||||
["extensions.allowPrivateBrowsingByDefault", false],
|
||||
]});
|
||||
|
||||
// This extension will not have access to private browsing so its
|
||||
// accessible resources should not be able to load in them.
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
manifest: {
|
||||
"web_accessible_resources": [
|
||||
"image.png",
|
||||
"test_script.js",
|
||||
"accessible.html",
|
||||
],
|
||||
},
|
||||
background() {
|
||||
browser.test.sendMessage("url", browser.extension.getURL(""));
|
||||
},
|
||||
files: {
|
||||
"image.png": IMAGE_ARRAYBUFFER,
|
||||
"test_script.js": testScript,
|
||||
"accessible.html": `<html><head>
|
||||
<meta charset="utf-8">
|
||||
</head></html>`,
|
||||
},
|
||||
});
|
||||
|
||||
await extension.startup();
|
||||
let baseUrl = await extension.awaitMessage("url");
|
||||
|
||||
async function content() {
|
||||
let baseUrl = await browser.runtime.sendMessage({name: "get-url"});
|
||||
testImageLoading(`${baseUrl}image.png`, "loaded");
|
||||
|
||||
let testScriptElement = document.createElement("script");
|
||||
// Set the src via wrappedJSObject so the load is triggered with the
|
||||
// content page's principal rather than ours.
|
||||
testScriptElement.wrappedJSObject.setAttribute("src", `${baseUrl}test_script.js`);
|
||||
document.head.appendChild(testScriptElement);
|
||||
|
||||
let iframe = document.createElement("iframe");
|
||||
// Set the src via wrappedJSObject so the load is triggered with the
|
||||
// content page's principal rather than ours.
|
||||
iframe.wrappedJSObject.setAttribute("src", `${baseUrl}accessible.html`);
|
||||
document.body.appendChild(iframe);
|
||||
|
||||
// eslint-disable-next-line mozilla/balanced-listeners
|
||||
window.addEventListener("message", event => {
|
||||
browser.runtime.sendMessage({"name": event.data});
|
||||
});
|
||||
}
|
||||
|
||||
let pb_extension = ExtensionTestUtils.loadExtension({
|
||||
incognitoOverride: "spanning",
|
||||
manifest: {
|
||||
permissions: ["tabs"],
|
||||
content_scripts: [{
|
||||
"matches": ["*://example.com/*/file_sample.html"],
|
||||
"run_at": "document_end",
|
||||
"js": ["content_script_helper.js", "content_script.js"],
|
||||
}],
|
||||
},
|
||||
files: {
|
||||
"content_script_helper.js": `${testImageLoading}`,
|
||||
"content_script.js": content,
|
||||
},
|
||||
background() {
|
||||
let url = "http://example.com/tests/toolkit/components/extensions/test/mochitest/file_sample.html";
|
||||
let baseUrl;
|
||||
let window;
|
||||
|
||||
browser.runtime.onMessage.addListener(async msg => {
|
||||
switch (msg.name) {
|
||||
case "image-loading":
|
||||
browser.test.assertFalse(msg.success, `Image was ${msg.expectedAction}`);
|
||||
browser.test.sendMessage(`image-${msg.expectedAction}`);
|
||||
break;
|
||||
case "get-url":
|
||||
return baseUrl;
|
||||
default:
|
||||
browser.test.fail(`unexepected message ${msg.name}`);
|
||||
}
|
||||
});
|
||||
|
||||
browser.test.onMessage.addListener(async (msg, data) => {
|
||||
if (msg == "start") {
|
||||
baseUrl = data;
|
||||
window = await browser.windows.create({url, incognito: true});
|
||||
}
|
||||
if (msg == "close") {
|
||||
browser.windows.remove(window.id);
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
await pb_extension.startup();
|
||||
|
||||
consoleMonitor.start([
|
||||
{message: /may not load or link to.*image.png/},
|
||||
{message: /may not load or link to.*test_script.js/},
|
||||
{message: /\<script\> source URI is not allowed in this document/},
|
||||
{message: /may not load or link to.*accessible.html/},
|
||||
]);
|
||||
|
||||
pb_extension.sendMessage("start", baseUrl);
|
||||
|
||||
await pb_extension.awaitMessage("image-loaded");
|
||||
|
||||
pb_extension.sendMessage("close");
|
||||
|
||||
await extension.unload();
|
||||
await pb_extension.unload();
|
||||
|
||||
await consoleMonitor.finished();
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user