Bug 1229639 - Part 1: Match CSP host source with percent-decoded URI. r=ckerschb
MozReview-Commit-ID: CSGeoSR2qw8
--- a/dom/security/nsCSPParser.cpp
+++ b/dom/security/nsCSPParser.cpp
@@ -186,73 +186,16 @@ nsCSPParser::resetCurChar(const nsAStrin
// number sign ("#") character, or by the end of the URI.
// http://tools.ietf.org/html/rfc3986#section-3.3
bool
nsCSPParser::atEndOfPath()
{
return (atEnd() || peek(QUESTIONMARK) || peek(NUMBER_SIGN));
}
-void
-nsCSPParser::percentDecodeStr(const nsAString& aEncStr, nsAString& outDecStr)
-{
- outDecStr.Truncate();
-
- // helper function that should not be visible outside this methods scope
- struct local {
- static inline char16_t convertHexDig(char16_t aHexDig) {
- if (isNumberToken(aHexDig)) {
- return aHexDig - '0';
- }
- if (aHexDig >= 'A' && aHexDig <= 'F') {
- return aHexDig - 'A' + 10;
- }
- // must be a lower case character
- // (aHexDig >= 'a' && aHexDig <= 'f')
- return aHexDig - 'a' + 10;
- }
- };
-
- const char16_t *cur, *end, *hexDig1, *hexDig2;
- cur = aEncStr.BeginReading();
- end = aEncStr.EndReading();
-
- while (cur != end) {
- // if it's not a percent sign then there is
- // nothing to do for that character
- if (*cur != PERCENT_SIGN) {
- outDecStr.Append(*cur);
- cur++;
- continue;
- }
-
- // get the two hexDigs following the '%'-sign
- hexDig1 = cur + 1;
- hexDig2 = cur + 2;
-
- // if there are no hexdigs after the '%' then
- // there is nothing to do for us.
- if (hexDig1 == end || hexDig2 == end ||
- !isValidHexDig(*hexDig1) ||
- !isValidHexDig(*hexDig2)) {
- outDecStr.Append(PERCENT_SIGN);
- cur++;
- continue;
- }
-
- // decode "% hexDig1 hexDig2" into a character.
- char16_t decChar = (local::convertHexDig(*hexDig1) << 4) +
- local::convertHexDig(*hexDig2);
- outDecStr.Append(decChar);
-
- // increment 'cur' to after the second hexDig
- cur = ++hexDig2;
- }
-}
-
// unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
bool
nsCSPParser::atValidUnreservedChar()
{
return (peek(isCharacterToken) || peek(isNumberToken) ||
peek(DASH) || peek(DOT) ||
peek(UNDERLINE) || peek(TILDE));
}
@@ -393,17 +336,17 @@ nsCSPParser::subPath(nsCSPHostSrc* aCspH
uint32_t charCounter = 0;
nsString pctDecodedSubPath;
while (!atEndOfPath()) {
if (peek(SLASH)) {
// before appendig any additional portion of a subpath we have to pct-decode
// that portion of the subpath. atValidPathChar() already verified a correct
// pct-encoding, now we can safely decode and append the decoded-sub path.
- percentDecodeStr(mCurValue, pctDecodedSubPath);
+ CSP_PercentDecodeStr(mCurValue, pctDecodedSubPath);
aCspHost->appendPath(pctDecodedSubPath);
// Resetting current value since we are appending parts of the path
// to aCspHost, e.g; "http://www.example.com/path1/path2" then the
// first part is "/path1", second part "/path2"
resetCurValue();
}
else if (!atValidPathChar()) {
const char16_t* params[] = { mCurToken.get() };
@@ -422,17 +365,17 @@ nsCSPParser::subPath(nsCSPHostSrc* aCspH
advance();
if (++charCounter > kSubHostPathCharacterCutoff) {
return false;
}
}
// before appendig any additional portion of a subpath we have to pct-decode
// that portion of the subpath. atValidPathChar() already verified a correct
// pct-encoding, now we can safely decode and append the decoded-sub path.
- percentDecodeStr(mCurValue, pctDecodedSubPath);
+ CSP_PercentDecodeStr(mCurValue, pctDecodedSubPath);
aCspHost->appendPath(pctDecodedSubPath);
resetCurValue();
return true;
}
bool
nsCSPParser::path(nsCSPHostSrc* aCspHost)
{
--- a/dom/security/nsCSPParser.h
+++ b/dom/security/nsCSPParser.h
@@ -139,18 +139,16 @@ class nsCSPParser {
bool port();
bool path(nsCSPHostSrc* aCspHost);
bool subHost(); // helper function to parse subDomains
bool atValidUnreservedChar(); // helper function to parse unreserved
bool atValidSubDelimChar(); // helper function to parse sub-delims
bool atValidPctEncodedChar(); // helper function to parse pct-encoded
bool subPath(nsCSPHostSrc* aCspHost); // helper function to parse paths
- void percentDecodeStr(const nsAString& aEncStr, // helper function to percent-decode
- nsAString& outDecStr);
inline bool atEnd()
{
return mCurChar >= mEndChar;
}
inline bool accept(char16_t aSymbol)
{
--- a/dom/security/nsCSPUtils.cpp
+++ b/dom/security/nsCSPUtils.cpp
@@ -26,16 +26,73 @@ GetCspUtilsLog()
static mozilla::LazyLogModule gCspUtilsPRLog("CSPUtils");
return gCspUtilsPRLog;
}
#define CSPUTILSLOG(args) MOZ_LOG(GetCspUtilsLog(), mozilla::LogLevel::Debug, args)
#define CSPUTILSLOGENABLED() MOZ_LOG_TEST(GetCspUtilsLog(), mozilla::LogLevel::Debug)
void
+CSP_PercentDecodeStr(const nsAString& aEncStr, nsAString& outDecStr)
+{
+ outDecStr.Truncate();
+
+ // helper function that should not be visible outside this methods scope
+ struct local {
+ static inline char16_t convertHexDig(char16_t aHexDig) {
+ if (isNumberToken(aHexDig)) {
+ return aHexDig - '0';
+ }
+ if (aHexDig >= 'A' && aHexDig <= 'F') {
+ return aHexDig - 'A' + 10;
+ }
+ // must be a lower case character
+ // (aHexDig >= 'a' && aHexDig <= 'f')
+ return aHexDig - 'a' + 10;
+ }
+ };
+
+ const char16_t *cur, *end, *hexDig1, *hexDig2;
+ cur = aEncStr.BeginReading();
+ end = aEncStr.EndReading();
+
+ while (cur != end) {
+ // if it's not a percent sign then there is
+ // nothing to do for that character
+ if (*cur != PERCENT_SIGN) {
+ outDecStr.Append(*cur);
+ cur++;
+ continue;
+ }
+
+ // get the two hexDigs following the '%'-sign
+ hexDig1 = cur + 1;
+ hexDig2 = cur + 2;
+
+ // if there are no hexdigs after the '%' then
+ // there is nothing to do for us.
+ if (hexDig1 == end || hexDig2 == end ||
+ !isValidHexDig(*hexDig1) ||
+ !isValidHexDig(*hexDig2)) {
+ outDecStr.Append(PERCENT_SIGN);
+ cur++;
+ continue;
+ }
+
+ // decode "% hexDig1 hexDig2" into a character.
+ char16_t decChar = (local::convertHexDig(*hexDig1) << 4) +
+ local::convertHexDig(*hexDig2);
+ outDecStr.Append(decChar);
+
+ // increment 'cur' to after the second hexDig
+ cur = ++hexDig2;
+ }
+}
+
+void
CSP_GetLocalizedStr(const char16_t* aName,
const char16_t** aParams,
uint32_t aLength,
char16_t** outResult)
{
nsCOMPtr<nsIStringBundle> keyStringBundle;
nsCOMPtr<nsIStringBundleService> stringBundleService =
mozilla::services::GetStringBundleService();
@@ -562,30 +619,33 @@ nsCSPHostSrc::permits(nsIURI* aUri, cons
}
// Before we can check if the host matches, we have to
// extract the host part from aUri.
nsAutoCString uriHost;
nsresult rv = aUri->GetHost(uriHost);
NS_ENSURE_SUCCESS(rv, false);
+ nsString decodedUriHost;
+ CSP_PercentDecodeStr(NS_ConvertUTF8toUTF16(uriHost), decodedUriHost);
+
// 4.5) host matching: Check if the allowed host starts with a wilcard.
if (mHost.First() == '*') {
NS_ASSERTION(mHost[1] == '.', "Second character needs to be '.' whenever host starts with '*'");
// Eliminate leading "*", but keeping the FULL STOP (.) thereafter before checking
// if the remaining characters match
nsString wildCardHost = mHost;
wildCardHost = Substring(wildCardHost, 1, wildCardHost.Length() - 1);
- if (!StringEndsWith(NS_ConvertUTF8toUTF16(uriHost), wildCardHost)) {
+ if (!StringEndsWith(decodedUriHost, wildCardHost)) {
return false;
}
}
// 4.6) host matching: Check if hosts match.
- else if (!mHost.Equals(NS_ConvertUTF8toUTF16(uriHost))) {
+ else if (!mHost.Equals(decodedUriHost)) {
return false;
}
// Port matching: Check if the ports match.
if (!permitsPort(mScheme, mPort, aUri)) {
return false;
}
@@ -599,28 +659,32 @@ nsCSPHostSrc::permits(nsIURI* aUri, cons
nsCOMPtr<nsIURL> url = do_QueryInterface(aUri);
if (!url) {
NS_ASSERTION(false, "can't QI into nsIURI");
return false;
}
nsAutoCString uriPath;
rv = url->GetFilePath(uriPath);
NS_ENSURE_SUCCESS(rv, false);
+
+ nsString decodedUriPath;
+ CSP_PercentDecodeStr(NS_ConvertUTF8toUTF16(uriPath), decodedUriPath);
+
// check if the last character of mPath is '/'; if so
// we just have to check loading resource is within
// the allowed path.
if (mPath.Last() == '/') {
- if (!StringBeginsWith(NS_ConvertUTF8toUTF16(uriPath), mPath)) {
+ if (!StringBeginsWith(decodedUriPath, mPath)) {
return false;
}
}
// otherwise mPath whitelists a specific file, and we have to
// check if the loading resource matches that whitelisted file.
else {
- if (!mPath.Equals(NS_ConvertUTF8toUTF16(uriPath))) {
+ if (!mPath.Equals(decodedUriPath)) {
return false;
}
}
}
// At the end: scheme, host, port and path match -> allow the load.
return true;
}
--- a/dom/security/nsCSPUtils.h
+++ b/dom/security/nsCSPUtils.h
@@ -188,16 +188,18 @@ nsCSPHostSrc* CSP_CreateHostSrcFromURI(n
bool CSP_IsValidDirective(const nsAString& aDir);
bool CSP_IsDirective(const nsAString& aValue, CSPDirective aDir);
bool CSP_IsKeyword(const nsAString& aValue, enum CSPKeyword aKey);
bool CSP_IsQuotelessKeyword(const nsAString& aKey);
CSPDirective CSP_ContentTypeToDirective(nsContentPolicyType aType);
class nsCSPSrcVisitor;
+void CSP_PercentDecodeStr(const nsAString& aEncStr, nsAString& outDecStr);
+
/* =============== nsCSPSrc ================== */
class nsCSPBaseSrc {
public:
nsCSPBaseSrc();
virtual ~nsCSPBaseSrc();
virtual bool permits(nsIURI* aUri, const nsAString& aNonce, bool aWasRedirected,