mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-28 23:31:56 +00:00
f66c3665b0
Differential Revision: https://phabricator.services.mozilla.com/D163815
218 lines
7.6 KiB
C++
218 lines
7.6 KiB
C++
/* -*- 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 "nsCSPContext.h"
|
|
#include "nsIURI.h"
|
|
#include "PolicyTokenizer.h"
|
|
|
|
bool isNumberToken(char16_t aSymbol);
|
|
bool isValidHexDig(char16_t aHexDig);
|
|
|
|
// clang-format off
|
|
const char16_t COLON = ':';
|
|
const char16_t SEMICOLON = ';';
|
|
const char16_t SLASH = '/';
|
|
const char16_t PLUS = '+';
|
|
const char16_t DASH = '-';
|
|
const char16_t DOT = '.';
|
|
const char16_t UNDERLINE = '_';
|
|
const char16_t TILDE = '~';
|
|
const char16_t WILDCARD = '*';
|
|
const char16_t SINGLEQUOTE = '\'';
|
|
const char16_t NUMBER_SIGN = '#';
|
|
const char16_t QUESTIONMARK = '?';
|
|
const char16_t PERCENT_SIGN = '%';
|
|
const char16_t EXCLAMATION = '!';
|
|
const char16_t DOLLAR = '$';
|
|
const char16_t AMPERSAND = '&';
|
|
const char16_t OPENBRACE = '(';
|
|
const char16_t CLOSINGBRACE = ')';
|
|
const char16_t EQUALS = '=';
|
|
const char16_t ATSYMBOL = '@';
|
|
// clang-format on
|
|
|
|
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,
|
|
bool aSuppressLogMessages);
|
|
|
|
private:
|
|
nsCSPParser(policyTokens& aTokens, nsIURI* aSelfURI,
|
|
nsCSPContext* aCSPContext, bool aDeliveredViaMetaTag,
|
|
bool aSuppressLogMessages);
|
|
|
|
~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 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* 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 nsTArray<nsString>& aParams);
|
|
|
|
/**
|
|
* 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 mHasAnyUnsafeEval; // false, if no (wasm-)unsafe-eval keyword is used.
|
|
bool mStrictDynamic; // false, if 'strict-dynamic' is not defined
|
|
nsCSPKeywordSrc* mUnsafeInlineKeywordSrc; // null, otherwise invlidate()
|
|
|
|
// cache variables for child-src, frame-src and worker-src handling;
|
|
// in CSP 3 child-src is deprecated. For backwards compatibility
|
|
// child-src needs to restrict:
|
|
// (*) frames, in case frame-src is not expicitly specified
|
|
// (*) workers, in case worker-src is not expicitly specified
|
|
// If neither worker-src, nor child-src is present, then script-src
|
|
// needs to govern workers.
|
|
nsCSPChildSrcDirective* mChildSrc;
|
|
nsCSPDirective* mFrameSrc;
|
|
nsCSPDirective* mWorkerSrc;
|
|
nsCSPScriptSrcDirective* mScriptSrc;
|
|
nsCSPStyleSrcDirective* mStyleSrc;
|
|
|
|
// cache variable to let nsCSPHostSrc know that it's within
|
|
// the frame-ancestors directive.
|
|
bool mParsingFrameAncestorsDir;
|
|
|
|
policyTokens mTokens;
|
|
nsIURI* mSelfURI;
|
|
nsCSPPolicy* mPolicy;
|
|
nsCSPContext* mCSPContext; // used for console logging
|
|
bool mDeliveredViaMetaTag;
|
|
bool mSuppressLogMessages;
|
|
};
|
|
|
|
#endif /* nsCSPParser_h___ */
|