зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1594234 manifest v3 content security policy support r=robwu,geckoview-reviewers,agi
Implement manifest v3 CSP that is compatible with the current chrome implementation.
Support for content_security_policy.isolated_world (a.k.a. content_security_policy.content_scripts)
has been removed for consistency with
345390adf6
%5E%21/
Differential Revision: https://phabricator.services.mozilla.com/D100573
This commit is contained in:
Родитель
3b83060956
Коммит
4a14410028
|
@ -70,6 +70,7 @@ pref("extensions.geckoProfiler.acceptedExtensionIds", "geckoprofiler@mozilla.com
|
|||
|
||||
// Add-on content security policies.
|
||||
pref("extensions.webextensions.base-content-security-policy", "script-src 'self' https://* moz-extension: blob: filesystem: 'unsafe-eval' 'unsafe-inline'; object-src 'self' https://* moz-extension: blob: filesystem:;");
|
||||
pref("extensions.webextensions.base-content-security-policy.v3", "script-src 'self'; object-src 'self'; style-src 'self'; worker-src 'self';");
|
||||
pref("extensions.webextensions.default-content-security-policy", "script-src 'self'; object-src 'self';");
|
||||
|
||||
pref("extensions.webextensions.remote", true);
|
||||
|
|
|
@ -14,18 +14,17 @@
|
|||
[scriptable, uuid(8a034ef9-9d14-4c5d-8319-06c1ab574baa)]
|
||||
interface nsIAddonPolicyService : nsISupports
|
||||
{
|
||||
/**
|
||||
* Returns the base content security policy, which is applied to all
|
||||
* extension documents, in addition to any custom policies.
|
||||
*/
|
||||
readonly attribute AString baseCSP;
|
||||
|
||||
/**
|
||||
* Returns the default content security policy which applies to extension
|
||||
* documents which do not specify any custom policies.
|
||||
*/
|
||||
readonly attribute AString defaultCSP;
|
||||
|
||||
/**
|
||||
* Returns the base content security policy which applies to all extension resources.
|
||||
*/
|
||||
AString getBaseCSP(in AString aAddonId);
|
||||
|
||||
/**
|
||||
* Returns the content security policy which applies to documents belonging
|
||||
* to the extension with the given ID. This may be either a custom policy,
|
||||
|
@ -33,13 +32,6 @@ interface nsIAddonPolicyService : nsISupports
|
|||
*/
|
||||
AString getExtensionPageCSP(in AString aAddonId);
|
||||
|
||||
/**
|
||||
* Returns the content security policy which applies to content scripts belonging
|
||||
* to the extension with the given ID. This may be either a custom policy,
|
||||
* if one was supplied, or the default policy if one was not.
|
||||
*/
|
||||
AString getContentScriptCSP(in AString aAddonId);
|
||||
|
||||
/**
|
||||
* Returns the generated background page as a data-URI, if any. If the addon
|
||||
* does not have an auto-generated background page, an empty string is
|
||||
|
|
|
@ -3642,10 +3642,7 @@ nsresult Document::InitCSP(nsIChannel* aChannel) {
|
|||
|
||||
// ----- if the doc is an addon, apply its CSP.
|
||||
if (addonPolicy) {
|
||||
nsAutoString extensionPageCSP;
|
||||
Unused << ExtensionPolicyService::GetSingleton().GetBaseCSP(
|
||||
extensionPageCSP);
|
||||
mCSP->AppendPolicy(extensionPageCSP, false, false);
|
||||
mCSP->AppendPolicy(addonPolicy->BaseCSP(), false, false);
|
||||
|
||||
mCSP->AppendPolicy(addonPolicy->ExtensionPageCSP(), false, false);
|
||||
// Bug 1548468: Move CSP off ExpandedPrincipal
|
||||
|
|
|
@ -49,6 +49,21 @@ interface WebExtensionPolicy {
|
|||
[Constant]
|
||||
readonly attribute boolean isPrivileged;
|
||||
|
||||
/**
|
||||
* The manifest version in use by the extension.
|
||||
*/
|
||||
[Constant]
|
||||
readonly attribute unsigned long manifestVersion;
|
||||
|
||||
/**
|
||||
* The base content security policy string to apply on extension
|
||||
* pages for this extension. The baseCSP is specific to the
|
||||
* manifest version. If the manifest version is 3 or higher it
|
||||
* is also applied to content scripts.
|
||||
*/
|
||||
[Constant]
|
||||
readonly attribute DOMString baseCSP;
|
||||
|
||||
/**
|
||||
* The content security policy string to apply to all pages loaded from the
|
||||
* extension's moz-extension: protocol. If one is not provided by the
|
||||
|
@ -58,18 +73,6 @@ interface WebExtensionPolicy {
|
|||
[Constant]
|
||||
readonly attribute DOMString extensionPageCSP;
|
||||
|
||||
/**
|
||||
* The content security policy string to apply to all the content scripts
|
||||
* belonging to the extension. If one is not provided by the
|
||||
* extension the default value from preferences is used.
|
||||
* See extensions.webextensions.default-content-security-policy.
|
||||
*
|
||||
* This is currently disabled, see bug 1578284. Developers may enable it
|
||||
* for testing using extensions.content_script_csp.enabled.
|
||||
*/
|
||||
[Constant]
|
||||
readonly attribute DOMString contentScriptCSP;
|
||||
|
||||
/**
|
||||
* The list of currently-active permissions for the extension, as specified
|
||||
* in its manifest.json file. May be updated to reflect changes in the
|
||||
|
@ -276,8 +279,9 @@ dictionary WebExtensionInit {
|
|||
|
||||
sequence<WebExtensionContentScriptInit> contentScripts = [];
|
||||
|
||||
// The use of a content script csp is determined by the manifest version.
|
||||
unsigned long manifestVersion = 2;
|
||||
DOMString? extensionPageCSP = null;
|
||||
DOMString? contentScriptCSP = null;
|
||||
|
||||
sequence<DOMString>? backgroundScripts = null;
|
||||
DOMString? backgroundWorkerScript = null;
|
||||
|
|
|
@ -1128,6 +1128,11 @@ nsresult ApplyAddonContentScriptCSP(nsISupports* prinOrSop) {
|
|||
if (!addonPolicy) {
|
||||
return NS_OK;
|
||||
}
|
||||
// For backwards compatibility, content scripts have no CSP
|
||||
// in manifest v2. Only apply content script CSP to V3 or later.
|
||||
if (addonPolicy->ManifestVersion() < 3) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsString url;
|
||||
MOZ_TRY_VAR(url, addonPolicy->GetURL(u""_ns));
|
||||
|
@ -1135,9 +1140,7 @@ nsresult ApplyAddonContentScriptCSP(nsISupports* prinOrSop) {
|
|||
nsCOMPtr<nsIURI> selfURI;
|
||||
MOZ_TRY(NS_NewURI(getter_AddRefs(selfURI), url));
|
||||
|
||||
nsAutoString baseCSP;
|
||||
MOZ_ALWAYS_SUCCEEDS(
|
||||
ExtensionPolicyService::GetSingleton().GetBaseCSP(baseCSP));
|
||||
const nsAString& baseCSP = addonPolicy->BaseCSP();
|
||||
|
||||
// If we got here, we're definitly an expanded principal.
|
||||
auto expanded = basePrin->As<ExpandedPrincipal>();
|
||||
|
@ -1167,10 +1170,6 @@ nsresult ApplyAddonContentScriptCSP(nsISupports* prinOrSop) {
|
|||
|
||||
MOZ_TRY(csp->AppendPolicy(baseCSP, reportOnly, false));
|
||||
|
||||
// Set default or extension provided csp.
|
||||
const nsAString& contentScriptCSP = addonPolicy->ContentScriptCSP();
|
||||
MOZ_TRY(csp->AppendPolicy(contentScriptCSP, reportOnly, false));
|
||||
|
||||
expanded->SetCsp(csp);
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -198,6 +198,7 @@ pref("extensions.webextOptionalPermissionPrompts", true);
|
|||
|
||||
// Add-on content security policies.
|
||||
pref("extensions.webextensions.base-content-security-policy", "script-src 'self' https://* moz-extension: blob: filesystem: 'unsafe-eval' 'unsafe-inline'; object-src 'self' https://* moz-extension: blob: filesystem:;");
|
||||
pref("extensions.webextensions.base-content-security-policy.v3", "script-src 'self'; object-src 'self'; style-src 'self'; worker-src 'self';");
|
||||
pref("extensions.webextensions.default-content-security-policy", "script-src 'self'; object-src 'self';");
|
||||
|
||||
pref("extensions.webextensions.background-delayed-startup", true);
|
||||
|
|
|
@ -2182,8 +2182,14 @@ class Extension extends ExtensionData {
|
|||
return manifest;
|
||||
}
|
||||
|
||||
get manifestVersion() {
|
||||
return this.manifest.manifest_version;
|
||||
}
|
||||
|
||||
get extensionPageCSP() {
|
||||
const { content_security_policy } = this.manifest;
|
||||
// While only manifest v3 should contain an object,
|
||||
// we'll remain lenient here.
|
||||
if (
|
||||
content_security_policy &&
|
||||
typeof content_security_policy === "object"
|
||||
|
@ -2193,19 +2199,6 @@ class Extension extends ExtensionData {
|
|||
return content_security_policy;
|
||||
}
|
||||
|
||||
get contentScriptCSP() {
|
||||
let { content_security_policy } = this.manifest;
|
||||
if (
|
||||
content_security_policy &&
|
||||
typeof content_security_policy === "object"
|
||||
) {
|
||||
return (
|
||||
content_security_policy.content_scripts ||
|
||||
content_security_policy.isolated_world
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
get backgroundScripts() {
|
||||
return this.manifest.background?.scripts;
|
||||
}
|
||||
|
@ -2234,8 +2227,8 @@ class Extension extends ExtensionData {
|
|||
id: this.id,
|
||||
uuid: this.uuid,
|
||||
name: this.name,
|
||||
manifestVersion: this.manifestVersion,
|
||||
extensionPageCSP: this.extensionPageCSP,
|
||||
contentScriptCSP: this.contentScriptCSP,
|
||||
instanceId: this.instanceId,
|
||||
resourceURL: this.resourceURL,
|
||||
contentScripts: this.contentScripts,
|
||||
|
|
|
@ -47,12 +47,6 @@ using dom::ContentFrameMessageManager;
|
|||
using dom::Document;
|
||||
using dom::Promise;
|
||||
|
||||
#define BASE_CSP_PREF "extensions.webextensions.base-content-security-policy"
|
||||
#define DEFAULT_BASE_CSP \
|
||||
"script-src 'self' https://* moz-extension: blob: filesystem: " \
|
||||
"'unsafe-eval' 'unsafe-inline'; " \
|
||||
"object-src 'self' https://* moz-extension: blob: filesystem:;"
|
||||
|
||||
#define DEFAULT_CSP_PREF \
|
||||
"extensions.webextensions.default-content-security-policy"
|
||||
#define DEFAULT_DEFAULT_CSP "script-src 'self'; object-src 'self';"
|
||||
|
@ -94,7 +88,6 @@ ExtensionPolicyService::ExtensionPolicyService() {
|
|||
mObs = services::GetObserverService();
|
||||
MOZ_RELEASE_ASSERT(mObs);
|
||||
|
||||
mBaseCSP.SetIsVoid(true);
|
||||
mDefaultCSP.SetIsVoid(true);
|
||||
|
||||
RegisterObservers();
|
||||
|
@ -228,7 +221,6 @@ void ExtensionPolicyService::RegisterObservers() {
|
|||
mObs->AddObserver(this, "document-on-opening-request", false);
|
||||
}
|
||||
|
||||
Preferences::AddStrongObserver(this, BASE_CSP_PREF);
|
||||
Preferences::AddStrongObserver(this, DEFAULT_CSP_PREF);
|
||||
}
|
||||
|
||||
|
@ -240,7 +232,6 @@ void ExtensionPolicyService::UnregisterObservers() {
|
|||
mObs->RemoveObserver(this, "document-on-opening-request");
|
||||
}
|
||||
|
||||
Preferences::RemoveObserver(this, BASE_CSP_PREF);
|
||||
Preferences::RemoveObserver(this, DEFAULT_CSP_PREF);
|
||||
}
|
||||
|
||||
|
@ -268,9 +259,7 @@ nsresult ExtensionPolicyService::Observe(nsISupports* aSubject,
|
|||
} else if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
|
||||
const nsCString converted = NS_ConvertUTF16toUTF8(aData);
|
||||
const char* pref = converted.get();
|
||||
if (!strcmp(pref, BASE_CSP_PREF)) {
|
||||
mBaseCSP.SetIsVoid(true);
|
||||
} else if (!strcmp(pref, DEFAULT_CSP_PREF)) {
|
||||
if (!strcmp(pref, DEFAULT_CSP_PREF)) {
|
||||
mDefaultCSP.SetIsVoid(true);
|
||||
}
|
||||
}
|
||||
|
@ -550,19 +539,6 @@ void ExtensionPolicyService::CheckContentScripts(const DocInfo& aDocInfo,
|
|||
* nsIAddonPolicyService
|
||||
*****************************************************************************/
|
||||
|
||||
nsresult ExtensionPolicyService::GetBaseCSP(nsAString& aBaseCSP) {
|
||||
if (mBaseCSP.IsVoid()) {
|
||||
nsresult rv = Preferences::GetString(BASE_CSP_PREF, mBaseCSP);
|
||||
if (NS_FAILED(rv)) {
|
||||
mBaseCSP.AssignLiteral(DEFAULT_BASE_CSP);
|
||||
}
|
||||
mBaseCSP.SetIsVoid(false);
|
||||
}
|
||||
|
||||
aBaseCSP.Assign(mBaseCSP);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult ExtensionPolicyService::GetDefaultCSP(nsAString& aDefaultCSP) {
|
||||
if (mDefaultCSP.IsVoid()) {
|
||||
nsresult rv = Preferences::GetString(DEFAULT_CSP_PREF, mDefaultCSP);
|
||||
|
@ -576,19 +552,19 @@ nsresult ExtensionPolicyService::GetDefaultCSP(nsAString& aDefaultCSP) {
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult ExtensionPolicyService::GetExtensionPageCSP(const nsAString& aAddonId,
|
||||
nsAString& aResult) {
|
||||
nsresult ExtensionPolicyService::GetBaseCSP(const nsAString& aAddonId,
|
||||
nsAString& aResult) {
|
||||
if (WebExtensionPolicy* policy = GetByID(aAddonId)) {
|
||||
policy->GetExtensionPageCSP(aResult);
|
||||
policy->GetBaseCSP(aResult);
|
||||
return NS_OK;
|
||||
}
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
nsresult ExtensionPolicyService::GetContentScriptCSP(const nsAString& aAddonId,
|
||||
nsresult ExtensionPolicyService::GetExtensionPageCSP(const nsAString& aAddonId,
|
||||
nsAString& aResult) {
|
||||
if (WebExtensionPolicy* policy = GetByID(aAddonId)) {
|
||||
policy->GetContentScriptCSP(aResult);
|
||||
policy->GetExtensionPageCSP(aResult);
|
||||
return NS_OK;
|
||||
}
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
|
|
|
@ -123,7 +123,6 @@ class ExtensionPolicyService final : public nsIAddonPolicyService,
|
|||
|
||||
nsCOMPtr<nsIObserverService> mObs;
|
||||
|
||||
nsString mBaseCSP;
|
||||
nsString mDefaultCSP;
|
||||
};
|
||||
|
||||
|
|
|
@ -222,8 +222,8 @@ ExtensionManager = {
|
|||
allowedOrigins: extension.allowedOrigins,
|
||||
webAccessibleResources: extension.webAccessibleResources,
|
||||
|
||||
manifestVersion: extension.manifestVersion,
|
||||
extensionPageCSP: extension.extensionPageCSP,
|
||||
contentScriptCSP: extension.contentScriptCSP,
|
||||
|
||||
localizeCallback,
|
||||
|
||||
|
|
|
@ -290,6 +290,19 @@ const POSTPROCESSORS = {
|
|||
context.logError(context.makeError(msg));
|
||||
throw new Error(msg);
|
||||
},
|
||||
|
||||
manifestVersionCheck(value, context) {
|
||||
if (
|
||||
value == 2 ||
|
||||
(value == 3 &&
|
||||
Services.prefs.getBoolPref("extensions.manifestV3.enabled", false))
|
||||
) {
|
||||
return value;
|
||||
}
|
||||
const msg = `Unsupported manifest version: ${value}`;
|
||||
context.logError(context.makeError(msg));
|
||||
throw new Error(msg);
|
||||
},
|
||||
};
|
||||
|
||||
// Parses a regular expression, with support for the Python extended
|
||||
|
@ -1088,11 +1101,11 @@ const FORMATS = {
|
|||
contentSecurityPolicy(string, context) {
|
||||
let error = contentPolicyService.validateAddonCSP(string);
|
||||
if (error != null) {
|
||||
// The SyntaxError raised below is not reported as part of the "choices" error message,
|
||||
// The CSP validation error is not reported as part of the "choices" error message,
|
||||
// we log the CSP validation error explicitly here to make it easier for the addon developers
|
||||
// to see and fix the extension CSP.
|
||||
context.logError(`Error processing ${context.currentTarget}: ${error}`);
|
||||
throw new SyntaxError(error);
|
||||
return null;
|
||||
}
|
||||
return string;
|
||||
},
|
||||
|
|
|
@ -41,6 +41,18 @@ static const char kBackgroundPageHTMLEnd[] =
|
|||
</body>\n\
|
||||
</html>";
|
||||
|
||||
#define BASE_CSP_PREF_V2 "extensions.webextensions.base-content-security-policy"
|
||||
#define DEFAULT_BASE_CSP_V2 \
|
||||
"script-src 'self' https://* moz-extension: blob: filesystem: " \
|
||||
"'unsafe-eval' 'unsafe-inline'; " \
|
||||
"object-src 'self' https://* moz-extension: blob: filesystem:;"
|
||||
|
||||
#define BASE_CSP_PREF_V3 \
|
||||
"extensions.webextensions.base-content-security-policy.v3"
|
||||
#define DEFAULT_BASE_CSP_V3 \
|
||||
"script-src 'self'; object-src 'self'; " \
|
||||
"style-src 'self'; worker-src 'self';"
|
||||
|
||||
static const char kRestrictedDomainPref[] =
|
||||
"extensions.webextensions.restrictedDomains";
|
||||
|
||||
|
@ -133,8 +145,8 @@ WebExtensionPolicy::WebExtensionPolicy(GlobalObject& aGlobal,
|
|||
: mId(NS_AtomizeMainThread(aInit.mId)),
|
||||
mHostname(aInit.mMozExtensionHostname),
|
||||
mName(aInit.mName),
|
||||
mManifestVersion(aInit.mManifestVersion),
|
||||
mExtensionPageCSP(aInit.mExtensionPageCSP),
|
||||
mContentScriptCSP(aInit.mContentScriptCSP),
|
||||
mLocalizeCallback(aInit.mLocalizeCallback),
|
||||
mIsPrivileged(aInit.mIsPrivileged),
|
||||
mPermissions(new AtomSet(aInit.mPermissions)) {
|
||||
|
@ -165,14 +177,12 @@ WebExtensionPolicy::WebExtensionPolicy(GlobalObject& aGlobal,
|
|||
mBackgroundWorkerScript.Assign(aInit.mBackgroundWorkerScript);
|
||||
}
|
||||
|
||||
InitializeBaseCSP();
|
||||
|
||||
if (mExtensionPageCSP.IsVoid()) {
|
||||
EPS().GetDefaultCSP(mExtensionPageCSP);
|
||||
}
|
||||
|
||||
if (mContentScriptCSP.IsVoid()) {
|
||||
EPS().GetDefaultCSP(mContentScriptCSP);
|
||||
}
|
||||
|
||||
mContentScripts.SetCapacity(aInit.mContentScripts.Length());
|
||||
for (const auto& scriptInit : aInit.mContentScripts) {
|
||||
// The activeTab permission is only for dynamically injected scripts,
|
||||
|
@ -210,6 +220,21 @@ already_AddRefed<WebExtensionPolicy> WebExtensionPolicy::Constructor(
|
|||
return policy.forget();
|
||||
}
|
||||
|
||||
void WebExtensionPolicy::InitializeBaseCSP() {
|
||||
if (mManifestVersion < 3) {
|
||||
nsresult rv = Preferences::GetString(BASE_CSP_PREF_V2, mBaseCSP);
|
||||
if (NS_FAILED(rv)) {
|
||||
mBaseCSP.AssignLiteral(DEFAULT_BASE_CSP_V2);
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Version 3 or higher.
|
||||
nsresult rv = Preferences::GetString(BASE_CSP_PREF_V3, mBaseCSP);
|
||||
if (NS_FAILED(rv)) {
|
||||
mBaseCSP.AssignLiteral(DEFAULT_BASE_CSP_V3);
|
||||
}
|
||||
}
|
||||
|
||||
/* static */
|
||||
void WebExtensionPolicy::GetActiveExtensions(
|
||||
dom::GlobalObject& aGlobal,
|
||||
|
|
|
@ -99,11 +99,13 @@ class WebExtensionPolicy final : public nsISupports,
|
|||
const nsString& Name() const { return mName; }
|
||||
void GetName(nsAString& aName) const { aName = mName; }
|
||||
|
||||
uint32_t ManifestVersion() const { return mManifestVersion; }
|
||||
|
||||
const nsString& ExtensionPageCSP() const { return mExtensionPageCSP; }
|
||||
void GetExtensionPageCSP(nsAString& aCSP) const { aCSP = mExtensionPageCSP; }
|
||||
|
||||
const nsString& ContentScriptCSP() const { return mContentScriptCSP; }
|
||||
void GetContentScriptCSP(nsAString& aCSP) const { aCSP = mContentScriptCSP; }
|
||||
const nsString& BaseCSP() const { return mBaseCSP; }
|
||||
void GetBaseCSP(nsAString& aCSP) const { aCSP = mBaseCSP; }
|
||||
|
||||
already_AddRefed<MatchPatternSet> AllowedOrigins() {
|
||||
return do_AddRef(mHostPermissions);
|
||||
|
@ -180,6 +182,7 @@ class WebExtensionPolicy final : public nsISupports,
|
|||
|
||||
bool Enable();
|
||||
bool Disable();
|
||||
void InitializeBaseCSP();
|
||||
|
||||
nsCOMPtr<nsISupports> mParent;
|
||||
|
||||
|
@ -188,8 +191,9 @@ class WebExtensionPolicy final : public nsISupports,
|
|||
nsCOMPtr<nsIURI> mBaseURI;
|
||||
|
||||
nsString mName;
|
||||
uint32_t mManifestVersion = 2;
|
||||
nsString mExtensionPageCSP;
|
||||
nsString mContentScriptCSP;
|
||||
nsString mBaseCSP;
|
||||
|
||||
uint64_t mBrowsingContextGroupId = 0;
|
||||
|
||||
|
|
|
@ -11,7 +11,8 @@
|
|||
"manifest_version": {
|
||||
"type": "integer",
|
||||
"minimum": 2,
|
||||
"maximum": 2
|
||||
"maximum": 3,
|
||||
"postprocess": "manifestVersionCheck"
|
||||
},
|
||||
|
||||
"applications": {
|
||||
|
@ -203,18 +204,6 @@
|
|||
"optional": true,
|
||||
"format": "contentSecurityPolicy",
|
||||
"description": "The Content Security Policy used for extension pages."
|
||||
},
|
||||
"content_scripts": {
|
||||
"type": "string",
|
||||
"optional": true,
|
||||
"format": "contentSecurityPolicy",
|
||||
"description": "The Content Security Policy used for content scripts."
|
||||
},
|
||||
"isolated_world": {
|
||||
"type": "string",
|
||||
"optional": true,
|
||||
"format": "contentSecurityPolicy",
|
||||
"description": "An alias for content_scripts to support Chrome compatibility. Content Security Policy implementations may differ between Firefox and Chrome. If both isolated_world and content_scripts exist, the value from content_scripts will be used."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,39 +12,27 @@ const aps = Cc["@mozilla.org/addons/policy-service;1"].getService(
|
|||
Ci.nsIAddonPolicyService
|
||||
);
|
||||
|
||||
let policy = null;
|
||||
const v2_csp = Preferences.get(
|
||||
"extensions.webextensions.base-content-security-policy"
|
||||
);
|
||||
const v3_csp = Preferences.get(
|
||||
"extensions.webextensions.base-content-security-policy.v3"
|
||||
);
|
||||
|
||||
function setExtensionCSP(csp) {
|
||||
if (policy) {
|
||||
policy.active = false;
|
||||
}
|
||||
|
||||
policy = new WebExtensionPolicy({
|
||||
id: ADDON_ID,
|
||||
mozExtensionHostname: ADDON_ID,
|
||||
baseURL: "file:///",
|
||||
|
||||
allowedOrigins: new MatchPatternSet([]),
|
||||
localizeCallback() {},
|
||||
|
||||
extensionPageCSP: csp,
|
||||
contentScriptCSP: csp,
|
||||
});
|
||||
|
||||
policy.active = true;
|
||||
}
|
||||
|
||||
registerCleanupFunction(() => {
|
||||
policy.active = false;
|
||||
add_task(async function test_invalid_addon_csp() {
|
||||
await Assert.throws(
|
||||
() => aps.getBaseCSP("invalid@missing"),
|
||||
/NS_ERROR_ILLEGAL_VALUE/,
|
||||
"no base csp for non-existent addon"
|
||||
);
|
||||
await Assert.throws(
|
||||
() => aps.getExtensionPageCSP("invalid@missing"),
|
||||
/NS_ERROR_ILLEGAL_VALUE/,
|
||||
"no extension page csp for non-existent addon"
|
||||
);
|
||||
});
|
||||
|
||||
add_task(async function test_addon_csp() {
|
||||
equal(
|
||||
aps.baseCSP,
|
||||
Preferences.get("extensions.webextensions.base-content-security-policy"),
|
||||
"Expected base CSP value"
|
||||
);
|
||||
|
||||
add_task(async function test_policy_csp() {
|
||||
equal(
|
||||
aps.defaultCSP,
|
||||
Preferences.get("extensions.webextensions.default-content-security-policy"),
|
||||
|
@ -54,101 +42,177 @@ add_task(async function test_addon_csp() {
|
|||
const CUSTOM_POLICY =
|
||||
"script-src: 'self' https://xpcshell.test.custom.csp; object-src: 'none'";
|
||||
|
||||
setExtensionCSP(CUSTOM_POLICY);
|
||||
let tests = [
|
||||
{
|
||||
name: "manifest version 2, no custom policy",
|
||||
policyData: {},
|
||||
expectedPolicy: aps.defaultCSP,
|
||||
},
|
||||
{
|
||||
name: "manifest version 2, no custom policy",
|
||||
policyData: {
|
||||
manifestVersion: 2,
|
||||
},
|
||||
expectedPolicy: aps.defaultCSP,
|
||||
},
|
||||
{
|
||||
name: "version 2 custom extension policy",
|
||||
policyData: {
|
||||
extensionPageCSP: CUSTOM_POLICY,
|
||||
},
|
||||
expectedPolicy: CUSTOM_POLICY,
|
||||
},
|
||||
{
|
||||
name: "manifest version 2 set, custom extension policy",
|
||||
policyData: {
|
||||
manifestVersion: 2,
|
||||
extensionPageCSP: CUSTOM_POLICY,
|
||||
},
|
||||
expectedPolicy: CUSTOM_POLICY,
|
||||
},
|
||||
{
|
||||
name: "manifest version 3, no custom policy",
|
||||
policyData: {
|
||||
manifestVersion: 3,
|
||||
},
|
||||
expectedPolicy: aps.defaultCSP,
|
||||
},
|
||||
{
|
||||
name: "manifest 3 version set, custom extensionPage policy",
|
||||
policyData: {
|
||||
manifestVersion: 3,
|
||||
extensionPageCSP: CUSTOM_POLICY,
|
||||
},
|
||||
expectedPolicy: CUSTOM_POLICY,
|
||||
},
|
||||
];
|
||||
|
||||
equal(
|
||||
aps.getExtensionPageCSP(ADDON_ID),
|
||||
CUSTOM_POLICY,
|
||||
"CSP should point to add-on's custom extension page policy"
|
||||
);
|
||||
let policy = null;
|
||||
|
||||
equal(
|
||||
aps.getContentScriptCSP(ADDON_ID),
|
||||
CUSTOM_POLICY,
|
||||
"CSP should point to add-on's custom content script policy"
|
||||
);
|
||||
function setExtensionCSP({ manifestVersion, extensionPageCSP }) {
|
||||
if (policy) {
|
||||
policy.active = false;
|
||||
}
|
||||
|
||||
setExtensionCSP(null);
|
||||
policy = new WebExtensionPolicy({
|
||||
id: ADDON_ID,
|
||||
mozExtensionHostname: ADDON_ID,
|
||||
baseURL: "file:///",
|
||||
|
||||
equal(
|
||||
aps.getExtensionPageCSP(ADDON_ID),
|
||||
aps.defaultCSP,
|
||||
"extension page CSP should be default when set to null"
|
||||
);
|
||||
allowedOrigins: new MatchPatternSet([]),
|
||||
localizeCallback() {},
|
||||
|
||||
equal(
|
||||
aps.getContentScriptCSP(ADDON_ID),
|
||||
aps.defaultCSP,
|
||||
"content script CSP should be default when set to null"
|
||||
);
|
||||
manifestVersion,
|
||||
extensionPageCSP,
|
||||
});
|
||||
|
||||
policy.active = true;
|
||||
}
|
||||
|
||||
for (let test of tests) {
|
||||
info(test.name);
|
||||
setExtensionCSP(test.policyData);
|
||||
equal(
|
||||
aps.getBaseCSP(ADDON_ID),
|
||||
test.policyData.manifestVersion == 3 ? v3_csp : v2_csp,
|
||||
"baseCSP is correct"
|
||||
);
|
||||
equal(
|
||||
aps.getExtensionPageCSP(ADDON_ID),
|
||||
test.expectedPolicy,
|
||||
"extensionPageCSP is correct"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
add_task(async function test_invalid_csp() {
|
||||
let defaultPolicy = Preferences.get(
|
||||
"extensions.webextensions.default-content-security-policy"
|
||||
);
|
||||
add_task(async function test_extension_csp() {
|
||||
Services.prefs.setBoolPref("extensions.manifestV3.enabled", true);
|
||||
|
||||
ExtensionTestUtils.failOnSchemaWarnings(false);
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
manifest: {
|
||||
content_security_policy: {
|
||||
extension_pages: `script-src 'none'`,
|
||||
content_scripts: `script-src 'none'`,
|
||||
|
||||
let extension_pages = "script-src 'self'; object-src 'none'; img-src 'none'";
|
||||
|
||||
let tests = [
|
||||
{
|
||||
name: "manifest_v2 invalid csp results in default csp used",
|
||||
manifest: {
|
||||
content_security_policy: `script-src 'none'`,
|
||||
},
|
||||
expectedPolicy: aps.defaultCSP,
|
||||
},
|
||||
});
|
||||
await extension.startup();
|
||||
let policy = WebExtensionPolicy.getByID(extension.id);
|
||||
equal(
|
||||
policy.extensionPageCSP,
|
||||
defaultPolicy,
|
||||
"csp is default when invalid csp is provided."
|
||||
);
|
||||
equal(
|
||||
policy.contentScriptCSP,
|
||||
defaultPolicy,
|
||||
"csp is default when invalid csp is provided."
|
||||
);
|
||||
await extension.unload();
|
||||
{
|
||||
name: "manifest_v3 invalid csp results in default csp used",
|
||||
manifest: {
|
||||
manifest_version: 3,
|
||||
content_security_policy: {
|
||||
extension_pages: `script-src 'none'`,
|
||||
},
|
||||
},
|
||||
expectedPolicy: aps.defaultCSP,
|
||||
},
|
||||
{
|
||||
name: "manifest_v2 csp",
|
||||
manifest: {
|
||||
manifest_version: 2,
|
||||
content_security_policy: extension_pages,
|
||||
},
|
||||
expectedPolicy: extension_pages,
|
||||
},
|
||||
{
|
||||
name: "manifest_v2 with no csp, expect default",
|
||||
manifest: {
|
||||
manifest_version: 2,
|
||||
},
|
||||
expectedPolicy: aps.defaultCSP,
|
||||
},
|
||||
{
|
||||
name: "manifest_v3 used with no csp, expect default",
|
||||
manifest: {
|
||||
manifest_version: 3,
|
||||
},
|
||||
expectedPolicy: aps.defaultCSP,
|
||||
},
|
||||
{
|
||||
name: "manifest_v3 used with v2 syntax",
|
||||
manifest: {
|
||||
manifest_version: 3,
|
||||
content_security_policy: extension_pages,
|
||||
},
|
||||
expectedPolicy: extension_pages,
|
||||
},
|
||||
{
|
||||
name: "manifest_v3 syntax used",
|
||||
manifest: {
|
||||
manifest_version: 3,
|
||||
content_security_policy: {
|
||||
extension_pages,
|
||||
},
|
||||
},
|
||||
expectedPolicy: extension_pages,
|
||||
},
|
||||
];
|
||||
|
||||
for (let test of tests) {
|
||||
info(test.name);
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
manifest: test.manifest,
|
||||
});
|
||||
await extension.startup();
|
||||
let policy = WebExtensionPolicy.getByID(extension.id);
|
||||
equal(
|
||||
policy.baseCSP,
|
||||
test.manifest.manifest_version == 3 ? v3_csp : v2_csp,
|
||||
"baseCSP is correct"
|
||||
);
|
||||
equal(
|
||||
policy.extensionPageCSP,
|
||||
test.expectedPolicy,
|
||||
"extensionPageCSP is correct."
|
||||
);
|
||||
await extension.unload();
|
||||
}
|
||||
|
||||
ExtensionTestUtils.failOnSchemaWarnings(true);
|
||||
});
|
||||
|
||||
add_task(async function test_isolated_world() {
|
||||
const test_policy = "script-src 'self'; object-src 'none'; img-src 'none'";
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
manifest: {
|
||||
content_security_policy: {
|
||||
isolated_world: test_policy,
|
||||
},
|
||||
},
|
||||
});
|
||||
await extension.startup();
|
||||
let policy = WebExtensionPolicy.getByID(extension.id);
|
||||
equal(
|
||||
policy.contentScriptCSP,
|
||||
test_policy,
|
||||
"csp is is correct when using isolated_world."
|
||||
);
|
||||
await extension.unload();
|
||||
});
|
||||
|
||||
// If both isolated_world and content_scripts is provided, content_scripts is used.
|
||||
add_task(async function test_isolated_world_overridden() {
|
||||
const test_policy =
|
||||
"script-src 'self'; object-src 'none'; img-src https://xpcshell.test.custom.csp";
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
manifest: {
|
||||
content_security_policy: {
|
||||
content_scripts: test_policy,
|
||||
isolated_world: "script-src 'self'; object-src 'none'; img-src 'none'",
|
||||
},
|
||||
},
|
||||
});
|
||||
await extension.startup();
|
||||
let policy = WebExtensionPolicy.getByID(extension.id);
|
||||
equal(
|
||||
policy.contentScriptCSP,
|
||||
test_policy,
|
||||
"csp is is correct when using isolated_world and content_scripts."
|
||||
);
|
||||
await extension.unload();
|
||||
Services.prefs.clearUserPref("extensions.manifestV3.enabled");
|
||||
});
|
||||
|
|
|
@ -9,6 +9,7 @@ const { TestUtils } = ChromeUtils.import(
|
|||
// Enable and turn off report-only so we can validate the results.
|
||||
Services.prefs.setBoolPref("extensions.content_script_csp.enabled", true);
|
||||
Services.prefs.setBoolPref("extensions.content_script_csp.report_only", false);
|
||||
Services.prefs.setBoolPref("extensions.manifestV3.enabled", true);
|
||||
|
||||
const server = createHttpServer({
|
||||
hosts: ["example.com", "csplog.example.net"],
|
||||
|
@ -42,7 +43,6 @@ const BASE_URL = `http://example.com`;
|
|||
const pageURL = `${BASE_URL}/plain.html`;
|
||||
|
||||
const CSP_REPORT_PATH = "/csp-report.sjs";
|
||||
const CSP_REPORT = `report-uri http://csplog.example.net${CSP_REPORT_PATH};`;
|
||||
|
||||
function readUTF8InputStream(stream) {
|
||||
let buffer = NetUtil.readInputStream(stream, stream.available());
|
||||
|
@ -107,6 +107,21 @@ async function testFunction(data = {}) {
|
|||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
function testScriptTag(data) {
|
||||
return new Promise(resolve => {
|
||||
let script = document.createElement("script");
|
||||
script.src = data.url;
|
||||
script.onload = () => {
|
||||
resolve(true);
|
||||
};
|
||||
script.onerror = () => {
|
||||
resolve(false);
|
||||
};
|
||||
document.body.appendChild(script);
|
||||
});
|
||||
}
|
||||
|
||||
// If the violation source is the extension the securitypolicyviolation event is not fired.
|
||||
// If the page is the source, the event is fired and both the content script or page scripts
|
||||
// will receive the event. If we're expecting a moz-extension report we'll fail in the
|
||||
|
@ -128,9 +143,6 @@ function contentScript(report) {
|
|||
});
|
||||
}
|
||||
|
||||
const gDefaultContentScriptCSP =
|
||||
"default-src 'self' 'report-sample'; object-src 'self'; script-src 'self';";
|
||||
|
||||
let TESTS = [
|
||||
// Image Tests
|
||||
{
|
||||
|
@ -141,20 +153,6 @@ let TESTS = [
|
|||
data: { image_url: `${BASE_URL}/data/file_image_good.png` },
|
||||
expect: true,
|
||||
},
|
||||
{
|
||||
description:
|
||||
"Image from content script using extension csp. Image is not allowed.",
|
||||
pageCSP: `${gDefaultCSP} img-src 'self';`,
|
||||
scriptCSP: `${gDefaultContentScriptCSP} img-src 'none';`,
|
||||
script: testImage,
|
||||
data: { image_url: `${BASE_URL}/data/file_image_good.png` },
|
||||
expect: false,
|
||||
report: {
|
||||
"blocked-uri": `${BASE_URL}/data/file_image_good.png`,
|
||||
"document-uri": "moz-extension",
|
||||
"violated-directive": "img-src",
|
||||
},
|
||||
},
|
||||
// Fetch Tests
|
||||
{
|
||||
description: "Fetch url in content script uses default extension csp.",
|
||||
|
@ -163,19 +161,6 @@ let TESTS = [
|
|||
data: { url: `${BASE_URL}/data/file_image_good.png` },
|
||||
expect: true,
|
||||
},
|
||||
{
|
||||
description: "Fetch url in content script uses extension csp.",
|
||||
pageCSP: `${gDefaultCSP} connect-src 'none';`,
|
||||
script: testFetch,
|
||||
scriptCSP: `${gDefaultContentScriptCSP} connect-src 'none';`,
|
||||
data: { url: `${BASE_URL}/data/file_image_good.png` },
|
||||
expect: false,
|
||||
report: {
|
||||
"blocked-uri": `${BASE_URL}/data/file_image_good.png`,
|
||||
"document-uri": "moz-extension",
|
||||
"violated-directive": "connect-src",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Fetch full url from content script uses page csp.",
|
||||
pageCSP: `${gDefaultCSP} connect-src 'none';`,
|
||||
|
@ -195,7 +180,7 @@ let TESTS = [
|
|||
description: "Fetch url from content script uses page csp.",
|
||||
pageCSP: `${gDefaultCSP} connect-src *;`,
|
||||
script: testFetch,
|
||||
scriptCSP: `${gDefaultContentScriptCSP} connect-src 'none' 'report-sample';`,
|
||||
version: 3,
|
||||
data: {
|
||||
content: true,
|
||||
url: `${BASE_URL}/data/file_image_good.png`,
|
||||
|
@ -203,7 +188,7 @@ let TESTS = [
|
|||
expect: true,
|
||||
},
|
||||
|
||||
// TODO Bug 1587939: Eval tests.
|
||||
// Eval tests.
|
||||
{
|
||||
description: "Eval from content script uses page csp with unsafe-eval.",
|
||||
pageCSP: `default-src 'none'; script-src 'unsafe-eval';`,
|
||||
|
@ -214,7 +199,7 @@ let TESTS = [
|
|||
{
|
||||
description: "Eval from content script uses page csp.",
|
||||
pageCSP: `default-src 'self' 'report-sample'; script-src 'self';`,
|
||||
scriptCSP: `object-src 'self'; script-src 'self' 'unsafe-eval';`,
|
||||
version: 3,
|
||||
script: testEval,
|
||||
data: { content: true },
|
||||
expect: false,
|
||||
|
@ -225,18 +210,40 @@ let TESTS = [
|
|||
},
|
||||
},
|
||||
{
|
||||
description: "Eval in content script uses extension csp.",
|
||||
description: "Eval in content script allowed by v2 csp.",
|
||||
pageCSP: `script-src 'self' 'unsafe-eval';`,
|
||||
script: testEval,
|
||||
expect: true,
|
||||
},
|
||||
{
|
||||
description: "Eval in content script disallowed by v3 csp.",
|
||||
pageCSP: `script-src 'self' 'unsafe-eval';`,
|
||||
version: 3,
|
||||
script: testEval,
|
||||
expect: false,
|
||||
},
|
||||
{
|
||||
description: "Eval in content script uses extension csp. unsafe-eval",
|
||||
pageCSP: `default-src 'self'; script-src 'self';`,
|
||||
scriptCSP: `object-src 'self'; script-src 'self' 'unsafe-eval';`,
|
||||
script: testEval,
|
||||
description: "Wrapped Eval in content script uses page csp.",
|
||||
pageCSP: `script-src 'self' 'unsafe-eval';`,
|
||||
version: 3,
|
||||
script: async () => {
|
||||
return window.wrappedJSObject.eval("true");
|
||||
},
|
||||
expect: true,
|
||||
},
|
||||
{
|
||||
description: "Wrapped Eval in content script denied by page csp.",
|
||||
pageCSP: `script-src 'self';`,
|
||||
version: 3,
|
||||
script: async () => {
|
||||
try {
|
||||
return window.wrappedJSObject.eval("true");
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
expect: false,
|
||||
},
|
||||
|
||||
{
|
||||
description: "Function from content script uses page csp.",
|
||||
|
@ -248,7 +255,7 @@ let TESTS = [
|
|||
{
|
||||
description: "Function from content script uses page csp.",
|
||||
pageCSP: `default-src 'self' 'report-sample'; script-src 'self';`,
|
||||
scriptCSP: `object-src 'self'; script-src 'self' 'unsafe-eval';`,
|
||||
version: 3,
|
||||
script: testFunction,
|
||||
data: { content: true },
|
||||
expect: 0,
|
||||
|
@ -259,18 +266,34 @@ let TESTS = [
|
|||
},
|
||||
},
|
||||
{
|
||||
description: "Function in content script uses default extension csp.",
|
||||
description: "Function in content script uses extension csp.",
|
||||
pageCSP: `default-src 'self'; script-src 'self' 'unsafe-eval';`,
|
||||
version: 3,
|
||||
script: testFunction,
|
||||
expect: 0,
|
||||
},
|
||||
|
||||
// The javascript url tests are not included as we do not execute those,
|
||||
// aparently even with the urlbar filtering pref flipped.
|
||||
// (browser.urlbar.filter.javascript)
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=866522
|
||||
|
||||
// script tag injection tests
|
||||
{
|
||||
description:
|
||||
"Function in content script uses extension csp, with unsafe-eval",
|
||||
pageCSP: `default-src 'self'; script-src 'self';`,
|
||||
scriptCSP: `default-src 'self'; object-src 'self'; script-src 'self' 'unsafe-eval';`,
|
||||
script: testFunction,
|
||||
expect: 2,
|
||||
description: "remote script in content script passes in v2",
|
||||
version: 2,
|
||||
pageCSP: "script-src http://example.com:*;",
|
||||
script: testScriptTag,
|
||||
data: { url: `${BASE_URL}/data/file_script_good.js` },
|
||||
expect: true,
|
||||
},
|
||||
{
|
||||
description: "remote script in content script fails in v3",
|
||||
version: 3,
|
||||
pageCSP: "script-src http://example.com:*;",
|
||||
script: testScriptTag,
|
||||
data: { url: `${BASE_URL}/data/file_script_good.js` },
|
||||
expect: false,
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -279,6 +302,7 @@ async function runCSPTest(test) {
|
|||
gCSP = `${test.pageCSP || gDefaultCSP} report-uri ${CSP_REPORT_PATH}`;
|
||||
let data = {
|
||||
manifest: {
|
||||
manifest_version: test.version || 2,
|
||||
content_scripts: [
|
||||
{
|
||||
matches: ["http://*/plain.html"],
|
||||
|
@ -300,17 +324,14 @@ async function runCSPTest(test) {
|
|||
`,
|
||||
},
|
||||
};
|
||||
if (test.scriptCSP) {
|
||||
data.manifest.content_security_policy = {
|
||||
content_scripts: `${test.scriptCSP} ${CSP_REPORT}`,
|
||||
};
|
||||
}
|
||||
|
||||
let extension = ExtensionTestUtils.loadExtension(data);
|
||||
await extension.startup();
|
||||
|
||||
let reportPromise = test.report && promiseCSPReport();
|
||||
let contentPage = await ExtensionTestUtils.loadContentPage(pageURL);
|
||||
|
||||
info(`running: ${test.description}`);
|
||||
await extension.awaitMessage("violationEvent");
|
||||
let result = await extension.awaitMessage("result");
|
||||
equal(result, test.expect, test.description);
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
/* vim: set sts=2 sw=2 et tw=80: */
|
||||
"use strict";
|
||||
|
||||
Services.prefs.setBoolPref("extensions.manifestV3.enabled", true);
|
||||
|
||||
add_task(async function test_manifest_csp() {
|
||||
let normalized = await ExtensionTestUtils.normalizeManifest({
|
||||
content_security_policy: "script-src 'self'; object-src 'none'",
|
||||
|
@ -27,7 +29,6 @@ add_task(async function test_manifest_csp() {
|
|||
normalized.errors,
|
||||
[
|
||||
"Error processing content_security_policy: Policy is missing a required ‘script-src’ directive",
|
||||
'Error processing content_security_policy: Value "object-src \'none\'" must either: match the format "contentSecurityPolicy", or be an object value',
|
||||
],
|
||||
"Should have the expected warning"
|
||||
);
|
||||
|
@ -41,9 +42,9 @@ add_task(async function test_manifest_csp() {
|
|||
|
||||
add_task(async function test_manifest_csp_v3() {
|
||||
let normalized = await ExtensionTestUtils.normalizeManifest({
|
||||
manifest_version: 3,
|
||||
content_security_policy: {
|
||||
extension_pages: "script-src 'self' 'unsafe-eval'; object-src 'none'",
|
||||
content_scripts: "script-src 'self'; object-src 'none'; img-src 'none'",
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -54,14 +55,10 @@ add_task(async function test_manifest_csp_v3() {
|
|||
"script-src 'self' 'unsafe-eval'; object-src 'none'",
|
||||
"Should have the expected policy string"
|
||||
);
|
||||
equal(
|
||||
normalized.value.content_security_policy.content_scripts,
|
||||
"script-src 'self'; object-src 'none'; img-src 'none'",
|
||||
"Should have the expected policy string"
|
||||
);
|
||||
|
||||
ExtensionTestUtils.failOnSchemaWarnings(false);
|
||||
normalized = await ExtensionTestUtils.normalizeManifest({
|
||||
manifest_version: 3,
|
||||
content_security_policy: {
|
||||
extension_pages: "object-src 'none'",
|
||||
},
|
||||
|
@ -69,32 +66,12 @@ add_task(async function test_manifest_csp_v3() {
|
|||
ExtensionTestUtils.failOnSchemaWarnings(true);
|
||||
|
||||
equal(normalized.error, undefined, "Should not have an error");
|
||||
equal(normalized.errors.length, 2, "Should have warnings");
|
||||
equal(normalized.errors.length, 1, "Should have warnings");
|
||||
Assert.deepEqual(
|
||||
normalized.errors,
|
||||
[
|
||||
"Error processing content_security_policy.extension_pages: Policy is missing a required ‘script-src’ directive",
|
||||
'Error processing content_security_policy: Value must either: be a string value, or .extension_pages must match the format "contentSecurityPolicy"',
|
||||
],
|
||||
"Should have the expected warning for extension_pages CSP"
|
||||
);
|
||||
|
||||
ExtensionTestUtils.failOnSchemaWarnings(false);
|
||||
normalized = await ExtensionTestUtils.normalizeManifest({
|
||||
content_security_policy: {
|
||||
content_scripts: "object-src 'none'",
|
||||
},
|
||||
});
|
||||
ExtensionTestUtils.failOnSchemaWarnings(true);
|
||||
|
||||
equal(normalized.error, undefined, "Should not have an error");
|
||||
equal(normalized.errors.length, 2, "Should have warnings");
|
||||
Assert.deepEqual(
|
||||
normalized.errors,
|
||||
[
|
||||
"Error processing content_security_policy.content_scripts: Policy is missing a required ‘script-src’ directive",
|
||||
'Error processing content_security_policy: Value must either: be a string value, or .content_scripts must match the format "contentSecurityPolicy"',
|
||||
],
|
||||
"Should have the expected warning for content_scripts CSP"
|
||||
);
|
||||
});
|
||||
|
|
|
@ -17,6 +17,38 @@ add_task(async function test_simple() {
|
|||
await extension.unload();
|
||||
});
|
||||
|
||||
add_task(async function test_manifest_V3_disabled() {
|
||||
Services.prefs.setBoolPref("extensions.manifestV3.enabled", false);
|
||||
let extensionData = {
|
||||
manifest: {
|
||||
manifest_version: 3,
|
||||
},
|
||||
};
|
||||
|
||||
let extension = ExtensionTestUtils.loadExtension(extensionData);
|
||||
await Assert.rejects(
|
||||
extension.startup(),
|
||||
/Unsupported manifest version: 3/,
|
||||
"manifest V3 cannot be loaded"
|
||||
);
|
||||
Services.prefs.clearUserPref("extensions.manifestV3.enabled");
|
||||
});
|
||||
|
||||
add_task(async function test_manifest_V3_enabled() {
|
||||
Services.prefs.setBoolPref("extensions.manifestV3.enabled", true);
|
||||
let extensionData = {
|
||||
manifest: {
|
||||
manifest_version: 3,
|
||||
},
|
||||
};
|
||||
|
||||
let extension = ExtensionTestUtils.loadExtension(extensionData);
|
||||
await extension.startup();
|
||||
equal(extension.extension.manifest.manifest_version, 3, "manifest V3 loads");
|
||||
await extension.unload();
|
||||
Services.prefs.clearUserPref("extensions.manifestV3.enabled");
|
||||
});
|
||||
|
||||
add_task(async function test_background() {
|
||||
function background() {
|
||||
browser.test.log("running background script");
|
||||
|
|
Загрузка…
Ссылка в новой задаче