Bug 1213632: Prevent WebExtensions from using versioned JavaScript. r=billm

--HG--
extra : commitid : 3aQ4wS5I7LP
extra : rebase_source : 252277140f700ab305877f3bfd0ba9b582ff0b7a
This commit is contained in:
Kris Maglione 2015-10-22 23:25:43 -07:00
parent db0f60ba3d
commit 9768c79d20
7 changed files with 271 additions and 2 deletions

View File

@ -89,6 +89,9 @@
"@mozilla.org/updates/update-processor;1"
#endif
#define NS_ADDONCONTENTPOLICY_CONTRACTID \
"@mozilla.org/addons/content-policy;1"
#define NS_ADDONPATHSERVICE_CONTRACTID \
"@mozilla.org/addon-path-service;1"
@ -178,5 +181,8 @@
#define NS_APPLICATION_REPUTATION_SERVICE_CID \
{ 0x8576c950, 0xf4a2, 0x11e2, { 0xb7, 0x78, 0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66 } }
#define NS_ADDONCONTENTPOLICY_CID \
{ 0xc26a8241, 0xecf4, 0x4aed, { 0x9f, 0x3c, 0xf1, 0xf5, 0xc7, 0x13, 0xb9, 0xa5 } }
#define NS_ADDON_PATH_SERVICE_CID \
{ 0xa39f39d0, 0xdfb6, 0x11e3, { 0x8b, 0x68, 0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66 } }

View File

@ -36,6 +36,7 @@
#include "nsBrowserStatusFilter.h"
#include "mozilla/FinalizationWitnessService.h"
#include "mozilla/NativeOSFileInternals.h"
#include "mozilla/AddonContentPolicy.h"
#include "mozilla/AddonPathService.h"
#if defined(XP_WIN)
@ -125,6 +126,7 @@ NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(FinalizationWitnessService, Init)
NS_GENERIC_FACTORY_CONSTRUCTOR(NativeOSFileInternalsService)
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(NativeFileWatcherService, Init)
NS_GENERIC_FACTORY_CONSTRUCTOR(AddonContentPolicy)
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(AddonPathService, AddonPathService::GetInstance)
NS_DEFINE_NAMED_CID(NS_TOOLKIT_APPSTARTUP_CID);
@ -158,6 +160,7 @@ NS_DEFINE_NAMED_CID(NS_UPDATEPROCESSOR_CID);
#endif
NS_DEFINE_NAMED_CID(FINALIZATIONWITNESSSERVICE_CID);
NS_DEFINE_NAMED_CID(NATIVE_OSFILE_INTERNALS_SERVICE_CID);
NS_DEFINE_NAMED_CID(NS_ADDONCONTENTPOLICY_CID);
NS_DEFINE_NAMED_CID(NS_ADDON_PATH_SERVICE_CID);
NS_DEFINE_NAMED_CID(NATIVE_FILEWATCHER_SERVICE_CID);
@ -192,6 +195,7 @@ static const Module::CIDEntry kToolkitCIDs[] = {
#endif
{ &kFINALIZATIONWITNESSSERVICE_CID, false, nullptr, FinalizationWitnessServiceConstructor },
{ &kNATIVE_OSFILE_INTERNALS_SERVICE_CID, false, nullptr, NativeOSFileInternalsServiceConstructor },
{ &kNS_ADDONCONTENTPOLICY_CID, false, nullptr, AddonContentPolicyConstructor },
{ &kNS_ADDON_PATH_SERVICE_CID, false, nullptr, AddonPathServiceConstructor },
{ &kNATIVE_FILEWATCHER_SERVICE_CID, false, nullptr, NativeFileWatcherServiceConstructor },
{ nullptr }
@ -228,15 +232,22 @@ static const Module::ContractIDEntry kToolkitContracts[] = {
#endif
{ FINALIZATIONWITNESSSERVICE_CONTRACTID, &kFINALIZATIONWITNESSSERVICE_CID },
{ NATIVE_OSFILE_INTERNALS_SERVICE_CONTRACTID, &kNATIVE_OSFILE_INTERNALS_SERVICE_CID },
{ NS_ADDONCONTENTPOLICY_CONTRACTID, &kNS_ADDONCONTENTPOLICY_CID },
{ NS_ADDONPATHSERVICE_CONTRACTID, &kNS_ADDON_PATH_SERVICE_CID },
{ NATIVE_FILEWATCHER_SERVICE_CONTRACTID, &kNATIVE_FILEWATCHER_SERVICE_CID },
{ nullptr }
};
static const mozilla::Module::CategoryEntry kToolkitCategories[] = {
{ "content-policy", NS_ADDONCONTENTPOLICY_CONTRACTID, NS_ADDONCONTENTPOLICY_CONTRACTID },
{ nullptr }
};
static const Module kToolkitModule = {
Module::kVersion,
kToolkitCIDs,
kToolkitContracts
kToolkitContracts,
kToolkitCategories
};
NSMODULE_DEFN(nsToolkitCompsModule) = &kToolkitModule;

View File

@ -33,3 +33,5 @@ support-files =
[test_ext_bookmarks.html]
[test_ext_alarms.html]
[test_ext_background_window_properties.html]
[test_ext_jsversion.html]
skip-if = e10s # Uses a console monitor which doesn't work from a content process. The code being tested doesn't run in a tab content process in any case.

View File

@ -0,0 +1,84 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for simple WebExtension</title>
<meta charset="utf-8">
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SpawnTask.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="application/javascript;version=1.8">
add_task(function* test_versioned_js() {
// We need to deal with escaping the close script tags.
// May as well consolidate it into one place.
let script = attrs => `<script ${attrs}></${'script'}>`;
let extension = ExtensionTestUtils.loadExtension({
manifest: {
"background": {"page": "background.html"}
},
files: {
"background.html": `
<meta charset="utf-8">
${script('src="background.js" type="application/javascript"')}
${script('src="background-1.js" type="application/javascript;version=1.8"')}
${script('src="background-2.js" type="application/javascript;version=latest"')}
${script('src="background-3.js" type="application/javascript"')}
`,
"background.js": "new " + function () {
browser.runtime.onMessage.addListener(msg => {
browser.test.assertEq(
msg, "background-script-3",
"Expected a message only from the unversioned background script.");
browser.test.sendMessage("finished");
});
},
"background-1.js": "new " + function () {
browser.runtime.sendMessage("background-script-1");
},
"background-2.js": "new " + function () {
browser.runtime.sendMessage("background-script-2");
},
"background-3.js": "new " + function () {
browser.runtime.sendMessage("background-script-3");
},
},
});
let messages = [/Versioned JavaScript.*not supported in WebExtension.*developer\.mozilla\.org/,
/Versioned JavaScript.*not supported in WebExtension.*developer\.mozilla\.org/];
let waitForConsole = new Promise(resolve => {
SimpleTest.monitorConsole(resolve, messages);
});
info("loading extension");
yield Promise.all([extension.startup(),
extension.awaitMessage("finished")]);
info("waiting for console");
SimpleTest.endMonitorConsole();
yield waitForConsole;
info("unloading extension");
yield extension.unload();
info("test complete");
});
</script>
</body>
</html>

View File

@ -0,0 +1,145 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "AddonContentPolicy.h"
#include "nsCOMPtr.h"
#include "nsContentPolicyUtils.h"
#include "nsContentTypeParser.h"
#include "nsContentUtils.h"
#include "nsIConsoleService.h"
#include "nsIContent.h"
#include "nsIDocument.h"
#include "nsIScriptError.h"
#include "nsIURI.h"
/* Enforces content policies for WebExtension scopes. Currently:
*
* - Prevents loading scripts with a non-default JavaScript version.
*/
#define VERSIONED_JS_BLOCKED_MESSAGE \
MOZ_UTF16("Versioned JavaScript is a non-standard, deprecated extension, and is ") \
MOZ_UTF16("not supported in WebExtension code. For alternatives, please see: ") \
MOZ_UTF16("https://developer.mozilla.org/Add-ons/WebExtensions/Tips")
AddonContentPolicy::AddonContentPolicy()
{
}
AddonContentPolicy::~AddonContentPolicy()
{
}
NS_IMPL_ISUPPORTS(AddonContentPolicy, nsIContentPolicy)
static nsresult
GetWindowIDFromContext(nsISupports* aContext, uint64_t *aResult)
{
NS_ENSURE_TRUE(aContext, NS_ERROR_FAILURE);
nsCOMPtr<nsIContent> content = do_QueryInterface(aContext);
NS_ENSURE_TRUE(content, NS_ERROR_FAILURE);
nsCOMPtr<nsIDocument> document = content->OwnerDoc();
NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
nsCOMPtr<nsPIDOMWindow> window = document->GetInnerWindow();
NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
*aResult = window->WindowID();
return NS_OK;
}
static nsresult
LogMessage(const nsAString &aMessage, nsIURI* aSourceURI, const nsAString &aSourceSample,
nsISupports* aContext)
{
nsCOMPtr<nsIScriptError> error = do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
NS_ENSURE_TRUE(error, NS_ERROR_OUT_OF_MEMORY);
nsAutoCString sourceName;
nsresult rv = aSourceURI->GetSpec(sourceName);
NS_ENSURE_SUCCESS(rv, rv);
uint64_t windowID = 0;
GetWindowIDFromContext(aContext, &windowID);
rv = error->InitWithWindowID(aMessage, NS_ConvertUTF8toUTF16(sourceName),
aSourceSample, 0, 0, nsIScriptError::errorFlag,
"JavaScript", windowID);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIConsoleService> console = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
NS_ENSURE_TRUE(console, NS_ERROR_OUT_OF_MEMORY);
console->LogMessage(error);
return NS_OK;
}
NS_IMETHODIMP
AddonContentPolicy::ShouldLoad(uint32_t aContentType,
nsIURI* aContentLocation,
nsIURI* aRequestOrigin,
nsISupports* aContext,
const nsACString& aMimeTypeGuess,
nsISupports* aExtra,
nsIPrincipal* aRequestPrincipal,
int16_t* aShouldLoad)
{
MOZ_ASSERT(aContentType == nsContentUtils::InternalContentPolicyTypeToExternal(aContentType),
"We should only see external content policy types here.");
*aShouldLoad = nsIContentPolicy::ACCEPT;
if (!aRequestOrigin) {
return NS_OK;
}
// Only apply this policy to requests from documents loaded from
// moz-extension URLs, or to resources being loaded from moz-extension URLs.
bool equals;
if (!((NS_SUCCEEDED(aContentLocation->SchemeIs("moz-extension", &equals)) && equals) ||
(NS_SUCCEEDED(aRequestOrigin->SchemeIs("moz-extension", &equals)) && equals))) {
return NS_OK;
}
if (aContentType == nsIContentPolicy::TYPE_SCRIPT) {
NS_ConvertUTF8toUTF16 typeString(aMimeTypeGuess);
nsContentTypeParser mimeParser(typeString);
// Reject attempts to load JavaScript scripts with a non-default version.
nsAutoString mimeType, version;
if (NS_SUCCEEDED(mimeParser.GetType(mimeType)) &&
nsContentUtils::IsJavascriptMIMEType(mimeType) &&
NS_SUCCEEDED(mimeParser.GetParameter("version", version))) {
*aShouldLoad = nsIContentPolicy::REJECT_REQUEST;
LogMessage(NS_MULTILINE_LITERAL_STRING(VERSIONED_JS_BLOCKED_MESSAGE),
aRequestOrigin, typeString, aContext);
return NS_OK;
}
}
return NS_OK;
}
NS_IMETHODIMP
AddonContentPolicy::ShouldProcess(uint32_t aContentType,
nsIURI* aContentLocation,
nsIURI* aRequestOrigin,
nsISupports* aRequestingContext,
const nsACString& aMimeTypeGuess,
nsISupports* aExtra,
nsIPrincipal* aRequestPrincipal,
int16_t* aShouldProcess)
{
MOZ_ASSERT(aContentType == nsContentUtils::InternalContentPolicyTypeToExternal(aContentType),
"We should only see external content policy types here.");
*aShouldProcess = nsIContentPolicy::ACCEPT;
return NS_OK;
}

View File

@ -0,0 +1,19 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsIContentPolicy.h"
class AddonContentPolicy : public nsIContentPolicy
{
protected:
virtual ~AddonContentPolicy();
public:
AddonContentPolicy();
NS_DECL_ISUPPORTS
NS_DECL_NSICONTENTPOLICY
};

View File

@ -49,11 +49,13 @@ if CONFIG['MOZ_EM_DEBUG']:
JAR_MANIFESTS += ['jar.mn']
EXPORTS.mozilla += [
'AddonContentPolicy.h',
'AddonPathService.h',
]
UNIFIED_SOURCES += [
'AddonPathService.cpp'
'AddonContentPolicy.cpp',
'AddonPathService.cpp',
]
FINAL_LIBRARY = 'xul'