mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 951457 - Create C++ CSP Parser and policy classes, part 0 - parser and utils r=sstamm, r=jst
This commit is contained in:
parent
f4a8908ef6
commit
15346fa52d
@ -110,7 +110,9 @@ UNIFIED_SOURCES += [
|
||||
'nsContentSink.cpp',
|
||||
'nsCopySupport.cpp',
|
||||
'nsCrossSiteListenerProxy.cpp',
|
||||
'nsCSPParser.cpp',
|
||||
'nsCSPService.cpp',
|
||||
'nsCSPUtils.cpp',
|
||||
'nsDataDocumentContentPolicy.cpp',
|
||||
'nsDocumentEncoder.cpp',
|
||||
'nsDOMAttributeMap.cpp',
|
||||
|
913
content/base/src/nsCSPParser.cpp
Normal file
913
content/base/src/nsCSPParser.cpp
Normal file
@ -0,0 +1,913 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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 "mozilla/ArrayUtils.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsCSPParser.h"
|
||||
#include "nsCSPUtils.h"
|
||||
#include "nsIConsoleService.h"
|
||||
#include "nsIScriptError.h"
|
||||
#include "nsIStringBundle.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsReadableUtils.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsUnicharUtils.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
#if defined(PR_LOGGING)
|
||||
static PRLogModuleInfo*
|
||||
GetCspParserLog()
|
||||
{
|
||||
static PRLogModuleInfo* gCspParserPRLog;
|
||||
if (!gCspParserPRLog)
|
||||
gCspParserPRLog = PR_NewLogModule("CSPParser");
|
||||
return gCspParserPRLog;
|
||||
}
|
||||
#endif
|
||||
|
||||
#define CSPPARSERLOG(args) PR_LOG(GetCspParserLog(), 4, args)
|
||||
|
||||
static const char16_t COLON = ':';
|
||||
static const char16_t SEMICOLON = ';';
|
||||
static const char16_t SLASH = '/';
|
||||
static const char16_t PLUS = '+';
|
||||
static const char16_t DASH = '-';
|
||||
static const char16_t DOT = '.';
|
||||
static const char16_t UNDERLINE = '_';
|
||||
static const char16_t WILDCARD = '*';
|
||||
static const char16_t WHITESPACE = ' ';
|
||||
static const char16_t SINGLEQUOTE = '\'';
|
||||
static const char16_t OPEN_CURL = '{';
|
||||
static const char16_t CLOSE_CURL = '}';
|
||||
|
||||
static uint32_t kSubHostPathCharacterCutoff = 512;
|
||||
|
||||
static const char* kHashSourceValidFns [] = { "sha256", "sha384", "sha512" };
|
||||
static const uint32_t kHashSourceValidFnsLen = 3;
|
||||
|
||||
/* ===== nsCSPTokenizer ==================== */
|
||||
|
||||
nsCSPTokenizer::nsCSPTokenizer(const char16_t* aStart,
|
||||
const char16_t* aEnd)
|
||||
: mCurChar(aStart)
|
||||
, mEndChar(aEnd)
|
||||
{
|
||||
CSPPARSERLOG(("nsCSPTokenizer::nsCSPTokenizer"));
|
||||
}
|
||||
|
||||
nsCSPTokenizer::~nsCSPTokenizer()
|
||||
{
|
||||
CSPPARSERLOG(("nsCSPTokenizer::~nsCSPTokenizer"));
|
||||
}
|
||||
|
||||
void
|
||||
nsCSPTokenizer::generateNextToken()
|
||||
{
|
||||
skipWhiteSpaceAndSemicolon();
|
||||
while (!atEnd() &&
|
||||
*mCurChar != WHITESPACE &&
|
||||
*mCurChar != SEMICOLON) {
|
||||
mCurToken.Append(*mCurChar++);
|
||||
}
|
||||
CSPPARSERLOG(("nsCSPTokenizer::generateNextToken: %s", NS_ConvertUTF16toUTF8(mCurToken).get()));
|
||||
}
|
||||
|
||||
void
|
||||
nsCSPTokenizer::generateTokens(cspTokens& outTokens)
|
||||
{
|
||||
CSPPARSERLOG(("nsCSPTokenizer::generateTokens"));
|
||||
|
||||
// dirAndSrcs holds one set of [ name, src, src, src, ... ]
|
||||
nsTArray <nsString> dirAndSrcs;
|
||||
|
||||
while (!atEnd()) {
|
||||
generateNextToken();
|
||||
dirAndSrcs.AppendElement(mCurToken);
|
||||
skipWhiteSpace();
|
||||
if (atEnd() || accept(SEMICOLON)) {
|
||||
outTokens.AppendElement(dirAndSrcs);
|
||||
dirAndSrcs.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsCSPTokenizer::tokenizeCSPPolicy(const nsAString &aPolicyString,
|
||||
cspTokens& outTokens)
|
||||
{
|
||||
CSPPARSERLOG(("nsCSPTokenizer::tokenizeCSPPolicy"));
|
||||
|
||||
nsCSPTokenizer tokenizer(aPolicyString.BeginReading(),
|
||||
aPolicyString.EndReading());
|
||||
|
||||
tokenizer.generateTokens(outTokens);
|
||||
}
|
||||
|
||||
/* ===== nsCSPParser ==================== */
|
||||
|
||||
nsCSPParser::nsCSPParser(cspTokens& aTokens,
|
||||
nsIURI* aSelfURI,
|
||||
uint64_t aInnerWindowID)
|
||||
: mTokens(aTokens)
|
||||
, mSelfURI(aSelfURI)
|
||||
, mInnerWindowID(aInnerWindowID)
|
||||
{
|
||||
CSPPARSERLOG(("nsCSPParser::nsCSPParser"));
|
||||
}
|
||||
|
||||
nsCSPParser::~nsCSPParser()
|
||||
{
|
||||
CSPPARSERLOG(("nsCSPParser::~nsCSPParser"));
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
isCharacterToken(char16_t aSymbol)
|
||||
{
|
||||
return (aSymbol >= 'a' && aSymbol <= 'z') ||
|
||||
(aSymbol >= 'A' && aSymbol <= 'Z');
|
||||
}
|
||||
|
||||
bool
|
||||
isNumberToken(char16_t aSymbol)
|
||||
{
|
||||
return (aSymbol >= '0' && aSymbol <= '9');
|
||||
}
|
||||
|
||||
void
|
||||
nsCSPParser::resetCurChar(const nsAString& aToken)
|
||||
{
|
||||
mCurChar = aToken.BeginReading();
|
||||
mEndChar = aToken.EndReading();
|
||||
resetCurValue();
|
||||
}
|
||||
|
||||
void
|
||||
nsCSPParser::logWarningErrorToConsole(uint32_t aSeverityFlag,
|
||||
const char* aProperty,
|
||||
const char16_t* aParams[],
|
||||
uint32_t aParamsLength)
|
||||
{
|
||||
CSPPARSERLOG(("nsCSPParser::logWarningErrorToConsole: %s", aProperty));
|
||||
|
||||
nsXPIDLString logMsg;
|
||||
CSP_GetLocalizedStr(NS_ConvertUTF8toUTF16(aProperty).get(),
|
||||
aParams,
|
||||
aParamsLength,
|
||||
getter_Copies(logMsg));
|
||||
|
||||
CSP_LogMessage(logMsg, EmptyString(), EmptyString(),
|
||||
0, 0, aSeverityFlag,
|
||||
"CSP", mInnerWindowID);
|
||||
}
|
||||
|
||||
bool
|
||||
nsCSPParser::hostChar()
|
||||
{
|
||||
if (atEnd()) {
|
||||
return false;
|
||||
}
|
||||
return accept(isCharacterToken) ||
|
||||
accept(isNumberToken) ||
|
||||
accept(DASH);
|
||||
}
|
||||
|
||||
// (ALPHA / DIGIT / "+" / "-" / "." )
|
||||
bool
|
||||
nsCSPParser::schemeChar()
|
||||
{
|
||||
if (atEnd()) {
|
||||
return false;
|
||||
}
|
||||
return accept(isCharacterToken) ||
|
||||
accept(isNumberToken) ||
|
||||
accept(PLUS) ||
|
||||
accept(DASH) ||
|
||||
accept(DOT);
|
||||
}
|
||||
|
||||
bool
|
||||
nsCSPParser::fileAndArguments()
|
||||
{
|
||||
CSPPARSERLOG(("nsCSPParser::fileAndArguments, mCurToken: %s, mCurValue: %s",
|
||||
NS_ConvertUTF16toUTF8(mCurToken).get(),
|
||||
NS_ConvertUTF16toUTF8(mCurValue).get()));
|
||||
|
||||
// Possibly we already parsed part of the file in path(), therefore accepting "."
|
||||
if (accept(DOT) && !accept(isCharacterToken)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// From now on, accept pretty much anything to avoid unnecessary errors
|
||||
while (!atEnd()) {
|
||||
advance();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// port = ":" ( 1*DIGIT / "*" )
|
||||
bool
|
||||
nsCSPParser::port()
|
||||
{
|
||||
CSPPARSERLOG(("nsCSPParser::port, mCurToken: %s, mCurValue: %s",
|
||||
NS_ConvertUTF16toUTF8(mCurToken).get(),
|
||||
NS_ConvertUTF16toUTF8(mCurValue).get()));
|
||||
|
||||
// Consume the COLON we just peeked at in houstSource
|
||||
accept(COLON);
|
||||
|
||||
// Resetting current value since we start to parse a port now.
|
||||
// e.g; "http://www.example.com:8888" then we have already parsed
|
||||
// everything up to (including) ":";
|
||||
resetCurValue();
|
||||
|
||||
// Port might be "*"
|
||||
if (accept(WILDCARD)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Port must start with a number
|
||||
if (!accept(isNumberToken)) {
|
||||
const char16_t* params[] = { mCurValue.get() };
|
||||
logWarningErrorToConsole(nsIScriptError::warningFlag, "policyURIParseError",
|
||||
params, ArrayLength(params));
|
||||
return false;
|
||||
}
|
||||
// Consume more numbers and set parsed port to the nsCSPHost
|
||||
while (accept(isNumberToken)) { /* consume */ }
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
nsCSPParser::subPath(nsCSPHostSrc* aCspHost)
|
||||
{
|
||||
CSPPARSERLOG(("nsCSPParser::subPath, mCurToken: %s, mCurValue: %s",
|
||||
NS_ConvertUTF16toUTF8(mCurToken).get(),
|
||||
NS_ConvertUTF16toUTF8(mCurValue).get()));
|
||||
|
||||
// Emergency exit to avoid endless loops in case a path in a CSP policy
|
||||
// is longer than 512 characters, or also to avoid endless loops
|
||||
// in case we are parsing unrecognized characters in the following loop.
|
||||
uint32_t charCounter = 0;
|
||||
|
||||
while (!atEnd() && !peek(DOT)) {
|
||||
++charCounter;
|
||||
while (hostChar() || accept(UNDERLINE)) {
|
||||
/* consume */
|
||||
++charCounter;
|
||||
}
|
||||
if (accept(SLASH)) {
|
||||
aCspHost->appendPath(mCurValue);
|
||||
// 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();
|
||||
}
|
||||
if (charCounter > kSubHostPathCharacterCutoff) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
aCspHost->appendPath(mCurValue);
|
||||
resetCurValue();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
nsCSPParser::path(nsCSPHostSrc* aCspHost)
|
||||
{
|
||||
CSPPARSERLOG(("nsCSPParser::path, mCurToken: %s, mCurValue: %s",
|
||||
NS_ConvertUTF16toUTF8(mCurToken).get(),
|
||||
NS_ConvertUTF16toUTF8(mCurValue).get()));
|
||||
|
||||
// Resetting current value and forgetting everything we have parsed so far
|
||||
// e.g. parsing "http://www.example.com/path1/path2", then
|
||||
// "http://www.example.com" has already been parsed so far
|
||||
// forget about it.
|
||||
resetCurValue();
|
||||
|
||||
if (!accept(SLASH)) {
|
||||
return false;
|
||||
}
|
||||
if (atEnd()) {
|
||||
return true;
|
||||
}
|
||||
if (!hostChar()) {
|
||||
return false;
|
||||
}
|
||||
return subPath(aCspHost);
|
||||
}
|
||||
|
||||
bool
|
||||
nsCSPParser::subHost()
|
||||
{
|
||||
CSPPARSERLOG(("nsCSPParser::subHost, mCurToken: %s, mCurValue: %s",
|
||||
NS_ConvertUTF16toUTF8(mCurToken).get(),
|
||||
NS_ConvertUTF16toUTF8(mCurValue).get()));
|
||||
|
||||
// Emergency exit to avoid endless loops in case a host in a CSP policy
|
||||
// is longer than 512 characters, or also to avoid endless loops
|
||||
// in case we are parsing unrecognized characters in the following loop.
|
||||
uint32_t charCounter = 0;
|
||||
|
||||
while (!atEnd() && !peek(COLON) && !peek(SLASH)) {
|
||||
++charCounter;
|
||||
while (hostChar()) {
|
||||
/* consume */
|
||||
++charCounter;
|
||||
}
|
||||
if (accept(DOT) && !accept(isCharacterToken)) {
|
||||
return false;
|
||||
}
|
||||
if (charCounter > kSubHostPathCharacterCutoff) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// host = "*" / [ "*." ] 1*host-char *( "." 1*host-char )
|
||||
nsCSPHostSrc*
|
||||
nsCSPParser::host()
|
||||
{
|
||||
CSPPARSERLOG(("nsCSPParser::host, mCurToken: %s, mCurValue: %s",
|
||||
NS_ConvertUTF16toUTF8(mCurToken).get(),
|
||||
NS_ConvertUTF16toUTF8(mCurValue).get()));
|
||||
|
||||
// Check if the token starts with "*"; please remember that we handle
|
||||
// a single "*" as host in sourceExpression, but we still have to handle
|
||||
// the case where a scheme was defined, e.g., as:
|
||||
// "https://*", "*.example.com", "*:*", etc.
|
||||
if (accept(WILDCARD)) {
|
||||
// Might solely be the wildcard
|
||||
if (atEnd() || peek(COLON)) {
|
||||
return new nsCSPHostSrc(mCurValue);
|
||||
}
|
||||
// If the token is not only the "*", a "." must follow right after
|
||||
if (!accept(DOT)) {
|
||||
const char16_t* params[] = { mCurValue.get() };
|
||||
logWarningErrorToConsole(nsIScriptError::warningFlag, "policyURIParseError",
|
||||
params, ArrayLength(params));
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Expecting at least one Character
|
||||
if (!accept(isCharacterToken)) {
|
||||
const char16_t* params[] = { mCurValue.get() };
|
||||
logWarningErrorToConsole(nsIScriptError::warningFlag, "policyURIParseError",
|
||||
params, ArrayLength(params));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// There might be several sub hosts defined.
|
||||
if (!subHost()) {
|
||||
const char16_t* params[] = { mCurValue.get() };
|
||||
logWarningErrorToConsole(nsIScriptError::warningFlag, "policyURIParseError",
|
||||
params, ArrayLength(params));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// HostName might match a keyword, log to the console.
|
||||
if (CSP_IsQuotelessKeyword(mCurValue)) {
|
||||
nsString keyword = mCurValue;
|
||||
ToLowerCase(keyword);
|
||||
const char16_t* params[] = { mCurValue.get(), keyword.get() };
|
||||
logWarningErrorToConsole(nsIScriptError::warningFlag, "hostNameMightBeKeyword",
|
||||
params, ArrayLength(params));
|
||||
}
|
||||
|
||||
// Create a new nsCSPHostSrc with the parsed host.
|
||||
return new nsCSPHostSrc(mCurValue);
|
||||
}
|
||||
|
||||
// apps use special hosts; "app://{app-host-is-uid}""
|
||||
nsCSPHostSrc*
|
||||
nsCSPParser::appHost()
|
||||
{
|
||||
CSPPARSERLOG(("nsCSPParser::appHost, mCurToken: %s, mCurValue: %s",
|
||||
NS_ConvertUTF16toUTF8(mCurToken).get(),
|
||||
NS_ConvertUTF16toUTF8(mCurValue).get()));
|
||||
|
||||
while (hostChar()) { /* consume */ }
|
||||
|
||||
// appHosts have to end with "}", otherwise we have to report an error
|
||||
if (!accept(CLOSE_CURL)) {
|
||||
const char16_t* params[] = { mCurValue.get() };
|
||||
logWarningErrorToConsole(nsIScriptError::warningFlag, "policyURIParseError",
|
||||
params, ArrayLength(params));
|
||||
return nullptr;
|
||||
}
|
||||
return new nsCSPHostSrc(mCurValue);
|
||||
}
|
||||
|
||||
// keyword-source = "'self'" / "'unsafe-inline'" / "'unsafe-eval'"
|
||||
nsCSPBaseSrc*
|
||||
nsCSPParser::keywordSource()
|
||||
{
|
||||
CSPPARSERLOG(("nsCSPParser::keywordSource, mCurToken: %s, mCurValue: %s",
|
||||
NS_ConvertUTF16toUTF8(mCurToken).get(),
|
||||
NS_ConvertUTF16toUTF8(mCurValue).get()));
|
||||
|
||||
// Special case handling for 'self' which is not stored internally as a keyword,
|
||||
// but rather creates a nsCSPHostSrc using the selfURI
|
||||
if (CSP_IsKeyword(mCurToken, CSP_SELF)) {
|
||||
return CSP_CreateHostSrcFromURI(mSelfURI);
|
||||
}
|
||||
|
||||
if (CSP_IsKeyword(mCurToken, CSP_UNSAFE_INLINE) ||
|
||||
CSP_IsKeyword(mCurToken, CSP_UNSAFE_EVAL)) {
|
||||
return new nsCSPKeywordSrc(CSP_KeywordToEnum(mCurToken));
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// host-source = [ scheme "://" ] host [ port ] [ path ]
|
||||
nsCSPHostSrc*
|
||||
nsCSPParser::hostSource()
|
||||
{
|
||||
CSPPARSERLOG(("nsCSPParser::hostSource, mCurToken: %s, mCurValue: %s",
|
||||
NS_ConvertUTF16toUTF8(mCurToken).get(),
|
||||
NS_ConvertUTF16toUTF8(mCurValue).get()));
|
||||
|
||||
// Special case handling for app specific hosts
|
||||
if (accept(OPEN_CURL)) {
|
||||
// If appHost() returns null, the error was handled in appHost().
|
||||
// appHosts can not have a port, or path, we can return.
|
||||
return appHost();
|
||||
}
|
||||
|
||||
nsCSPHostSrc* cspHost = host();
|
||||
if (!cspHost) {
|
||||
// Error was reported in host()
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Calling port() to see if there is a port to parse, if an error
|
||||
// occurs, port() reports the error, if port() returns true;
|
||||
// we have a valid port, so we add it to cspHost.
|
||||
if (peek(COLON)) {
|
||||
if (!port()) {
|
||||
delete cspHost;
|
||||
return nullptr;
|
||||
}
|
||||
cspHost->setPort(mCurValue);
|
||||
}
|
||||
|
||||
if (atEnd()) {
|
||||
return cspHost;
|
||||
}
|
||||
|
||||
// Calling path() to see if there is a path to parse, if an error
|
||||
// occurs, path() reports the error; handing cspHost as an argument
|
||||
// which simplifies parsing of several paths.
|
||||
if (!path(cspHost)) {
|
||||
return cspHost;
|
||||
}
|
||||
|
||||
// Calling fileAndArguments to see if there are any files to parse;
|
||||
// if an error occurs, fileAndArguments() reports the error; if
|
||||
// fileAndArguments returns true, we have a valid file, so we add it.
|
||||
if (fileAndArguments()) {
|
||||
cspHost->setFileAndArguments(mCurValue);
|
||||
}
|
||||
|
||||
return cspHost;
|
||||
}
|
||||
|
||||
// scheme-source = scheme ":"
|
||||
nsCSPSchemeSrc*
|
||||
nsCSPParser::schemeSource()
|
||||
{
|
||||
CSPPARSERLOG(("nsCSPParser::schemeSource, mCurToken: %s, mCurValue: %s",
|
||||
NS_ConvertUTF16toUTF8(mCurToken).get(),
|
||||
NS_ConvertUTF16toUTF8(mCurValue).get()));
|
||||
|
||||
if (!accept(isCharacterToken)) {
|
||||
return nullptr;
|
||||
}
|
||||
while (schemeChar()) { /* consume */ }
|
||||
nsString scheme = mCurValue;
|
||||
|
||||
// If the potential scheme is not followed by ":" - it's not a scheme
|
||||
if (!accept(COLON)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// If the chraracter following the ":" is a number or the "*"
|
||||
// then we are not parsing a scheme; but rather a host;
|
||||
if (peek(isNumberToken) || peek(WILDCARD)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new nsCSPSchemeSrc(scheme);
|
||||
}
|
||||
|
||||
// nonce-source = "'nonce-" nonce-value "'"
|
||||
nsCSPNonceSrc*
|
||||
nsCSPParser::nonceSource()
|
||||
{
|
||||
CSPPARSERLOG(("nsCSPParser::nonceSource, mCurToken: %s, mCurValue: %s",
|
||||
NS_ConvertUTF16toUTF8(mCurToken).get(),
|
||||
NS_ConvertUTF16toUTF8(mCurValue).get()));
|
||||
|
||||
// Check if mCurToken begins with "'nonce-" and ends with "'"
|
||||
if (!StringBeginsWith(mCurToken, NS_ConvertUTF8toUTF16(CSP_EnumToKeyword(CSP_NONCE)),
|
||||
nsASCIICaseInsensitiveStringComparator()) ||
|
||||
mCurToken.Last() != SINGLEQUOTE) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Trim surrounding single quotes
|
||||
const nsAString& expr = Substring(mCurToken, 1, mCurToken.Length() - 2);
|
||||
|
||||
int32_t dashIndex = expr.FindChar(DASH);
|
||||
if (dashIndex < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
return new nsCSPNonceSrc(Substring(expr,
|
||||
dashIndex + 1,
|
||||
expr.Length() - dashIndex + 1));
|
||||
}
|
||||
|
||||
// hash-source = "'" hash-algo "-" base64-value "'"
|
||||
nsCSPHashSrc*
|
||||
nsCSPParser::hashSource()
|
||||
{
|
||||
CSPPARSERLOG(("nsCSPParser::hashSource, mCurToken: %s, mCurValue: %s",
|
||||
NS_ConvertUTF16toUTF8(mCurToken).get(),
|
||||
NS_ConvertUTF16toUTF8(mCurValue).get()));
|
||||
|
||||
|
||||
// Check if mCurToken starts and ends with "'"
|
||||
if (mCurToken.First() != SINGLEQUOTE ||
|
||||
mCurToken.Last() != SINGLEQUOTE) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Trim surrounding single quotes
|
||||
const nsAString& expr = Substring(mCurToken, 1, mCurToken.Length() - 2);
|
||||
|
||||
int32_t dashIndex = expr.FindChar(DASH);
|
||||
if (dashIndex < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsAutoString algo(Substring(expr, 0, dashIndex));
|
||||
nsAutoString hash(Substring(expr, dashIndex + 1, expr.Length() - dashIndex + 1));
|
||||
|
||||
for (uint32_t i = 0; i < kHashSourceValidFnsLen; i++) {
|
||||
if (algo.LowerCaseEqualsASCII(kHashSourceValidFns[i])) {
|
||||
return new nsCSPHashSrc(algo, hash);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// source-expression = scheme-source / host-source / keyword-source
|
||||
// / nonce-source / hash-source
|
||||
nsCSPBaseSrc*
|
||||
nsCSPParser::sourceExpression()
|
||||
{
|
||||
CSPPARSERLOG(("nsCSPParser::sourceExpression, mCurToken: %s, mCurValue: %s",
|
||||
NS_ConvertUTF16toUTF8(mCurToken).get(),
|
||||
NS_ConvertUTF16toUTF8(mCurValue).get()));
|
||||
|
||||
// Check if it is a keyword
|
||||
if (nsCSPBaseSrc *cspKeyword = keywordSource()) {
|
||||
return cspKeyword;
|
||||
}
|
||||
|
||||
// Check if it is a nonce-source
|
||||
if (nsCSPNonceSrc* cspNonce = nonceSource()) {
|
||||
return cspNonce;
|
||||
}
|
||||
|
||||
// Check if it is a hash-source
|
||||
if (nsCSPHashSrc* cspHash = hashSource()) {
|
||||
return cspHash;
|
||||
}
|
||||
|
||||
// We handle a single "*" as host here, to avoid any confusion when applying the default scheme.
|
||||
// However, we still would need to apply the default scheme in case we would parse "*:80".
|
||||
if (mCurToken.EqualsASCII("*")) {
|
||||
return new nsCSPHostSrc(NS_LITERAL_STRING("*"));
|
||||
}
|
||||
|
||||
// Calling resetCurChar allows us to use mCurChar and mEndChar
|
||||
// to parse mCurToken; e.g. mCurToken = "http://www.example.com", then
|
||||
// mCurChar = 'h'
|
||||
// mEndChar = points just after the last 'm'
|
||||
// mCurValue = ""
|
||||
resetCurChar(mCurToken);
|
||||
|
||||
// Check if mCurToken starts with a scheme
|
||||
nsAutoString parsedScheme;
|
||||
if (nsCSPSchemeSrc* cspScheme = schemeSource()) {
|
||||
// mCurToken might only enforce a specific scheme
|
||||
if (atEnd()) {
|
||||
return cspScheme;
|
||||
}
|
||||
// If something follows the scheme, we do not create
|
||||
// a nsCSPSchemeSrc, but rather a nsCSPHostSrc, which
|
||||
// needs to know the scheme to enforce; remember the
|
||||
// scheme and delete cspScheme;
|
||||
cspScheme->toString(parsedScheme);
|
||||
parsedScheme.Trim(":", false, true);
|
||||
delete cspScheme;
|
||||
|
||||
// If mCurToken provides not only a scheme, but also a host, we have to check
|
||||
// if two slashes follow the scheme.
|
||||
if (!accept(SLASH) || !accept(SLASH)) {
|
||||
const char16_t* params[] = { mCurToken.get() };
|
||||
logWarningErrorToConsole(nsIScriptError::warningFlag, "failedToParseUnrecognizedSource",
|
||||
params, ArrayLength(params));
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Calling resetCurValue allows us to keep pointers for mCurChar and mEndChar
|
||||
// alive, but resets mCurValue; e.g. mCurToken = "http://www.example.com", then
|
||||
// mCurChar = 'w'
|
||||
// mEndChar = 'm'
|
||||
// mCurValue = ""
|
||||
resetCurValue();
|
||||
|
||||
// If mCurToken does not provide a scheme, we apply the scheme from selfURI
|
||||
if (parsedScheme.IsEmpty()) {
|
||||
// Resetting internal helpers, because we might already have parsed some of the host
|
||||
// when trying to parse a scheme.
|
||||
resetCurChar(mCurToken);
|
||||
nsAutoCString scheme;
|
||||
mSelfURI->GetScheme(scheme);
|
||||
parsedScheme.AssignASCII(scheme.get());
|
||||
}
|
||||
|
||||
// At this point we are expecting a host to be parsed.
|
||||
// Trying to create a new nsCSPHost.
|
||||
if (nsCSPHostSrc *cspHost = hostSource()) {
|
||||
// Do not forget to set the parsed scheme.
|
||||
cspHost->setScheme(parsedScheme);
|
||||
return cspHost;
|
||||
}
|
||||
// Error was reported in hostSource()
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// source-list = *WSP [ source-expression *( 1*WSP source-expression ) *WSP ]
|
||||
// / *WSP "'none'" *WSP
|
||||
void
|
||||
nsCSPParser::sourceList(nsTArray<nsCSPBaseSrc*>& outSrcs)
|
||||
{
|
||||
bool isNone = false;
|
||||
|
||||
// remember, srcs start at index 1
|
||||
for (uint32_t i = 1; i < mCurDir.Length(); i++) {
|
||||
// mCurToken is only set here and remains the current token
|
||||
// to be processed, which avoid passing arguments between functions.
|
||||
mCurToken = mCurDir[i];
|
||||
|
||||
CSPPARSERLOG(("nsCSPParser::sourceList, mCurToken: %s, mCurValue: %s",
|
||||
NS_ConvertUTF16toUTF8(mCurToken).get(),
|
||||
NS_ConvertUTF16toUTF8(mCurValue).get()));
|
||||
|
||||
// Special case handling for none:
|
||||
// Ignore 'none' if any other src is available.
|
||||
// (See http://www.w3.org/TR/CSP11/#parsing)
|
||||
if (CSP_IsKeyword(mCurToken, CSP_NONE)) {
|
||||
isNone = true;
|
||||
continue;
|
||||
}
|
||||
// Must be a regular source expression
|
||||
nsCSPBaseSrc* src = sourceExpression();
|
||||
if (src) {
|
||||
outSrcs.AppendElement(src);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the directive contains a 'none'
|
||||
if (isNone) {
|
||||
// If the directive contains no other srcs, then we set the 'none'
|
||||
if (outSrcs.Length() == 0) {
|
||||
nsCSPKeywordSrc *keyword = new nsCSPKeywordSrc(CSP_NONE);
|
||||
outSrcs.AppendElement(keyword);
|
||||
}
|
||||
// Otherwise, we ignore 'none' and report a warning
|
||||
else {
|
||||
const char16_t* params[] = { NS_ConvertUTF8toUTF16(CSP_EnumToKeyword(CSP_NONE)).get() };
|
||||
logWarningErrorToConsole(nsIScriptError::warningFlag, "ignoringUnknownOption",
|
||||
params, ArrayLength(params));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsCSPParser::reportURIList(nsTArray<nsCSPBaseSrc*>& outSrcs)
|
||||
{
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
nsresult rv;
|
||||
|
||||
// remember, srcs start at index 1
|
||||
for (uint32_t i = 1; i < mCurDir.Length(); i++) {
|
||||
mCurToken = mCurDir[i];
|
||||
|
||||
CSPPARSERLOG(("nsCSPParser::reportURIList, mCurToken: %s, mCurValue: %s",
|
||||
NS_ConvertUTF16toUTF8(mCurToken).get(),
|
||||
NS_ConvertUTF16toUTF8(mCurValue).get()));
|
||||
|
||||
rv = NS_NewURI(getter_AddRefs(uri), mCurToken, "", mSelfURI);
|
||||
|
||||
// If creating the URI casued an error, skip this URI
|
||||
if (NS_FAILED(rv)) {
|
||||
const char16_t* params[] = { mCurToken.get() };
|
||||
logWarningErrorToConsole(nsIScriptError::warningFlag, "couldNotParseReportURI",
|
||||
params, ArrayLength(params));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create new nsCSPReportURI and append to the list.
|
||||
nsCSPReportURI* reportURI = new nsCSPReportURI(uri);
|
||||
outSrcs.AppendElement(reportURI);
|
||||
}
|
||||
}
|
||||
|
||||
// directive-value = *( WSP / <VCHAR except ";" and ","> )
|
||||
void
|
||||
nsCSPParser::directiveValue(nsTArray<nsCSPBaseSrc*>& outSrcs)
|
||||
{
|
||||
CSPPARSERLOG(("nsCSPParser::directiveValue"));
|
||||
|
||||
// The tokenzier already generated an array in the form of
|
||||
// [ name, src, src, ... ], no need to parse again, but
|
||||
// special case handling in case the directive is report-uri.
|
||||
if (CSP_IsDirective(mCurDir[0], CSP_REPORT_URI)) {
|
||||
reportURIList(outSrcs);
|
||||
return;
|
||||
}
|
||||
// Otherwise just forward to sourceList
|
||||
sourceList(outSrcs);
|
||||
}
|
||||
|
||||
// directive-name = 1*( ALPHA / DIGIT / "-" )
|
||||
nsCSPDirective*
|
||||
nsCSPParser::directiveName()
|
||||
{
|
||||
CSPPARSERLOG(("nsCSPParser::directiveName, mCurToken: %s, mCurValue: %s",
|
||||
NS_ConvertUTF16toUTF8(mCurToken).get(),
|
||||
NS_ConvertUTF16toUTF8(mCurValue).get()));
|
||||
|
||||
// Check if it is a valid directive
|
||||
if (!CSP_IsValidDirective(mCurToken)) {
|
||||
const char16_t* params[] = { mCurToken.get() };
|
||||
logWarningErrorToConsole(nsIScriptError::warningFlag, "couldNotProcessUnknownDirective",
|
||||
params, ArrayLength(params));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Make sure the directive does not already exist
|
||||
// (see http://www.w3.org/TR/CSP11/#parsing)
|
||||
if (mPolicy->directiveExists(CSP_DirectiveToEnum(mCurToken))) {
|
||||
const char16_t* params[] = { mCurToken.get() };
|
||||
logWarningErrorToConsole(nsIScriptError::warningFlag, "duplicateDirective",
|
||||
params, ArrayLength(params));
|
||||
return nullptr;
|
||||
}
|
||||
return new nsCSPDirective(CSP_DirectiveToEnum(mCurToken));
|
||||
}
|
||||
|
||||
// directive = *WSP [ directive-name [ WSP directive-value ] ]
|
||||
void
|
||||
nsCSPParser::directive()
|
||||
{
|
||||
// Set the directiveName to mCurToken
|
||||
// Remember, the directive name is stored at index 0
|
||||
mCurToken = mCurDir[0];
|
||||
|
||||
CSPPARSERLOG(("nsCSPParser::directive, mCurToken: %s, mCurValue: %s",
|
||||
NS_ConvertUTF16toUTF8(mCurToken).get(),
|
||||
NS_ConvertUTF16toUTF8(mCurValue).get()));
|
||||
|
||||
// Make sure that the directive-srcs-array contains at least
|
||||
// one directive and one src.
|
||||
if (mCurDir.Length() < 1) {
|
||||
const char16_t* params[] = { NS_LITERAL_STRING("directive missing").get() };
|
||||
logWarningErrorToConsole(nsIScriptError::warningFlag, "failedToParseUnrecognizedSource",
|
||||
params, ArrayLength(params));
|
||||
return;
|
||||
}
|
||||
|
||||
if (mCurDir.Length() < 2) {
|
||||
const char16_t* params[] = { mCurToken.get() };
|
||||
logWarningErrorToConsole(nsIScriptError::warningFlag, "failedToParseUnrecognizedSource",
|
||||
params, ArrayLength(params));
|
||||
return;
|
||||
}
|
||||
|
||||
// Try to create a new CSPDirective
|
||||
nsCSPDirective* cspDir = directiveName();
|
||||
if (!cspDir) {
|
||||
// if we can not create a CSPDirective, we can skip parsing the srcs for that array
|
||||
return;
|
||||
}
|
||||
|
||||
// Try to parse all the srcs by handing the array off to directiveValue
|
||||
nsTArray<nsCSPBaseSrc*> srcs;
|
||||
directiveValue(srcs);
|
||||
|
||||
// If we can not parse any srcs; it's not worth having a directive; delete and return
|
||||
if (srcs.Length() == 0) {
|
||||
const char16_t* params[] = { mCurToken.get() };
|
||||
logWarningErrorToConsole(nsIScriptError::warningFlag, "failedToParseUnrecognizedSource",
|
||||
params, ArrayLength(params));
|
||||
delete cspDir;
|
||||
return;
|
||||
}
|
||||
|
||||
// Add the newly created srcs to the directive and add the directive to the policy
|
||||
cspDir->addSrcs(srcs);
|
||||
mPolicy->addDirective(cspDir);
|
||||
}
|
||||
|
||||
// policy = [ directive *( ";" [ directive ] ) ]
|
||||
nsCSPPolicy*
|
||||
nsCSPParser::policy()
|
||||
{
|
||||
CSPPARSERLOG(("nsCSPParser::policy"));
|
||||
|
||||
mPolicy = new nsCSPPolicy();
|
||||
for (uint32_t i = 0; i < mTokens.Length(); i++) {
|
||||
// All input is already tokenized; set one tokenized array in the form of
|
||||
// [ name, src, src, ... ]
|
||||
// to mCurDir and call directive which processes the current directive.
|
||||
mCurDir = mTokens[i];
|
||||
directive();
|
||||
}
|
||||
return mPolicy;
|
||||
}
|
||||
|
||||
nsCSPPolicy*
|
||||
nsCSPParser::parseContentSecurityPolicy(const nsAString& aPolicyString,
|
||||
nsIURI *aSelfURI,
|
||||
bool aReportOnly,
|
||||
uint64_t aInnerWindowID)
|
||||
{
|
||||
#ifdef PR_LOGGING
|
||||
{
|
||||
CSPPARSERLOG(("nsCSPParser::parseContentSecurityPolicy, policy: %s",
|
||||
NS_ConvertUTF16toUTF8(aPolicyString).get()));
|
||||
nsAutoCString spec;
|
||||
aSelfURI->GetSpec(spec);
|
||||
CSPPARSERLOG(("nsCSPParser::parseContentSecurityPolicy, selfURI: %s", spec.get()));
|
||||
CSPPARSERLOG(("nsCSPParser::parseContentSecurityPolicy, reportOnly: %s",
|
||||
(aReportOnly ? "true" : "false")));
|
||||
}
|
||||
#endif
|
||||
|
||||
NS_ASSERTION(aSelfURI, "Can not parseContentSecurityPolicy without aSelfURI");
|
||||
|
||||
// Separate all input into tokens and store them in the form of:
|
||||
// [ [ name, src, src, ... ], [ name, src, src, ... ], ... ]
|
||||
// The tokenizer itself can not fail; all eventual errors
|
||||
// are detected in the parser itself.
|
||||
|
||||
nsTArray< nsTArray<nsString> > tokens;
|
||||
nsCSPTokenizer::tokenizeCSPPolicy(aPolicyString, tokens);
|
||||
|
||||
nsCSPParser parser(tokens, aSelfURI, aInnerWindowID);
|
||||
|
||||
// Start the parser to generate a new CSPPolicy using the generated tokens.
|
||||
nsCSPPolicy* policy = parser.policy();
|
||||
|
||||
// Check that report-only policies define a report-uri, otherwise log warning.
|
||||
if (aReportOnly) {
|
||||
policy->setReportOnlyFlag(true);
|
||||
if (!policy->directiveExists(CSP_REPORT_URI)) {
|
||||
nsAutoCString prePath;
|
||||
nsresult rv = aSelfURI->GetPrePath(prePath);
|
||||
NS_ENSURE_SUCCESS(rv, policy);
|
||||
const char16_t* params[] = { NS_ConvertUTF8toUTF16(prePath).get() };
|
||||
parser.logWarningErrorToConsole(nsIScriptError::warningFlag, "reportURInotInReportOnlyHeader",
|
||||
params, ArrayLength(params));
|
||||
}
|
||||
}
|
||||
|
||||
if (policy->getNumDirectives() == 0) {
|
||||
// Individual errors were already reported in the parser, but if
|
||||
// we do not have an enforcable directive at all, we return null.
|
||||
delete policy;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
{
|
||||
nsString parsedPolicy;
|
||||
policy->toString(parsedPolicy);
|
||||
CSPPARSERLOG(("nsCSPParser::parseContentSecurityPolicy, parsedPolicy: %s",
|
||||
NS_ConvertUTF16toUTF8(parsedPolicy).get()));
|
||||
}
|
||||
#endif
|
||||
|
||||
return policy;
|
||||
}
|
229
content/base/src/nsCSPParser.h
Normal file
229
content/base/src/nsCSPParser.h
Normal file
@ -0,0 +1,229 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef nsCSPParser_h___
|
||||
#define nsCSPParser_h___
|
||||
|
||||
#include "nsCSPUtils.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsString.h"
|
||||
|
||||
/**
|
||||
* How does the parsing work?
|
||||
*
|
||||
* We generate tokens by splitting the policy-string by whitespace and semicolon.
|
||||
* Interally the tokens are represented as an array of string-arrays:
|
||||
*
|
||||
* [
|
||||
* [ name, src, src, src, ... ],
|
||||
* [ name, src, src, src, ... ],
|
||||
* [ name, src, src, src, ... ]
|
||||
* ]
|
||||
*
|
||||
* for example:
|
||||
* [
|
||||
* [ img-src, http://www.example.com, http:www.test.com ],
|
||||
* [ default-src, 'self'],
|
||||
* [ script-src, 'unsafe-eval', 'unsafe-inline' ],
|
||||
* ]
|
||||
*
|
||||
* The first element of each array has to be a valid directive-name, otherwise we can
|
||||
* ignore the remaining elements of the array. Also, if the
|
||||
* directive already exists in the current policy, we can ignore
|
||||
* the remaining elements of that array. (http://www.w3.org/TR/CSP/#parsing)
|
||||
*/
|
||||
|
||||
typedef nsTArray< nsTArray<nsString> > cspTokens;
|
||||
|
||||
class nsCSPTokenizer {
|
||||
|
||||
public:
|
||||
static void tokenizeCSPPolicy(const nsAString &aPolicyString, cspTokens& outTokens);
|
||||
|
||||
private:
|
||||
nsCSPTokenizer(const char16_t* aStart, const char16_t* aEnd);
|
||||
~nsCSPTokenizer();
|
||||
|
||||
inline bool atEnd()
|
||||
{
|
||||
return mCurChar >= mEndChar;
|
||||
}
|
||||
|
||||
inline void skipWhiteSpace()
|
||||
{
|
||||
while (mCurChar < mEndChar && *mCurChar == ' ') {
|
||||
mCurToken.Append(*mCurChar++);
|
||||
}
|
||||
mCurToken.Truncate();
|
||||
}
|
||||
|
||||
inline void skipWhiteSpaceAndSemicolon()
|
||||
{
|
||||
while (mCurChar < mEndChar && (*mCurChar == ' ' || *mCurChar == ';')) {
|
||||
mCurToken.Append(*mCurChar++);
|
||||
}
|
||||
mCurToken.Truncate();
|
||||
}
|
||||
|
||||
inline bool accept(char16_t aChar)
|
||||
{
|
||||
NS_ASSERTION(mCurChar < mEndChar, "Trying to dereference mEndChar");
|
||||
if (*mCurChar == aChar) {
|
||||
mCurToken.Append(*mCurChar++);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void generateNextToken();
|
||||
void generateTokens(cspTokens& outTokens);
|
||||
|
||||
const char16_t* mCurChar;
|
||||
const char16_t* mEndChar;
|
||||
nsString mCurToken;
|
||||
};
|
||||
|
||||
|
||||
class nsCSPParser {
|
||||
|
||||
public:
|
||||
/**
|
||||
* The CSP parser only has one publicly accessible function, which is parseContentSecurityPolicy.
|
||||
* Internally the input string is separated into string tokens and policy() is called, which starts
|
||||
* parsing the policy. The parser calls one function after the other according the the source-list
|
||||
* from http://www.w3.org/TR/CSP11/#source-list. E.g., the parser can only call port() after the parser
|
||||
* has already processed any possible host in host(), similar to a finite state machine.
|
||||
*/
|
||||
static nsCSPPolicy* parseContentSecurityPolicy(const nsAString &aPolicyString,
|
||||
nsIURI *aSelfURI,
|
||||
bool aReportOnly,
|
||||
uint64_t aInnerWindowID);
|
||||
|
||||
private:
|
||||
nsCSPParser(cspTokens& aTokens,
|
||||
nsIURI* aSelfURI,
|
||||
uint64_t aInnerWindowID);
|
||||
~nsCSPParser();
|
||||
|
||||
|
||||
// Parsing the CSP using the source-list from http://www.w3.org/TR/CSP11/#source-list
|
||||
nsCSPPolicy* policy();
|
||||
void directive();
|
||||
nsCSPDirective* directiveName();
|
||||
void directiveValue(nsTArray<nsCSPBaseSrc*>& outSrcs);
|
||||
void sourceList(nsTArray<nsCSPBaseSrc*>& outSrcs);
|
||||
nsCSPBaseSrc* sourceExpression();
|
||||
nsCSPSchemeSrc* schemeSource();
|
||||
nsCSPHostSrc* hostSource();
|
||||
nsCSPBaseSrc* keywordSource();
|
||||
nsCSPNonceSrc* nonceSource();
|
||||
nsCSPHashSrc* hashSource();
|
||||
nsCSPHostSrc* appHost(); // helper function to support app specific hosts
|
||||
nsCSPHostSrc* host();
|
||||
bool hostChar();
|
||||
bool schemeChar();
|
||||
bool port();
|
||||
bool path(nsCSPHostSrc* aCspHost);
|
||||
bool fileAndArguments();
|
||||
|
||||
bool subHost(); // helper function to parse subDomains
|
||||
bool subPath(nsCSPHostSrc* aCspHost); // helper function to parse paths
|
||||
void reportURIList(nsTArray<nsCSPBaseSrc*>& outSrcs); // helper function to parse report-uris
|
||||
|
||||
inline bool atEnd()
|
||||
{
|
||||
return mCurChar >= mEndChar;
|
||||
}
|
||||
|
||||
inline bool accept(char16_t aSymbol)
|
||||
{
|
||||
if (atEnd()) { return false; }
|
||||
return (*mCurChar == aSymbol) && advance();
|
||||
}
|
||||
|
||||
inline bool accept(bool (*aClassifier) (char16_t))
|
||||
{
|
||||
if (atEnd()) { return false; }
|
||||
return (aClassifier(*mCurChar)) && advance();
|
||||
}
|
||||
|
||||
inline bool peek(char16_t aSymbol)
|
||||
{
|
||||
if (atEnd()) { return false; }
|
||||
return *mCurChar == aSymbol;
|
||||
}
|
||||
|
||||
inline bool peek(bool (*aClassifier) (char16_t))
|
||||
{
|
||||
if (atEnd()) { return false; }
|
||||
return aClassifier(*mCurChar);
|
||||
}
|
||||
|
||||
inline bool advance()
|
||||
{
|
||||
if (atEnd()) { return false; }
|
||||
mCurValue.Append(*mCurChar++);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline void resetCurValue()
|
||||
{
|
||||
mCurValue.Truncate();
|
||||
}
|
||||
|
||||
void resetCurChar(const nsAString& aToken);
|
||||
|
||||
void logWarningErrorToConsole(uint32_t aSeverityFlag,
|
||||
const char* aProperty,
|
||||
const char16_t* aParams[],
|
||||
uint32_t aParamsLength);
|
||||
|
||||
/**
|
||||
* When parsing the policy, the parser internally uses the following helper
|
||||
* variables/members which are used/reset during parsing. The following
|
||||
* example explains how they are used.
|
||||
* The tokenizer separats all input into arrays of arrays of strings, which
|
||||
* are stored in mTokens, for example:
|
||||
* mTokens = [ [ script-src, http://www.example.com, 'self' ], ... ]
|
||||
*
|
||||
* When parsing starts, mCurdir always holds the currently processed array of strings.
|
||||
* In our example:
|
||||
* mCurDir = [ script-src, http://www.example.com, 'self' ]
|
||||
*
|
||||
* During parsing, we process/consume one string at a time of that array.
|
||||
* We set mCurToken to the string we are currently processing; in the first case
|
||||
* that would be:
|
||||
* mCurToken = script-src
|
||||
* which allows to do simple string comparisons to see if mCurToken is a valid directive.
|
||||
*
|
||||
* Continuing parsing, the parser consumes the next string of that array, resetting:
|
||||
* mCurToken = "http://www.example.com"
|
||||
* ^ ^
|
||||
* mCurChar mEndChar (points *after* the 'm')
|
||||
* mCurValue = ""
|
||||
*
|
||||
* After calling advance() the first time, helpers would hold the following values:
|
||||
* mCurToken = "http://www.example.com"
|
||||
* ^ ^
|
||||
* mCurChar mEndChar (points *after* the 'm')
|
||||
* mCurValue = "h"
|
||||
*
|
||||
* We continue parsing till all strings of one directive are consumed, then we reset
|
||||
* mCurDir to hold the next array of strings and start the process all over.
|
||||
*/
|
||||
|
||||
const char16_t* mCurChar;
|
||||
const char16_t* mEndChar;
|
||||
nsString mCurValue;
|
||||
nsString mCurToken;
|
||||
nsTArray<nsString> mCurDir;
|
||||
|
||||
cspTokens mTokens;
|
||||
nsIURI* mSelfURI;
|
||||
nsCSPPolicy* mPolicy;
|
||||
uint64_t mInnerWindowID; // used for console reporting
|
||||
};
|
||||
|
||||
#endif /* nsCSPParser_h___ */
|
802
content/base/src/nsCSPUtils.cpp
Normal file
802
content/base/src/nsCSPUtils.cpp
Normal file
@ -0,0 +1,802 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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 "nsCSPUtils.h"
|
||||
#include "nsDebug.h"
|
||||
#include "nsIConsoleService.h"
|
||||
#include "nsICryptoHash.h"
|
||||
#include "nsIScriptError.h"
|
||||
#include "nsIServiceManager.h"
|
||||
#include "nsIStringBundle.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsReadableUtils.h"
|
||||
|
||||
#if defined(PR_LOGGING)
|
||||
static PRLogModuleInfo*
|
||||
GetCspUtilsLog()
|
||||
{
|
||||
static PRLogModuleInfo* gCspUtilsPRLog;
|
||||
if (!gCspUtilsPRLog)
|
||||
gCspUtilsPRLog = PR_NewLogModule("CSPUtils");
|
||||
return gCspUtilsPRLog;
|
||||
}
|
||||
#endif
|
||||
|
||||
#define CSPUTILSLOG(args) PR_LOG(GetCspUtilsLog(), 4, args)
|
||||
|
||||
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();
|
||||
|
||||
NS_ASSERTION(stringBundleService, "String bundle service must be present!");
|
||||
stringBundleService->CreateBundle("chrome://global/locale/security/csp.properties",
|
||||
getter_AddRefs(keyStringBundle));
|
||||
|
||||
NS_ASSERTION(keyStringBundle, "Key string bundle must be available!");
|
||||
|
||||
if (!keyStringBundle) {
|
||||
return;
|
||||
}
|
||||
keyStringBundle->FormatStringFromName(aName, aParams, aLength, outResult);
|
||||
}
|
||||
|
||||
void
|
||||
CSP_LogStrMessage(const nsAString& aMsg)
|
||||
{
|
||||
nsCOMPtr<nsIConsoleService> console(do_GetService("@mozilla.org/consoleservice;1"));
|
||||
|
||||
if (!console) {
|
||||
return;
|
||||
}
|
||||
nsString msg = PromiseFlatString(aMsg);
|
||||
console->LogStringMessage(msg.get());
|
||||
}
|
||||
|
||||
void
|
||||
CSP_LogMessage(const nsAString& aMessage,
|
||||
const nsAString& aSourceName,
|
||||
const nsAString& aSourceLine,
|
||||
uint32_t aLineNumber,
|
||||
uint32_t aColumnNumber,
|
||||
uint32_t aFlags,
|
||||
const char *aCategory,
|
||||
uint32_t aInnerWindowID)
|
||||
{
|
||||
nsCOMPtr<nsIConsoleService> console(do_GetService(NS_CONSOLESERVICE_CONTRACTID));
|
||||
|
||||
nsCOMPtr<nsIScriptError> error(do_CreateInstance(NS_SCRIPTERROR_CONTRACTID));
|
||||
|
||||
if (!console || !error) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Prepending CSP to the outgoing console message
|
||||
nsString cspMsg;
|
||||
cspMsg.Append(NS_LITERAL_STRING("Content Security Policy: "));
|
||||
cspMsg.Append(aMessage);
|
||||
|
||||
nsresult rv;
|
||||
if (aInnerWindowID > 0) {
|
||||
nsCString catStr;
|
||||
catStr.AssignASCII(aCategory);
|
||||
rv = error->InitWithWindowID(cspMsg, aSourceName,
|
||||
aSourceLine, aLineNumber,
|
||||
aColumnNumber, aFlags,
|
||||
catStr, aInnerWindowID);
|
||||
}
|
||||
else {
|
||||
rv = error->Init(cspMsg, aSourceName,
|
||||
aSourceLine, aLineNumber,
|
||||
aColumnNumber, aFlags,
|
||||
aCategory);
|
||||
}
|
||||
if (NS_FAILED(rv)) {
|
||||
return;
|
||||
}
|
||||
console->LogMessage(error);
|
||||
}
|
||||
|
||||
/* ===== Helpers ============================ */
|
||||
|
||||
nsCSPHostSrc*
|
||||
CSP_CreateHostSrcFromURI(nsIURI* aURI)
|
||||
{
|
||||
// Create the host first
|
||||
nsCString host;
|
||||
aURI->GetHost(host);
|
||||
nsCSPHostSrc *hostsrc = new nsCSPHostSrc(NS_ConvertUTF8toUTF16(host));
|
||||
|
||||
// Add the scheme.
|
||||
nsCString scheme;
|
||||
aURI->GetScheme(scheme);
|
||||
hostsrc->setScheme(NS_ConvertUTF8toUTF16(scheme));
|
||||
|
||||
int32_t port;
|
||||
aURI->GetPort(&port);
|
||||
// Only add port if it's not default port.
|
||||
if (port > 0) {
|
||||
nsAutoString portStr;
|
||||
portStr.AppendInt(port);
|
||||
hostsrc->setPort(portStr);
|
||||
}
|
||||
return hostsrc;
|
||||
}
|
||||
|
||||
bool
|
||||
CSP_IsValidDirective(const nsAString& aDir)
|
||||
{
|
||||
static_assert(CSP_LAST_DIRECTIVE_VALUE ==
|
||||
(sizeof(CSPStrDirectives) / sizeof(CSPStrDirectives[0])),
|
||||
"CSP_LAST_DIRECTIVE_VALUE does not match length of CSPStrDirectives");
|
||||
|
||||
for (uint32_t i = 0; i < CSP_LAST_DIRECTIVE_VALUE; i++) {
|
||||
if (aDir.LowerCaseEqualsASCII(CSPStrDirectives[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool
|
||||
CSP_IsDirective(const nsAString& aValue, enum CSPDirective aDir)
|
||||
{
|
||||
return aValue.LowerCaseEqualsASCII(CSP_EnumToDirective(aDir));
|
||||
}
|
||||
|
||||
bool
|
||||
CSP_IsKeyword(const nsAString& aValue, enum CSPKeyword aKey)
|
||||
{
|
||||
return aValue.LowerCaseEqualsASCII(CSP_EnumToKeyword(aKey));
|
||||
}
|
||||
|
||||
bool
|
||||
CSP_IsQuotelessKeyword(const nsAString& aKey)
|
||||
{
|
||||
nsString lowerKey = PromiseFlatString(aKey);
|
||||
ToLowerCase(lowerKey);
|
||||
|
||||
static_assert(CSP_LAST_KEYWORD_VALUE ==
|
||||
(sizeof(CSPStrKeywords) / sizeof(CSPStrKeywords[0])),
|
||||
"CSP_LAST_KEYWORD_VALUE does not match length of CSPStrKeywords");
|
||||
|
||||
nsAutoString keyword;
|
||||
for (uint32_t i = 0; i < CSP_LAST_KEYWORD_VALUE; i++) {
|
||||
// skipping the leading ' and trimming the trailing '
|
||||
keyword.AssignASCII(CSPStrKeywords[i] + 1);
|
||||
keyword.Trim("'", false, true);
|
||||
if (lowerKey.Equals(keyword)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* ===== nsCSPSrc ============================ */
|
||||
|
||||
nsCSPBaseSrc::nsCSPBaseSrc()
|
||||
{
|
||||
}
|
||||
|
||||
nsCSPBaseSrc::~nsCSPBaseSrc()
|
||||
{
|
||||
}
|
||||
|
||||
// ::permits is only called for external load requests, therefore:
|
||||
// nsCSPKeywordSrc and nsCSPHashSource fall back to this base class
|
||||
// implementation which will never allow the load.
|
||||
bool
|
||||
nsCSPBaseSrc::permits(nsIURI* aUri, const nsAString& aNonce) const
|
||||
{
|
||||
#ifdef PR_LOGGING
|
||||
{
|
||||
nsAutoCString spec;
|
||||
aUri->GetSpec(spec);
|
||||
CSPUTILSLOG(("nsCSPBaseSrc::permits, aUri: %s", spec.get()));
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
// ::allows is only called for inlined loads, therefore:
|
||||
// nsCSPSchemeSrc, nsCSPHostSrc fall back
|
||||
// to this base class implementation which will never allow the load.
|
||||
bool
|
||||
nsCSPBaseSrc::allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce) const
|
||||
{
|
||||
CSPUTILSLOG(("nsCSPBaseSrc::allows, aKeyWord: %s, a HashOrNonce: %s",
|
||||
aKeyword == CSP_HASH ? "hash" : CSP_EnumToKeyword(aKeyword),
|
||||
NS_ConvertUTF16toUTF8(aHashOrNonce).get()));
|
||||
return false;
|
||||
}
|
||||
|
||||
/* ====== nsCSPSchemeSrc ===================== */
|
||||
|
||||
nsCSPSchemeSrc::nsCSPSchemeSrc(const nsAString& aScheme)
|
||||
: mScheme(aScheme)
|
||||
{
|
||||
ToLowerCase(mScheme);
|
||||
}
|
||||
|
||||
nsCSPSchemeSrc::~nsCSPSchemeSrc()
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
nsCSPSchemeSrc::permits(nsIURI* aUri, const nsAString& aNonce) const
|
||||
{
|
||||
#ifdef PR_LOGGING
|
||||
{
|
||||
nsAutoCString spec;
|
||||
aUri->GetSpec(spec);
|
||||
CSPUTILSLOG(("nsCSPSchemeSrc::permits, aUri: %s", spec.get()));
|
||||
}
|
||||
#endif
|
||||
|
||||
NS_ASSERTION((!mScheme.EqualsASCII("")), "scheme can not be the empty string");
|
||||
nsAutoCString scheme;
|
||||
nsresult rv = aUri->GetScheme(scheme);
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
return mScheme.EqualsASCII(scheme.get());
|
||||
}
|
||||
|
||||
void
|
||||
nsCSPSchemeSrc::toString(nsAString& outStr) const
|
||||
{
|
||||
outStr.Append(mScheme);
|
||||
outStr.AppendASCII(":");
|
||||
}
|
||||
|
||||
/* ===== nsCSPHostSrc ======================== */
|
||||
|
||||
nsCSPHostSrc::nsCSPHostSrc(const nsAString& aHost)
|
||||
: mHost(aHost)
|
||||
{
|
||||
ToLowerCase(mHost);
|
||||
}
|
||||
|
||||
nsCSPHostSrc::~nsCSPHostSrc()
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
nsCSPHostSrc::permits(nsIURI* aUri, const nsAString& aNonce) const
|
||||
{
|
||||
#ifdef PR_LOGGING
|
||||
{
|
||||
nsAutoCString spec;
|
||||
aUri->GetSpec(spec);
|
||||
CSPUTILSLOG(("nsCSPHostSrc::permits, aUri: %s", spec.get()));
|
||||
}
|
||||
#endif
|
||||
|
||||
// If the host is defined as a "*", and:
|
||||
// a) no scheme, and
|
||||
// b) no port is defined, allow the load.
|
||||
// http://www.w3.org/TR/CSP11/#matching
|
||||
if (mHost.EqualsASCII("*") &&
|
||||
mScheme.IsEmpty() &&
|
||||
mPort.IsEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if the scheme matches.
|
||||
nsAutoCString scheme;
|
||||
nsresult rv = aUri->GetScheme(scheme);
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
if (!mScheme.EqualsASCII(scheme.get())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The host in nsCSpHostSrc should never be empty. In case we are enforcing
|
||||
// just a specific scheme, the parser should generate a nsCSPSchemeSource.
|
||||
NS_ASSERTION((!mHost.IsEmpty()), "host can not be the empty string");
|
||||
|
||||
// Extract the host part from aUri.
|
||||
nsAutoCString uriHost;
|
||||
rv = aUri->GetHost(uriHost);
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
|
||||
// Check it the allowed host starts with a wilcard.
|
||||
if (mHost.First() == '*') {
|
||||
// Eliminate leading "*." and check if uriHost ends with defined mHost.
|
||||
NS_ASSERTION(mHost[1] == '.', "Second character needs to be '.' whenever host starts with '*'");
|
||||
|
||||
nsString wildCardHost = mHost;
|
||||
wildCardHost = Substring(wildCardHost, 2, wildCardHost.Length() - 2);
|
||||
if (!StringEndsWith(NS_ConvertUTF8toUTF16(uriHost), wildCardHost)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Check if hosts match.
|
||||
else if (!mHost.Equals(NS_ConvertUTF8toUTF16(uriHost))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If port uses wildcard, allow the load.
|
||||
if (mPort.EqualsASCII("*")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if ports match
|
||||
int32_t uriPort;
|
||||
rv = aUri->GetPort(&uriPort);
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
uriPort = (uriPort > 0) ? uriPort : NS_GetDefaultPort(scheme.get());
|
||||
|
||||
// If mPort is empty, we have to compare default ports.
|
||||
if (mPort.IsEmpty()) {
|
||||
int32_t port = NS_GetDefaultPort(NS_ConvertUTF16toUTF8(mScheme).get());
|
||||
if (port != uriPort) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Otherwise compare the ports
|
||||
else {
|
||||
nsString portStr;
|
||||
portStr.AppendInt(uriPort);
|
||||
if (!mPort.Equals(portStr)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// At the end: scheme, host, port, match; allow the load.
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
nsCSPHostSrc::toString(nsAString& outStr) const
|
||||
{
|
||||
// If mHost is a single "*", we append the wildcard and return.
|
||||
if (mHost.EqualsASCII("*") &&
|
||||
mScheme.IsEmpty() &&
|
||||
mPort.IsEmpty()) {
|
||||
outStr.Append(mHost);
|
||||
return;
|
||||
}
|
||||
|
||||
// append scheme
|
||||
outStr.Append(mScheme);
|
||||
|
||||
// append host
|
||||
outStr.AppendASCII("://");
|
||||
outStr.Append(mHost);
|
||||
|
||||
// append port
|
||||
if (!mPort.IsEmpty()) {
|
||||
outStr.AppendASCII(":");
|
||||
outStr.Append(mPort);
|
||||
}
|
||||
|
||||
// in CSP 1.1, paths are ignoed
|
||||
// outStr.Append(mPath);
|
||||
// outStr.Append(mFileAndArguments);
|
||||
}
|
||||
|
||||
void
|
||||
nsCSPHostSrc::setScheme(const nsAString& aScheme)
|
||||
{
|
||||
mScheme = aScheme;
|
||||
ToLowerCase(mScheme);
|
||||
}
|
||||
|
||||
void
|
||||
nsCSPHostSrc::setPort(const nsAString& aPort)
|
||||
{
|
||||
mPort = aPort;
|
||||
ToLowerCase(mPort);
|
||||
}
|
||||
|
||||
void
|
||||
nsCSPHostSrc::appendPath(const nsAString& aPath)
|
||||
{
|
||||
mPath.Append(aPath);
|
||||
ToLowerCase(mPath);
|
||||
}
|
||||
|
||||
void
|
||||
nsCSPHostSrc::setFileAndArguments(const nsAString& aFile)
|
||||
{
|
||||
mFileAndArguments = aFile;
|
||||
ToLowerCase(mFileAndArguments);
|
||||
}
|
||||
|
||||
/* ===== nsCSPKeywordSrc ===================== */
|
||||
|
||||
nsCSPKeywordSrc::nsCSPKeywordSrc(CSPKeyword aKeyword)
|
||||
{
|
||||
NS_ASSERTION((aKeyword != CSP_SELF),
|
||||
"'self' should have been replaced in the parser");
|
||||
mKeyword = aKeyword;
|
||||
}
|
||||
|
||||
nsCSPKeywordSrc::~nsCSPKeywordSrc()
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
nsCSPKeywordSrc::allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce) const
|
||||
{
|
||||
CSPUTILSLOG(("nsCSPKeywordSrc::allows, aKeyWord: %s, a HashOrNonce: %s",
|
||||
CSP_EnumToKeyword(aKeyword), NS_ConvertUTF16toUTF8(aHashOrNonce).get()));
|
||||
return mKeyword == aKeyword;
|
||||
}
|
||||
|
||||
void
|
||||
nsCSPKeywordSrc::toString(nsAString& outStr) const
|
||||
{
|
||||
outStr.AppendASCII(CSP_EnumToKeyword(mKeyword));
|
||||
}
|
||||
|
||||
/* ===== nsCSPNonceSrc ==================== */
|
||||
|
||||
nsCSPNonceSrc::nsCSPNonceSrc(const nsAString& aNonce)
|
||||
: mNonce(aNonce)
|
||||
{
|
||||
}
|
||||
|
||||
nsCSPNonceSrc::~nsCSPNonceSrc()
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
nsCSPNonceSrc::permits(nsIURI* aUri, const nsAString& aNonce) const
|
||||
{
|
||||
#ifdef PR_LOGGING
|
||||
{
|
||||
nsAutoCString spec;
|
||||
aUri->GetSpec(spec);
|
||||
CSPUTILSLOG(("nsCSPNonceSrc::permits, aUri: %s, aNonce: %s",
|
||||
spec.get(), NS_ConvertUTF16toUTF8(aNonce).get()));
|
||||
}
|
||||
#endif
|
||||
|
||||
return mNonce.Equals(aNonce);
|
||||
}
|
||||
|
||||
bool
|
||||
nsCSPNonceSrc::allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce) const
|
||||
{
|
||||
CSPUTILSLOG(("nsCSPNonceSrc::allows, aKeyWord: %s, a HashOrNonce: %s",
|
||||
CSP_EnumToKeyword(aKeyword), NS_ConvertUTF16toUTF8(aHashOrNonce).get()));
|
||||
|
||||
if (aKeyword != CSP_NONCE) {
|
||||
return false;
|
||||
}
|
||||
return mNonce.Equals(aHashOrNonce);
|
||||
}
|
||||
|
||||
void
|
||||
nsCSPNonceSrc::toString(nsAString& outStr) const
|
||||
{
|
||||
outStr.AppendASCII(CSP_EnumToKeyword(CSP_NONCE));
|
||||
outStr.Append(mNonce);
|
||||
outStr.AppendASCII("'");
|
||||
}
|
||||
|
||||
/* ===== nsCSPHashSrc ===================== */
|
||||
|
||||
nsCSPHashSrc::nsCSPHashSrc(const nsAString& aAlgo, const nsAString& aHash)
|
||||
: mAlgorithm(aAlgo)
|
||||
, mHash(aHash)
|
||||
{
|
||||
// Only the algo should be rewritten to lowercase, the hash must remain the same.
|
||||
ToLowerCase(mAlgorithm);
|
||||
}
|
||||
|
||||
nsCSPHashSrc::~nsCSPHashSrc()
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
nsCSPHashSrc::allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce) const
|
||||
{
|
||||
CSPUTILSLOG(("nsCSPHashSrc::allows, aKeyWord: %s, a HashOrNonce: %s",
|
||||
CSP_EnumToKeyword(aKeyword), NS_ConvertUTF16toUTF8(aHashOrNonce).get()));
|
||||
|
||||
if (aKeyword != CSP_HASH) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Convert aHashOrNonce to UTF-8
|
||||
NS_ConvertUTF16toUTF8 utf8_hash(aHashOrNonce);
|
||||
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsICryptoHash> hasher;
|
||||
hasher = do_CreateInstance("@mozilla.org/security/hash;1", &rv);
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
|
||||
rv = hasher->InitWithString(NS_ConvertUTF16toUTF8(mAlgorithm));
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
|
||||
rv = hasher->Update((uint8_t *)utf8_hash.get(), utf8_hash.Length());
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
|
||||
nsAutoCString hash;
|
||||
rv = hasher->Finish(true, hash);
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
|
||||
// The NSS Base64 encoder automatically adds linebreaks "\r\n" every 64
|
||||
// characters. We need to remove these so we can properly validate longer
|
||||
// (SHA-512) base64-encoded hashes
|
||||
hash.StripChars("\r\n");
|
||||
return NS_ConvertUTF16toUTF8(mHash).Equals(hash);
|
||||
}
|
||||
|
||||
void
|
||||
nsCSPHashSrc::toString(nsAString& outStr) const
|
||||
{
|
||||
outStr.AppendASCII("'");
|
||||
outStr.Append(mAlgorithm);
|
||||
outStr.AppendASCII("-");
|
||||
outStr.Append(mHash);
|
||||
outStr.AppendASCII("'");
|
||||
}
|
||||
|
||||
/* ===== nsCSPReportURI ===================== */
|
||||
|
||||
nsCSPReportURI::nsCSPReportURI(nsIURI *aURI)
|
||||
:mReportURI(aURI)
|
||||
{
|
||||
}
|
||||
|
||||
nsCSPReportURI::~nsCSPReportURI()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
nsCSPReportURI::toString(nsAString& outStr) const
|
||||
{
|
||||
nsAutoCString spec;
|
||||
nsresult rv = mReportURI->GetSpec(spec);
|
||||
if (NS_FAILED(rv)) {
|
||||
return;
|
||||
}
|
||||
outStr.AppendASCII(spec.get());
|
||||
}
|
||||
|
||||
/* ===== nsCSPDirective ====================== */
|
||||
|
||||
nsCSPDirective::nsCSPDirective(enum CSPDirective aDirective)
|
||||
{
|
||||
mDirective = aDirective;
|
||||
}
|
||||
|
||||
nsCSPDirective::~nsCSPDirective()
|
||||
{
|
||||
for (uint32_t i = 0; i < mSrcs.Length(); i++) {
|
||||
delete mSrcs[i];
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
nsCSPDirective::permits(nsIURI* aUri, const nsAString& aNonce) const
|
||||
{
|
||||
#ifdef PR_LOGGING
|
||||
{
|
||||
nsAutoCString spec;
|
||||
aUri->GetSpec(spec);
|
||||
CSPUTILSLOG(("nsCSPDirective::permits, aUri: %s", spec.get()));
|
||||
}
|
||||
#endif
|
||||
|
||||
for (uint32_t i = 0; i < mSrcs.Length(); i++) {
|
||||
if (mSrcs[i]->permits(aUri, aNonce)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
nsCSPDirective::allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce) const
|
||||
{
|
||||
CSPUTILSLOG(("nsCSPDirective::allows, aKeyWord: %s, a HashOrNonce: %s",
|
||||
CSP_EnumToKeyword(aKeyword), NS_ConvertUTF16toUTF8(aHashOrNonce).get()));
|
||||
|
||||
for (uint32_t i = 0; i < mSrcs.Length(); i++) {
|
||||
if (mSrcs[i]->allows(aKeyword, aHashOrNonce)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
nsCSPDirective::toString(nsAString& outStr) const
|
||||
{
|
||||
// Append directive name
|
||||
outStr.AppendASCII(CSP_EnumToDirective(mDirective));
|
||||
outStr.AppendASCII(" ");
|
||||
|
||||
// Append srcs
|
||||
uint32_t length = mSrcs.Length();
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
mSrcs[i]->toString(outStr);
|
||||
if (i != (length - 1)) {
|
||||
outStr.AppendASCII(" ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsContentPolicyType
|
||||
CSP_DirectiveToContentType(enum CSPDirective aDir)
|
||||
{
|
||||
switch (aDir) {
|
||||
case CSP_IMG_SRC: return nsIContentPolicy::TYPE_IMAGE;
|
||||
case CSP_SCRIPT_SRC: return nsIContentPolicy::TYPE_SCRIPT;
|
||||
case CSP_STYLE_SRC: return nsIContentPolicy::TYPE_STYLESHEET;
|
||||
case CSP_FONT_SRC: return nsIContentPolicy::TYPE_FONT;
|
||||
case CSP_MEDIA_SRC: return nsIContentPolicy::TYPE_MEDIA;
|
||||
case CSP_OBJECT_SRC: return nsIContentPolicy::TYPE_OBJECT;
|
||||
case CSP_FRAME_SRC: return nsIContentPolicy::TYPE_SUBDOCUMENT;
|
||||
|
||||
// Fall through to error for the following Directives:
|
||||
case CSP_DEFAULT_SRC:
|
||||
case CSP_CONNECT_SRC:
|
||||
case CSP_REPORT_URI:
|
||||
case CSP_LAST_DIRECTIVE_VALUE:
|
||||
default:
|
||||
NS_ASSERTION(false, "Can not convert CSPDirective into nsContentPolicyType");
|
||||
}
|
||||
return nsIContentPolicy::TYPE_OTHER;
|
||||
}
|
||||
|
||||
bool
|
||||
nsCSPDirective::directiveNameEqualsContentType(nsContentPolicyType aContentType) const
|
||||
{
|
||||
// make sure we do not check for the default src before any other sources
|
||||
if (isDefaultDirective()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// BLock XSLT as script, see bug 910139
|
||||
if (aContentType == nsIContentPolicy::TYPE_XSLT) {
|
||||
aContentType = nsIContentPolicy::TYPE_SCRIPT;
|
||||
}
|
||||
return aContentType == CSP_DirectiveToContentType(mDirective);
|
||||
}
|
||||
|
||||
/* ===== nsCSPPolicy ========================= */
|
||||
|
||||
nsCSPPolicy::nsCSPPolicy()
|
||||
: mReportOnly(false)
|
||||
{
|
||||
CSPUTILSLOG(("nsCSPPolicy::nsCSPPolicy"));
|
||||
}
|
||||
|
||||
nsCSPPolicy::~nsCSPPolicy()
|
||||
{
|
||||
CSPUTILSLOG(("nsCSPPolicy::~nsCSPPolicy"));
|
||||
|
||||
for (uint32_t i = 0; i < mDirectives.Length(); i++) {
|
||||
delete mDirectives[i];
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
nsCSPPolicy::permits(nsContentPolicyType aContentType,
|
||||
nsIURI* aUri,
|
||||
const nsAString& aNonce,
|
||||
nsAString& outViolatedDirective) const
|
||||
{
|
||||
#ifdef PR_LOGGING
|
||||
{
|
||||
nsAutoCString spec;
|
||||
aUri->GetSpec(spec);
|
||||
CSPUTILSLOG(("nsCSPPolicy::permits, aContentType: %d, aUri: %s, aNonce: %s",
|
||||
aContentType, spec.get(), NS_ConvertUTF16toUTF8(aNonce).get()));
|
||||
}
|
||||
#endif
|
||||
|
||||
NS_ASSERTION(aUri, "permits needs an uri to perform the check!");
|
||||
|
||||
nsCSPDirective* defaultDir = nullptr;
|
||||
|
||||
// These directive arrays are short (1-5 elements), not worth using a hashtable.
|
||||
|
||||
for (uint32_t i = 0; i < mDirectives.Length(); i++) {
|
||||
// Check if the directive name matches
|
||||
if (mDirectives[i]->directiveNameEqualsContentType(aContentType)) {
|
||||
if (!mDirectives[i]->permits(aUri, aNonce)) {
|
||||
mDirectives[i]->toString(outViolatedDirective);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (mDirectives[i]->isDefaultDirective()) {
|
||||
defaultDir = mDirectives[i];
|
||||
}
|
||||
}
|
||||
|
||||
// If the above loop runs through, we haven't found a matching directive.
|
||||
// Avoid relooping, just store the result of default-src while looping.
|
||||
if (defaultDir) {
|
||||
if (!defaultDir->permits(aUri, aNonce)) {
|
||||
defaultDir->toString(outViolatedDirective);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Didn't find a directive, load is not allowed.
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
nsCSPPolicy::allows(nsContentPolicyType aContentType,
|
||||
enum CSPKeyword aKeyword,
|
||||
const nsAString& aHashOrNonce) const
|
||||
{
|
||||
CSPUTILSLOG(("nsCSPPolicy::allows, aKeyWord: %s, a HashOrNonce: %s",
|
||||
CSP_EnumToKeyword(aKeyword), NS_ConvertUTF16toUTF8(aHashOrNonce).get()));
|
||||
|
||||
nsCSPDirective* defaultDir = nullptr;
|
||||
|
||||
// Try to find a matching directive
|
||||
for (uint32_t i = 0; i < mDirectives.Length(); i++) {
|
||||
if (mDirectives[i]->directiveNameEqualsContentType(aContentType)) {
|
||||
if (mDirectives[i]->allows(aKeyword, aHashOrNonce)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (mDirectives[i]->isDefaultDirective()) {
|
||||
defaultDir = mDirectives[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Only match {nonce,hash}-source on specific directives (not default-src)
|
||||
if (aKeyword == CSP_NONCE || aKeyword == CSP_HASH) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the above loop runs through, we haven't found a matching directive.
|
||||
// Avoid relooping, just store the result of default-src while looping.
|
||||
if (defaultDir) {
|
||||
return defaultDir->allows(aKeyword, aHashOrNonce);
|
||||
}
|
||||
|
||||
// Allowing the load; see Bug 885433
|
||||
// a) inline scripts (also unsafe eval) should only be blocked
|
||||
// if there is a [script-src] or [default-src]
|
||||
// b) inline styles should only be blocked
|
||||
// if there is a [style-src] or [default-src]
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
nsCSPPolicy::allows(nsContentPolicyType aContentType,
|
||||
enum CSPKeyword aKeyword) const
|
||||
{
|
||||
return allows(aContentType, aKeyword, NS_LITERAL_STRING(""));
|
||||
}
|
||||
|
||||
void
|
||||
nsCSPPolicy::toString(nsAString& outStr) const
|
||||
{
|
||||
uint32_t length = mDirectives.Length();
|
||||
for (uint32_t i = 0; i < length; ++i) {
|
||||
mDirectives[i]->toString(outStr);
|
||||
if (i != (length - 1)) {
|
||||
outStr.AppendASCII("; ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
nsCSPPolicy::directiveExists(enum CSPDirective aDir) const
|
||||
{
|
||||
for (uint32_t i = 0; i < mDirectives.Length(); i++) {
|
||||
if (mDirectives[i]->equals(aDir)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
339
content/base/src/nsCSPUtils.h
Normal file
339
content/base/src/nsCSPUtils.h
Normal file
@ -0,0 +1,339 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef nsCSPUtils_h___
|
||||
#define nsCSPUtils_h___
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIContentPolicy.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsString.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsUnicharUtils.h"
|
||||
#include "prlog.h"
|
||||
|
||||
/* =============== Logging =================== */
|
||||
|
||||
void CSP_GetLocalizedStr(const char16_t* aName,
|
||||
const char16_t** aParams,
|
||||
uint32_t aLength,
|
||||
char16_t** outResult);
|
||||
|
||||
void CSP_LogStrMessage(const nsAString& aMsg);
|
||||
|
||||
void CSP_LogMessage(const nsAString& aMessage,
|
||||
const nsAString& aSourceName,
|
||||
const nsAString& aSourceLine,
|
||||
uint32_t aLineNumber,
|
||||
uint32_t aColumnNumber,
|
||||
uint32_t aFlags,
|
||||
const char* aCategory,
|
||||
uint32_t aInnerWindowID);
|
||||
|
||||
|
||||
/* =============== Definitions ================== */
|
||||
|
||||
// Please add any new enum items not only to CSPDirective, but also add
|
||||
// a string version for every enum >> using the same index << to
|
||||
// CSPStrDirectives underneath.
|
||||
enum CSPDirective {
|
||||
CSP_DEFAULT_SRC = 0,
|
||||
CSP_SCRIPT_SRC,
|
||||
CSP_OBJECT_SRC,
|
||||
CSP_STYLE_SRC,
|
||||
CSP_IMG_SRC,
|
||||
CSP_MEDIA_SRC,
|
||||
CSP_FRAME_SRC,
|
||||
CSP_FONT_SRC,
|
||||
CSP_CONNECT_SRC,
|
||||
CSP_REPORT_URI,
|
||||
// CSP_LAST_DIRECTIVE_VALUE always needs to be the last element in the enum
|
||||
// because we use it to calculate the size for the char* array.
|
||||
CSP_LAST_DIRECTIVE_VALUE
|
||||
};
|
||||
|
||||
static const char* CSPStrDirectives[] = {
|
||||
"default-src", // CSP_DEFAULT_SRC = 0
|
||||
"script-src", // CSP_SCRIPT_SRC
|
||||
"object-src", // CSP_OBJECT_SRC
|
||||
"style-src", // CSP_STYLE_SRC
|
||||
"img-src", // CSP_IMG_SRC
|
||||
"media-src", // CSP_MEDIA_SRC
|
||||
"frame-src", // CSP_FRAME_SRC
|
||||
"font-src", // CSP_FONT_SRC
|
||||
"connect-src", // CSP_CONNECT_SRC
|
||||
"report-uri", // CSP_REPORT_URI
|
||||
};
|
||||
|
||||
inline const char* CSP_EnumToDirective(enum CSPDirective aDir)
|
||||
{
|
||||
// Make sure all elements in enum CSPDirective got added to CSPStrDirectives.
|
||||
static_assert((sizeof(CSPStrDirectives) / sizeof(CSPStrDirectives[0]) ==
|
||||
static_cast<uint32_t>(CSP_LAST_DIRECTIVE_VALUE)),
|
||||
"CSP_LAST_DIRECTIVE_VALUE does not match length of CSPStrDirectives");
|
||||
return CSPStrDirectives[static_cast<uint32_t>(aDir)];
|
||||
}
|
||||
|
||||
inline CSPDirective CSP_DirectiveToEnum(const nsAString& aDir)
|
||||
{
|
||||
nsString lowerDir = PromiseFlatString(aDir);
|
||||
ToLowerCase(lowerDir);
|
||||
|
||||
static_assert(CSP_LAST_DIRECTIVE_VALUE ==
|
||||
(sizeof(CSPStrDirectives) / sizeof(CSPStrDirectives[0])),
|
||||
"CSP_LAST_DIRECTIVE_VALUE does not match length of CSPStrDirectives");
|
||||
|
||||
for (uint32_t i = 0; i < CSP_LAST_DIRECTIVE_VALUE; i++) {
|
||||
if (lowerDir.EqualsASCII(CSPStrDirectives[i])) {
|
||||
return static_cast<CSPDirective>(i);
|
||||
}
|
||||
}
|
||||
NS_ASSERTION(false, "Can not convert unknown Directive to Enum");
|
||||
return CSP_LAST_DIRECTIVE_VALUE;
|
||||
}
|
||||
|
||||
// Please add any new enum items not only to CSPKeyword, but also add
|
||||
// a string version for every enum >> using the same index << to
|
||||
// CSPStrKeywords underneath.
|
||||
enum CSPKeyword {
|
||||
CSP_SELF = 0,
|
||||
CSP_UNSAFE_INLINE,
|
||||
CSP_UNSAFE_EVAL,
|
||||
CSP_NONE,
|
||||
CSP_NONCE,
|
||||
// CSP_LAST_KEYWORD_VALUE always needs to be the last element in the enum
|
||||
// because we use it to calculate the size for the char* array.
|
||||
CSP_LAST_KEYWORD_VALUE,
|
||||
// Putting CSP_HASH after the delimitor, because CSP_HASH is not a valid
|
||||
// keyword (hash uses e.g. sha256, sha512) but we use CSP_HASH internally
|
||||
// to identify allowed hashes in ::allows.
|
||||
CSP_HASH
|
||||
};
|
||||
|
||||
static const char* CSPStrKeywords[] = {
|
||||
"'self'", // CSP_SELF = 0
|
||||
"'unsafe-inline'", // CSP_UNSAFE_INLINE
|
||||
"'unsafe-eval'", // CSP_UNSAFE_EVAL
|
||||
"'none'", // CSP_NONE
|
||||
"'nonce-", // CSP_NONCE
|
||||
// Remember: CSP_HASH is not supposed to be used
|
||||
};
|
||||
|
||||
inline const char* CSP_EnumToKeyword(enum CSPKeyword aKey)
|
||||
{
|
||||
// Make sure all elements in enum CSPKeyword got added to CSPStrKeywords.
|
||||
static_assert((sizeof(CSPStrKeywords) / sizeof(CSPStrKeywords[0]) ==
|
||||
static_cast<uint32_t>(CSP_LAST_KEYWORD_VALUE)),
|
||||
"CSP_LAST_KEYWORD_VALUE does not match length of CSPStrKeywords");
|
||||
return CSPStrKeywords[static_cast<uint32_t>(aKey)];
|
||||
}
|
||||
|
||||
inline CSPKeyword CSP_KeywordToEnum(const nsAString& aKey)
|
||||
{
|
||||
nsString lowerKey = PromiseFlatString(aKey);
|
||||
ToLowerCase(lowerKey);
|
||||
|
||||
static_assert(CSP_LAST_KEYWORD_VALUE ==
|
||||
(sizeof(CSPStrKeywords) / sizeof(CSPStrKeywords[0])),
|
||||
"CSP_LAST_KEYWORD_VALUE does not match length of CSPStrKeywords");
|
||||
|
||||
for (uint32_t i = 0; i < CSP_LAST_KEYWORD_VALUE; i++) {
|
||||
if (lowerKey.EqualsASCII(CSPStrKeywords[i])) {
|
||||
return static_cast<CSPKeyword>(i);
|
||||
}
|
||||
}
|
||||
NS_ASSERTION(false, "Can not convert unknown Keyword to Enum");
|
||||
return CSP_LAST_KEYWORD_VALUE;
|
||||
}
|
||||
|
||||
/* =============== Helpers ================== */
|
||||
|
||||
class nsCSPHostSrc;
|
||||
|
||||
nsCSPHostSrc* CSP_CreateHostSrcFromURI(nsIURI* aURI);
|
||||
bool CSP_IsValidDirective(const nsAString& aDir);
|
||||
bool CSP_IsDirective(const nsAString& aValue, enum CSPDirective aDir);
|
||||
bool CSP_IsKeyword(const nsAString& aValue, enum CSPKeyword aKey);
|
||||
bool CSP_IsQuotelessKeyword(const nsAString& aKey);
|
||||
|
||||
/* =============== nsCSPSrc ================== */
|
||||
|
||||
class nsCSPBaseSrc {
|
||||
public:
|
||||
nsCSPBaseSrc();
|
||||
virtual ~nsCSPBaseSrc();
|
||||
|
||||
virtual bool permits(nsIURI* aUri, const nsAString& aNonce) const;
|
||||
virtual bool allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce) const;
|
||||
virtual void toString(nsAString& outStr) const = 0;
|
||||
};
|
||||
|
||||
/* =============== nsCSPSchemeSrc ============ */
|
||||
|
||||
class nsCSPSchemeSrc : public nsCSPBaseSrc {
|
||||
public:
|
||||
nsCSPSchemeSrc(const nsAString& aScheme);
|
||||
virtual ~nsCSPSchemeSrc();
|
||||
|
||||
bool permits(nsIURI* aUri, const nsAString& aNonce) const;
|
||||
void toString(nsAString& outStr) const;
|
||||
|
||||
private:
|
||||
nsString mScheme;
|
||||
};
|
||||
|
||||
/* =============== nsCSPHostSrc ============== */
|
||||
|
||||
class nsCSPHostSrc : public nsCSPBaseSrc {
|
||||
public:
|
||||
nsCSPHostSrc(const nsAString& aHost);
|
||||
virtual ~nsCSPHostSrc();
|
||||
|
||||
bool permits(nsIURI* aUri, const nsAString& aNonce) const;
|
||||
void toString(nsAString& outStr) const;
|
||||
|
||||
void setScheme(const nsAString& aScheme);
|
||||
void setPort(const nsAString& aPort);
|
||||
void appendPath(const nsAString &aPath);
|
||||
void setFileAndArguments(const nsAString& aFile);
|
||||
|
||||
private:
|
||||
nsString mScheme;
|
||||
nsString mHost;
|
||||
nsString mPort;
|
||||
nsString mPath;
|
||||
nsString mFileAndArguments;
|
||||
};
|
||||
|
||||
/* =============== nsCSPKeywordSrc ============ */
|
||||
|
||||
class nsCSPKeywordSrc : public nsCSPBaseSrc {
|
||||
public:
|
||||
nsCSPKeywordSrc(CSPKeyword aKeyword);
|
||||
virtual ~nsCSPKeywordSrc();
|
||||
|
||||
bool allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce) const;
|
||||
void toString(nsAString& outStr) const;
|
||||
|
||||
private:
|
||||
CSPKeyword mKeyword;
|
||||
};
|
||||
|
||||
/* =============== nsCSPNonceSource =========== */
|
||||
|
||||
class nsCSPNonceSrc : public nsCSPBaseSrc {
|
||||
public:
|
||||
nsCSPNonceSrc(const nsAString& aNonce);
|
||||
virtual ~nsCSPNonceSrc();
|
||||
|
||||
bool permits(nsIURI* aUri, const nsAString& aNonce) const;
|
||||
bool allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce) const;
|
||||
void toString(nsAString& outStr) const;
|
||||
|
||||
private:
|
||||
nsString mNonce;
|
||||
};
|
||||
|
||||
/* =============== nsCSPHashSource ============ */
|
||||
|
||||
class nsCSPHashSrc : public nsCSPBaseSrc {
|
||||
public:
|
||||
nsCSPHashSrc(const nsAString& algo, const nsAString& hash);
|
||||
virtual ~nsCSPHashSrc();
|
||||
|
||||
bool allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce) const;
|
||||
void toString(nsAString& outStr) const;
|
||||
|
||||
private:
|
||||
nsString mAlgorithm;
|
||||
nsString mHash;
|
||||
};
|
||||
|
||||
/* =============== nsCSPReportURI ============ */
|
||||
|
||||
class nsCSPReportURI : public nsCSPBaseSrc {
|
||||
public:
|
||||
nsCSPReportURI(nsIURI *aURI);
|
||||
virtual ~nsCSPReportURI();
|
||||
|
||||
void toString(nsAString& outStr) const;
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIURI> mReportURI;
|
||||
};
|
||||
|
||||
/* =============== nsCSPDirective ============= */
|
||||
|
||||
class nsCSPDirective {
|
||||
public:
|
||||
nsCSPDirective();
|
||||
nsCSPDirective(enum CSPDirective aDirective);
|
||||
virtual ~nsCSPDirective();
|
||||
|
||||
bool permits(nsIURI* aUri, const nsAString& aNonce) const;
|
||||
bool allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce) const;
|
||||
void toString(nsAString& outStr) const;
|
||||
|
||||
inline void addSrcs(const nsTArray<nsCSPBaseSrc*>& aSrcs)
|
||||
{ mSrcs = aSrcs; }
|
||||
|
||||
bool directiveNameEqualsContentType(nsContentPolicyType aContentType) const;
|
||||
|
||||
inline bool isDefaultDirective() const
|
||||
{ return mDirective == CSP_DEFAULT_SRC; }
|
||||
|
||||
inline bool equals(enum CSPDirective aDirective) const
|
||||
{ return (mDirective == aDirective); }
|
||||
|
||||
void getReportURIs(nsTArray<nsString> &outReportURIs) const;
|
||||
|
||||
private:
|
||||
CSPDirective mDirective;
|
||||
nsTArray<nsCSPBaseSrc*> mSrcs;
|
||||
};
|
||||
|
||||
/* =============== nsCSPPolicy ================== */
|
||||
|
||||
class nsCSPPolicy {
|
||||
public:
|
||||
nsCSPPolicy();
|
||||
virtual ~nsCSPPolicy();
|
||||
|
||||
bool permits(nsContentPolicyType aContentType,
|
||||
nsIURI* aUri,
|
||||
const nsAString& aNonce,
|
||||
nsAString& outViolatedDirective) const;
|
||||
bool allows(nsContentPolicyType aContentType,
|
||||
enum CSPKeyword aKeyword,
|
||||
const nsAString& aHashOrNonce) const;
|
||||
bool allows(nsContentPolicyType aContentType,
|
||||
enum CSPKeyword aKeyword) const;
|
||||
void toString(nsAString& outStr) const;
|
||||
|
||||
inline void addDirective(nsCSPDirective* aDir)
|
||||
{ mDirectives.AppendElement(aDir); }
|
||||
|
||||
bool directiveExists(enum CSPDirective aDir) const;
|
||||
|
||||
inline void setReportOnlyFlag(bool aFlag)
|
||||
{ mReportOnly = aFlag; }
|
||||
|
||||
inline bool getReportOnlyFlag() const
|
||||
{ return mReportOnly; }
|
||||
|
||||
void getReportURIs(nsTArray<nsString> &outReportURIs) const;
|
||||
|
||||
void getDirectiveStringForContentType(nsContentPolicyType aContentType,
|
||||
nsAString& outDirective) const;
|
||||
|
||||
inline uint32_t getNumDirectives() const
|
||||
{ return mDirectives.Length(); }
|
||||
|
||||
private:
|
||||
nsTArray<nsCSPDirective*> mDirectives;
|
||||
bool mReportOnly;
|
||||
};
|
||||
|
||||
#endif /* nsCSPUtils_h___ */
|
Loading…
Reference in New Issue
Block a user