Bug 766569 - add l10n support to existing CSP errors and warnings. (r=bz)

This commit is contained in:
Mark Goodwin 2012-06-29 12:51:24 -07:00
parent d2d56a7644
commit f752a13d8e
4 changed files with 218 additions and 50 deletions

View File

@ -10,11 +10,19 @@
* separate file for testing purposes.
*/
const Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Services",
"resource://gre/modules/Services.jsm");
// Module stuff
var EXPORTED_SYMBOLS = ["CSPRep", "CSPSourceList", "CSPSource", "CSPHost",
"CSPWarning", "CSPError", "CSPdebug",
"CSPViolationReportListener"];
"CSPViolationReportListener", "CSPLocalizer"];
var STRINGS_URI = "chrome://global/locale/security/csp.properties";
// these are not exported
var gIoService = Components.classes["@mozilla.org/network/io-service;1"]
@ -222,7 +230,7 @@ CSPRep.fromString = function(aStr, self, docRequest, csp) {
else if (opt === "eval-script")
aCSPR._allowEval = true;
else
CSPWarning("don't understand option '" + opt + "'. Ignoring it.");
CSPWarning(CSPLocalizer.getFormatStr("doNotUnderstandOption", [opt]));
}
continue directive;
}
@ -275,18 +283,18 @@ CSPRep.fromString = function(aStr, self, docRequest, csp) {
if (self) {
if (gETLDService.getBaseDomain(uri) !==
gETLDService.getBaseDomain(selfUri)) {
CSPWarning("can't use report URI from non-matching eTLD+1: "
+ gETLDService.getBaseDomain(uri));
CSPWarning(CSPLocalizer.getFormatStr("notETLDPlus1",
[gETLDService.getBaseDomain(uri)]));
continue;
}
if (!uri.schemeIs(selfUri.scheme)) {
CSPWarning("can't use report URI with different scheme from "
+ "originating document: " + uri.asciiSpec);
CSPWarning(CSPLocalizer.getFormatStr("notSameScheme",
[uri.asciiSpec]));
continue;
}
if (uri.port && uri.port !== selfUri.port) {
CSPWarning("can't use report URI with different port from "
+ "originating document: " + uri.asciiSpec);
CSPWarning(CSPLocalizer.getFormatStr("notSamePort",
[uri.asciiSpec]));
continue;
}
}
@ -295,14 +303,14 @@ CSPRep.fromString = function(aStr, self, docRequest, csp) {
case Components.results.NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS:
case Components.results.NS_ERROR_HOST_IS_IP_ADDRESS:
if (uri.host !== selfUri.host) {
CSPWarning("page on " + selfUri.host
+ " cannot send reports to " + uri.host);
CSPWarning(CSPLocalizer.getFormatStr("pageCannotSendReportsTo",
[selfUri.host, uri.host]));
continue;
}
break;
default:
CSPWarning("couldn't parse report URI: " + uriStrings[i]);
CSPWarning(CSPLocalizer.getFormatStr("couldNotParseReportURI", [uriStrings[i]]));
continue;
}
}
@ -317,13 +325,13 @@ CSPRep.fromString = function(aStr, self, docRequest, csp) {
if (dirname === UD.POLICY_URI) {
// POLICY_URI can only be alone
if (aCSPR._directives.length > 0 || dirs.length > 1) {
CSPError("policy-uri directive can only appear alone");
CSPError(CSPLocalizer.getStr("policyURINotAlone"));
return CSPRep.fromString("default-src 'none'");
}
// if we were called without a reference to the parent document request
// we won't be able to suspend it while we fetch the policy -> fail closed
if (!docRequest || !csp) {
CSPError("The policy-uri cannot be fetched without a parent request and a CSP.");
CSPError(CSPLocalizer.getStr("noParentRequest"));
return CSPRep.fromString("default-src 'none'");
}
@ -331,22 +339,22 @@ CSPRep.fromString = function(aStr, self, docRequest, csp) {
try {
uri = gIoService.newURI(dirvalue, null, selfUri);
} catch(e) {
CSPError("could not parse URI in policy URI: " + dirvalue);
CSPError(CSPLocalizer.getFormatStr("policyURIParseError", [dirvalue]));
return CSPRep.fromString("default-src 'none'");
}
// Verify that policy URI comes from the same origin
if (selfUri) {
if (selfUri.host !== uri.host){
CSPError("can't fetch policy uri from non-matching hostname: " + uri.host);
CSPError(CSPLocalizer.getFormatStr("nonMatchingHost", [uri.host]));
return CSPRep.fromString("default-src 'none'");
}
if (selfUri.port !== uri.port){
CSPError("can't fetch policy uri from non-matching port: " + uri.port);
CSPError(CSPLocalizer.getFormatStr("nonMatchingPort", [uri.port.toString()]));
return CSPRep.fromString("default-src 'none'");
}
if (selfUri.scheme !== uri.scheme){
CSPError("can't fetch policy uri from non-matching scheme: " + uri.scheme);
CSPError(CSPLocalizer.getFormatStr("nonMatchingScheme", [uri.scheme]));
return CSPRep.fromString("default-src 'none'");
}
}
@ -363,7 +371,7 @@ CSPRep.fromString = function(aStr, self, docRequest, csp) {
catch (e) {
// resume the document request and apply most restrictive policy
docRequest.resume();
CSPError("Error fetching policy-uri: " + e);
CSPError(CSPLocalizer.getFormatStr("errorFetchingPolicy", [e.toString()]));
return CSPRep.fromString("default-src 'none'");
}
@ -373,7 +381,7 @@ CSPRep.fromString = function(aStr, self, docRequest, csp) {
}
// UNIDENTIFIED DIRECTIVE /////////////////////////////////////////////
CSPWarning("Couldn't process unknown directive '" + dirname + "'");
CSPWarning(CSPLocalizer.getFormatStr("couldNotProcessUnknownDirective", [dirname]));
} // end directive: loop
@ -516,7 +524,7 @@ CSPRep.prototype = {
var SD = CSPRep.SRC_DIRECTIVES;
var defaultSrcDir = this._directives[SD.DEFAULT_SRC];
if (!defaultSrcDir) {
CSPWarning("'allow' or 'default-src' directive required but not present. Reverting to \"default-src 'none'\"");
CSPWarning(CSPLocalizer.getStr("allowOrDefaultSrcRequired"));
return false;
}
@ -606,7 +614,7 @@ CSPSourceList.fromString = function(aStr, self, enforceSelfChecks) {
if (tokens[i] === "") continue;
var src = CSPSource.create(tokens[i], self, enforceSelfChecks);
if (!src) {
CSPWarning("Failed to parse unrecoginzied source " + tokens[i]);
CSPWarning(CSPLocalizer.getFormatStr("failedToParseUnrecognizedSource", [tokens[i]]));
continue;
}
slObj._sources.push(src);
@ -832,12 +840,12 @@ CSPSource.create = function(aData, self, enforceSelfChecks) {
*/
CSPSource.fromURI = function(aURI, self, enforceSelfChecks) {
if (!(aURI instanceof Components.interfaces.nsIURI)){
CSPError("Provided argument is not an nsIURI");
CSPError(CSPLocalizer.getStr("cspSourceNotURI"));
return null;
}
if (!self && enforceSelfChecks) {
CSPError("Can't use 'self' if self data is not provided");
CSPError(CSPLocalizer.getStr("selfDataNotProvided"));
return null;
}
@ -856,7 +864,7 @@ CSPSource.fromURI = function(aURI, self, enforceSelfChecks) {
sObj._scheme = aURI.scheme;
} catch(e) {
sObj._scheme = undefined;
CSPError("can't parse a URI without a scheme: " + aURI.asciiSpec);
CSPError(CSPLocalizer.getFormatStr("uriWithoutScheme", [aURI.asciiSpec]));
return null;
}
@ -912,12 +920,12 @@ CSPSource.fromString = function(aStr, self, enforceSelfChecks) {
return null;
if (!(typeof aStr === 'string')) {
CSPError("Provided argument is not a string");
CSPError(CSPLocalizer.getStr("argumentIsNotString"));
return null;
}
if (!self && enforceSelfChecks) {
CSPError("Can't use 'self' if self data is not provided");
CSPError(CSPLocalizer.getStr("selfDataNotProvided"));
return null;
}
@ -931,7 +939,7 @@ CSPSource.fromString = function(aStr, self, enforceSelfChecks) {
// take care of 'self' keyword
if (aStr === "'self'") {
if (!self) {
CSPError("self keyword used, but no self data specified");
CSPError(CSPLocalizer.getStr("selfKeywordNoSelfData"));
return null;
}
sObj._self = self.clone();
@ -950,7 +958,7 @@ CSPSource.fromString = function(aStr, self, enforceSelfChecks) {
if (chunks.length == 1) {
sObj._host = CSPHost.fromString(chunks[0]);
if (!sObj._host) {
CSPError("Couldn't parse invalid source " + aStr);
CSPError(CSPLocalizer.getFormatStr("couldntParseInvalidSource",[aStr]));
return null;
}
@ -958,7 +966,7 @@ CSPSource.fromString = function(aStr, self, enforceSelfChecks) {
if (enforceSelfChecks) {
// note: the non _scheme accessor checks sObj._self
if (!sObj.scheme || !sObj.port) {
CSPError("Can't create host-only source " + aStr + " without 'self' data");
CSPError(CSPLocalizer.getFormatStr("hostSourceWithoutData",[aStr]));
return null;
}
}
@ -977,7 +985,7 @@ CSPSource.fromString = function(aStr, self, enforceSelfChecks) {
if (chunks[0] !== "") {
sObj._host = CSPHost.fromString(chunks[0]);
if (!sObj._host) {
CSPError("Couldn't parse invalid source " + aStr);
CSPError(CSPLocalizer.getFormatStr("couldntParseInvalidSource",[aStr]));
return null;
}
}
@ -987,7 +995,7 @@ CSPSource.fromString = function(aStr, self, enforceSelfChecks) {
if (enforceSelfChecks) {
// note: the non _scheme accessor checks sObj._self
if (!sObj.scheme || !sObj.host || !sObj.port) {
CSPError("Can't create source " + aStr + " without 'self' data");
CSPError(CSPLocalizer.getFormatStr("sourceWithoutData",[aStr]));
return null;
}
}
@ -1009,7 +1017,7 @@ CSPSource.fromString = function(aStr, self, enforceSelfChecks) {
// ... and parse
sObj._host = CSPHost.fromString(cleanHost);
if (!sObj._host) {
CSPError("Couldn't parse invalid host " + cleanHost);
CSPError(CSPLocalizer.getFormatStr("couldntParseInvalidHost",[cleanHost]));
return null;
}
}
@ -1019,14 +1027,14 @@ CSPSource.fromString = function(aStr, self, enforceSelfChecks) {
if (enforceSelfChecks) {
// note: the non _scheme accessor checks sObj._self
if (!sObj.scheme || !sObj.host || !sObj.port) {
CSPError("Can't create source " + aStr + " without 'self' data");
CSPError(CSPLocalizer.getFormatStr("sourceWithoutData",[aStr]));
return null;
}
}
}
else {
// AAAH! Don't know what to do! No valid scheme or port!
CSPError("Couldn't parse invalid source " + aStr);
CSPError(CSPLocalizer.getFormatStr("couldntParseInvalidSource",[aStr]));
return null;
}
@ -1035,12 +1043,12 @@ CSPSource.fromString = function(aStr, self, enforceSelfChecks) {
// If there are three chunks, we got 'em all!
if (!CSPSource.validSchemeName(chunks[0])) {
CSPError("Couldn't parse scheme in " + aStr);
CSPError(CSPLocalizer.getFormatStr("couldntParseScheme",[aStr]));
return null;
}
sObj._scheme = chunks[0];
if (!(chunks[2] === "*" || chunks[2].match(/^\d+$/))) {
CSPError("Couldn't parse port in " + aStr);
CSPError(CSPLocalizer.getFormatStr("couldntParsePort",[aStr]));
return null;
}
@ -1202,8 +1210,7 @@ CSPSource.prototype = {
else if (that._port === this._port)
newSource._port = this._port;
else {
CSPError("Could not intersect " + this + " with " + that
+ " due to port problems.");
CSPError(CSPLocalizer.getFormatStr("notIntersectPort", [this.toString(), that.toString()]));
return null;
}
@ -1219,8 +1226,7 @@ CSPSource.prototype = {
else if (that._scheme === this._scheme)
newSource._scheme = this._scheme;
else {
CSPError("Could not intersect " + this + " with " + that
+ " due to scheme problems.");
CSPError(CSPLocalizer.getFormatStr("notIntersectScheme", [this.toString(), that.toString()]));
return null;
}
@ -1236,14 +1242,13 @@ CSPSource.prototype = {
if (this._host && that._host) {
newSource._host = this._host.intersectWith(that._host);
} else if (this._host) {
CSPError("intersecting source with undefined host: " + that.toString());
CSPError(CSPLocalizer.getFormatStr("intersectingSourceWithUndefinedHost", [that.toString()]));
newSource._host = this._host.clone();
} else if (that._host) {
CSPError("intersecting source with undefined host: " + this.toString());
CSPError(CSPLocalizer.getFormatStr("intersectingSourceWithUndefinedHost", [this.toString()]));
newSource._host = that._host.clone();
} else {
CSPError("intersecting two sources with undefined hosts: " +
this.toString() + " and " + that.toString());
CSPError(CSPLocalizer.getFormatStr("intersectingSourcesWithUndefinedHosts", [this.toString(), that.toString()]));
newSource._host = CSPHost.fromString("*");
}
@ -1486,3 +1491,55 @@ CSPViolationReportListener.prototype = {
};
//////////////////////////////////////////////////////////////////////
CSPLocalizer = {
/**
* Retrieve a localized string.
*
* @param string aName
* The string name you want from the CSP string bundle.
* @return string
* The localized string.
*/
getStr: function CSPLoc_getStr(aName)
{
let result;
try {
result = this.stringBundle.GetStringFromName(aName);
}
catch (ex) {
Cu.reportError("Failed to get string: " + aName);
throw ex;
}
return result;
},
/**
* Retrieve a localized string formatted with values coming from the given
* array.
*
* @param string aName
* The string name you want from the CSP string bundle.
* @param array aArray
* The array of values you want in the formatted string.
* @return string
* The formatted local string.
*/
getFormatStr: function CSPLoc_getFormatStr(aName, aArray)
{
let result;
try {
result = this.stringBundle.formatStringFromName(aName, aArray, aArray.length);
}
catch (ex) {
Cu.reportError("Failed to format string: " + aName);
throw ex;
}
return result;
},
};
XPCOMUtils.defineLazyGetter(CSPLocalizer, "stringBundle", function() {
return Services.strings.createBundle(STRINGS_URI);
});

View File

@ -284,8 +284,13 @@ ContentSecurityPolicy.prototype = {
var reportString = JSON.stringify(report);
CSPdebug("Constructed violation report:\n" + reportString);
CSPWarning("Directive \"" + violatedDirective + "\" violated"
+ (blockedUri['asciiSpec'] ? " by " + blockedUri.asciiSpec : ""),
var violationMessage = null;
if(blockedUri["asciiSpec"]){
violationMessage = CSPLocalizer.getFormatStr("directiveViolatedWithURI", [violatedDirective, blockedUri.asciiSpec]);
} else {
violationMessage = CSPLocalizer.getFormatStr("directiveViolated", [violatedDirective]);
}
CSPWarning(violationMessage,
this.innerWindowID,
(aSourceFile) ? aSourceFile : null,
(aScriptSample) ? decodeURIComponent(aScriptSample) : null,
@ -347,8 +352,8 @@ ContentSecurityPolicy.prototype = {
} catch(e) {
// it's possible that the URI was invalid, just log a
// warning and skip over that.
CSPWarning("Tried to send report to invalid URI: \"" + uris[i] + "\"", this.innerWindowID);
CSPWarning("error was: \"" + e + "\"", this.innerWindowID);
CSPWarning(CSPLocalizer.getFormatStr("triedToSendReport", [uris[i]]), this.innerWindowID);
CSPWarning(CSPLocalizer.getFormatStr("errorWas", [e.toString()]), this.innerWindowID);
}
}
}
@ -550,8 +555,7 @@ CSPReportRedirectSink.prototype = {
// nsIChannelEventSink
asyncOnChannelRedirect: function channel_redirect(oldChannel, newChannel,
flags, callback) {
CSPWarning("Post of violation report to " + oldChannel.URI.asciiSpec +
" failed, as a redirect occurred", this.innerWindowID);
CSPWarning(CSPLocalizer.getFormatStr("reportPostRedirect", [oldChannel.URI.asciiSpec]));
// cancel the old channel so XHR failure callback happens
oldChannel.cancel(Cr.NS_ERROR_ABORT);

View File

@ -0,0 +1,106 @@
# 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/.
# CSP Warnings:
# LOCALIZATION NOTE (directiveViolated):
# %1$S is the directive that has been violated.
directiveViolated = Directive %1$S violated
# LOCALIZATION NOTE (directiveViolatedWithURI):
# %1$S is the directive that has been violated.
# %2$S is the URI of the resource which violated the directive.
directiveViolatedWithURI = Directive %1$S violated by %2$S
# LOCALIZATION NOTE (triedToSendReport):
# %1$S is the URI we attempted to send a report to.
triedToSendReport = Tried to send report to invalid URI: "%1$S"
# LOCALIZATION NOTE (errorWas):
# %1$S is the error resulting from attempting to send the report
errorWas = error was: "%1$S"
# LOCALIZATION NOTE (couldNotParseReportURI):
# %1$S is the report URI that could not be parsed
couldNotParseReportURI = couldn't parse report URI: %1$S
# LOCALIZATION NOTE (couldNotProcessUnknownDirective):
# %1$S is the unknown directive
couldNotProcessUnknownDirective = Couldn't process unknown directive '%1$S'
# LOCALIZATION NOTE (doNotUnderstandOption):
# %1$S is the option that could not be understood
doNotUnderstandOption = don't understand option '%1$S'. Ignoring it.
# LOCALIZATION NOTE (notETLDPlus1):
# %1$S is the ETLD of the report URI that could not be used
notETLDPlus1 = can't use report URI from non-matching eTLD+1: %1$S
# LOCALIZATION NOTE (notSameScheme):
# %1$S is the report URI that could not be used
notSameScheme = can't use report URI with different scheme from originating document: %1$S
# LOCALIZATION NOTE (notSamePort):
# %1$S is the report URI that could not be used
notSamePort = can't use report URI with different port from originating document: %1$S
# LOCALIZATION NOTE (pageCannotSendReportsTo):
# %1$S is the URI of the page with the policy
# %2$S is the report URI that could not be used
pageCannotSendReportsTo = page on %1$S cannot send reports to %2$S
allowOrDefaultSrcRequired = 'allow' or 'default-src' directive required but not present. Reverting to "default-src 'none'"
# LOCALIZATION NOTE (failedToParseUnrecognizedSource):
# %1$S is the CSP Source that could not be parsed
failedToParseUnrecognizedSource = Failed to parse unrecoginzied source %1$S
# LOCALIZATION NOTE (reportPostRedirect):
# %1$S is the specified report URI before redirect
reportPostRedirect = Post of violation report to %1$S failed, as a redirect occurred
# CSP Errors:
policyURINotAlone = policy-uri directive can only appear alone
noParentRequest = The policy-uri cannot be fetched without a parent request and a CSP.
# LOCALIZATION NOTE (policyURIParseError):
# %1$S is the URI that could not be parsed
policyURIParseError = could not parse URI in policy URI: %1$S
# LOCALIZATION NOTE (nonMatchingHost):
# %1$S is the URI host that does not match
nonMatchingHost = can't fetch policy uri from non-matching hostname: %1$S
# LOCALIZATION NOTE (nonMatchingPort):
# %1$S is the URI port that does not match
nonMatchingPort = can't fetch policy uri from non-matching port: %1$S
# LOCALIZATION NOTE (nonMatchingScheme):
# %1$S is the URI scheme that does not match
nonMatchingScheme = can't fetch policy uri from non-matching scheme: %1$S
# LOCALIZATION NOTE (errorFetchingPolicy):
# %1$S is the error that caused fetching to fail
errorFetchingPolicy = Error fetching policy-uri: %1$S
cspSourceNotURI = Provided argument is not an nsIURI
argumentIsNotString = Provided argument is not a string
selfDataNotProvided = Can't use 'self' if self data is not provided
# LOCALIZATION NOTE (uriWithoutScheme):
# %1$S is the URI without a scheme
uriWithoutScheme = can't parse a URI without a scheme: %1$S
selfKeywordNoSelfData = self keyword used, but no self data specified
# LOCALIZATION NOTE (couldntParseInvalidSource):
# %1$S is the source that could not be parsed
couldntParseInvalidSource = Couldn't parse invalid source %1$S
# LOCALIZATION NOTE (hostSourceWithoutData):
# %1$S is the source
hostSourceWithoutData = Can't create host-only source %1$S without 'self' data
# LOCALIZATION NOTE (sourceWithoutData):
# %1$S is the source
sourceWithoutData = Can't create source %1$S without 'self' data
# LOCALIZATION NOTE (couldntParseInvalidHost):
# %1$S is the host that's invalid
couldntParseInvalidHost = Couldn't parse invalid host %1$S
# LOCALIZATION NOTE (couldntParseScheme):
# %1$S is the string source
couldntParseScheme = Couldn't parse scheme in %1$S
# LOCALIZATION NOTE (couldntParsePort):
# %1$S is the string source
couldntParsePort = Couldn't parse port in %1$S
# LOCALIZATION NOTE (notIntersectPort):
# %1$S is one source we tried to intersect
# %2$S is the other
notIntersectPort = Could not intersect %1$S with %2$S due to port problems.
# LOCALIZATION NOTE (notIntersectScheme):
# %1$S is one source we tried to intersect
# %2$S is the other
notIntersectScheme = Could not intersect %1$S with %2$S due to scheme problems.
# LOCALIZATION NOTE (intersectingSourceWithUndefinedHost):
# %1$S is the source
intersectingSourceWithUndefinedHost = intersecting source with undefined host: %1$S
# LOCALIZATION NOTE (intersectingSourcesWithUndefinedHosts):
# %1$S is the first source
# %2$S is the second source
intersectingSourcesWithUndefinedHosts = intersecting two sources with undefined hosts: %1$S and %2$S

View File

@ -26,6 +26,7 @@
locale/@AB_CD@/global/layout/xmlparser.properties (%chrome/layout/xmlparser.properties)
locale/@AB_CD@/global/layout/HtmlForm.properties (%chrome/layout/HtmlForm.properties)
locale/@AB_CD@/global/security/caps.properties (%chrome/security/caps.properties)
locale/@AB_CD@/global/security/csp.properties (%chrome/security/csp.properties)
locale/@AB_CD@/global/xml/prettyprint.dtd (%chrome/xml/prettyprint.dtd)
locale/@AB_CD@/global-platform/win/accessible.properties (%chrome/accessibility/win/accessible.properties)
locale/@AB_CD@/global-platform/mac/accessible.properties (%chrome/accessibility/mac/accessible.properties)