diff options
Diffstat (limited to 'dom/security/nsCSPParser.h')
-rw-r--r-- | dom/security/nsCSPParser.h | 262 |
1 files changed, 262 insertions, 0 deletions
diff --git a/dom/security/nsCSPParser.h b/dom/security/nsCSPParser.h new file mode 100644 index 000000000..30954b10f --- /dev/null +++ b/dom/security/nsCSPParser.h @@ -0,0 +1,262 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#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 && + nsContentUtils::IsHTMLWhitespace(*mCurChar)) { + mCurToken.Append(*mCurChar++); + } + mCurToken.Truncate(); + } + + inline void skipWhiteSpaceAndSemicolon() + { + while (mCurChar < mEndChar && (*mCurChar == ';' || + nsContentUtils::IsHTMLWhitespace(*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, + nsCSPContext* aCSPContext, + bool aDeliveredViaMetaTag); + + private: + nsCSPParser(cspTokens& aTokens, + nsIURI* aSelfURI, + nsCSPContext* aCSPContext, + bool aDeliveredViaMetaTag); + + static bool sCSPExperimentalEnabled; + static bool sStrictDynamicEnabled; + + ~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 requireSRIForDirectiveValue(nsRequireSRIForDirective* aDir); + void referrerDirectiveValue(nsCSPDirective* aDir); + void reportURIList(nsCSPDirective* aDir); + void sandboxFlagList(nsCSPDirective* aDir); + 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 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 + + 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(); + } + + bool atEndOfPath(); + bool atValidPathChar(); + + 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; + + // helpers to allow invalidation of srcs within script-src and style-src + // if either 'strict-dynamic' or at least a hash or nonce is present. + bool mHasHashOrNonce; // false, if no hash or nonce is defined + bool mStrictDynamic; // false, if 'strict-dynamic' is not defined + nsCSPKeywordSrc* mUnsafeInlineKeywordSrc; // null, otherwise invlidate() + + // cache variables for child-src and frame-src directive handling. + // frame-src is deprecated in favor of child-src, however if we + // see a frame-src directive, it takes precedence for frames and iframes. + // At the end of parsing, if we have a child-src directive, we need to + // decide whether it will handle frames, or if there is a frame-src we + // should honor instead. + nsCSPChildSrcDirective* mChildSrc; + nsCSPDirective* mFrameSrc; + + cspTokens mTokens; + nsIURI* mSelfURI; + nsCSPPolicy* mPolicy; + nsCSPContext* mCSPContext; // used for console logging + bool mDeliveredViaMetaTag; +}; + +#endif /* nsCSPParser_h___ */ |