diff options
author | Matt A. Tobin <email@mattatobin.com> | 2021-11-23 03:11:24 -0500 |
---|---|---|
committer | Matt A. Tobin <email@mattatobin.com> | 2021-11-23 03:11:24 -0500 |
commit | 54276d068cdb87c3600233b66702b12be697ed68 (patch) | |
tree | 5bf85359e76eebd9a92baf7f8c703d3e93ac1f29 /dom/security/nsCSPUtils.cpp | |
parent | baad25e39733dfc3dd310198fdcced00ffbef68b (diff) | |
download | aura-central-54276d068cdb87c3600233b66702b12be697ed68.tar.gz |
Issue %3005 - Centralize the Security Features and locate them to system/security
Diffstat (limited to 'dom/security/nsCSPUtils.cpp')
-rw-r--r-- | dom/security/nsCSPUtils.cpp | 1721 |
1 files changed, 0 insertions, 1721 deletions
diff --git a/dom/security/nsCSPUtils.cpp b/dom/security/nsCSPUtils.cpp deleted file mode 100644 index 5f0bac1cd..000000000 --- a/dom/security/nsCSPUtils.cpp +++ /dev/null @@ -1,1721 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; 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 "nsAttrValue.h" -#include "nsCharSeparatedTokenizer.h" -#include "nsContentUtils.h" -#include "nsCSPUtils.h" -#include "nsDebug.h" -#include "nsIConsoleService.h" -#include "nsICryptoHash.h" -#include "nsIScriptError.h" -#include "nsIServiceManager.h" -#include "nsIStringBundle.h" -#include "nsIURL.h" -#include "nsReadableUtils.h" -#include "nsSandboxFlags.h" -#include "nsString.h" -#include "mozilla/Logging.h" - -using namespace mozilla; - -#define DEFAULT_PORT -1 - -static mozilla::LogModule* -GetCspUtilsLog() -{ - static mozilla::LazyLogModule gCspUtilsPRLog("CSPUtils"); - return gCspUtilsPRLog; -} - -#define CSPUTILSLOG(args) MOZ_LOG(GetCspUtilsLog(), mozilla::LogLevel::Debug, args) -#define CSPUTILSLOGENABLED() MOZ_LOG_TEST(GetCspUtilsLog(), mozilla::LogLevel::Debug) - -static const char16_t PERCENT_SIGN = '%'; - -static bool -isCharacterToken(char16_t aSymbol) -{ - return (aSymbol >= 'a' && aSymbol <= 'z') || - (aSymbol >= 'A' && aSymbol <= 'Z'); -} - -static bool -isNumberToken(char16_t aSymbol) -{ - return (aSymbol >= '0' && aSymbol <= '9'); -} - -static bool -isValidHexDig(char16_t aHexDig) -{ - return (isNumberToken(aHexDig) || - (aHexDig >= 'A' && aHexDig <= 'F') || - (aHexDig >= 'a' && aHexDig <= 'f')); -} - -void -CSP_PercentDecodeStr(const nsAString& aEncStr, nsAString& outDecStr) -{ - outDecStr.Truncate(); - - // helper function that should not be visible outside this methods scope - struct local { - static inline char16_t convertHexDig(char16_t aHexDig) { - if (isNumberToken(aHexDig)) { - return aHexDig - '0'; - } - if (aHexDig >= 'A' && aHexDig <= 'F') { - return aHexDig - 'A' + 10; - } - // must be a lower case character - // (aHexDig >= 'a' && aHexDig <= 'f') - return aHexDig - 'a' + 10; - } - }; - - const char16_t *cur, *end, *hexDig1, *hexDig2; - cur = aEncStr.BeginReading(); - end = aEncStr.EndReading(); - - while (cur != end) { - // if it's not a percent sign then there is - // nothing to do for that character - if (*cur != PERCENT_SIGN) { - outDecStr.Append(*cur); - cur++; - continue; - } - - // get the two hexDigs following the '%'-sign - hexDig1 = cur + 1; - hexDig2 = cur + 2; - - // if there are no hexdigs after the '%' then - // there is nothing to do for us. - if (hexDig1 == end || hexDig2 == end || - !isValidHexDig(*hexDig1) || - !isValidHexDig(*hexDig2)) { - outDecStr.Append(PERCENT_SIGN); - cur++; - continue; - } - - // decode "% hexDig1 hexDig2" into a character. - char16_t decChar = (local::convertHexDig(*hexDig1) << 4) + - local::convertHexDig(*hexDig2); - outDecStr.Append(decChar); - - // increment 'cur' to after the second hexDig - cur = ++hexDig2; - } -} - -void -CSP_GetLocalizedStr(const char16_t* aName, - const char16_t** aParams, - uint32_t aLength, - char16_t** outResult) -{ - nsCOMPtr<nsIStringBundle> keyStringBundle; - nsCOMPtr<nsIStringBundleService> stringBundleService = - mozilla::services::GetStringBundleService(); - - 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, - uint64_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); - - // Currently 'aSourceLine' is not logged to the console, because similar - // information is already included within the source link of the message. - // For inline violations however, the line and column number are 0 and - // information contained within 'aSourceLine' can be really useful for devs. - // E.g. 'aSourceLine' might be: 'onclick attribute on DIV element'. - // In such cases we append 'aSourceLine' directly to the error message. - if (!aSourceLine.IsEmpty()) { - cspMsg.Append(NS_LITERAL_STRING(" Source: ")); - cspMsg.Append(aSourceLine); - cspMsg.Append(NS_LITERAL_STRING(".")); - } - - 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); -} - -/** - * Combines CSP_LogMessage and CSP_GetLocalizedStr into one call. - */ -void -CSP_LogLocalizedStr(const char16_t* aName, - const char16_t** aParams, - uint32_t aLength, - const nsAString& aSourceName, - const nsAString& aSourceLine, - uint32_t aLineNumber, - uint32_t aColumnNumber, - uint32_t aFlags, - const char* aCategory, - uint64_t aInnerWindowID) -{ - nsXPIDLString logMsg; - CSP_GetLocalizedStr(aName, aParams, aLength, getter_Copies(logMsg)); - CSP_LogMessage(logMsg, aSourceName, aSourceLine, - aLineNumber, aColumnNumber, aFlags, - aCategory, aInnerWindowID); -} - -/* ===== Helpers ============================ */ -CSPDirective -CSP_ContentTypeToDirective(nsContentPolicyType aType) -{ - switch (aType) { - case nsIContentPolicy::TYPE_IMAGE: - case nsIContentPolicy::TYPE_IMAGESET: - return nsIContentSecurityPolicy::IMG_SRC_DIRECTIVE; - - // BLock XSLT as script, see bug 910139 - case nsIContentPolicy::TYPE_XSLT: - case nsIContentPolicy::TYPE_SCRIPT: - case nsIContentPolicy::TYPE_INTERNAL_SCRIPT: - case nsIContentPolicy::TYPE_INTERNAL_SCRIPT_PRELOAD: - return nsIContentSecurityPolicy::SCRIPT_SRC_DIRECTIVE; - - case nsIContentPolicy::TYPE_STYLESHEET: - return nsIContentSecurityPolicy::STYLE_SRC_DIRECTIVE; - - case nsIContentPolicy::TYPE_FONT: - return nsIContentSecurityPolicy::FONT_SRC_DIRECTIVE; - - case nsIContentPolicy::TYPE_MEDIA: - return nsIContentSecurityPolicy::MEDIA_SRC_DIRECTIVE; - - case nsIContentPolicy::TYPE_WEB_MANIFEST: - return nsIContentSecurityPolicy::WEB_MANIFEST_SRC_DIRECTIVE; - - case nsIContentPolicy::TYPE_INTERNAL_WORKER: - case nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER: - case nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER: - return nsIContentSecurityPolicy::WORKER_SRC_DIRECTIVE; - - case nsIContentPolicy::TYPE_SUBDOCUMENT: - return nsIContentSecurityPolicy::FRAME_SRC_DIRECTIVE; - - case nsIContentPolicy::TYPE_WEBSOCKET: - case nsIContentPolicy::TYPE_XMLHTTPREQUEST: - case nsIContentPolicy::TYPE_BEACON: - case nsIContentPolicy::TYPE_PING: - case nsIContentPolicy::TYPE_FETCH: - return nsIContentSecurityPolicy::CONNECT_SRC_DIRECTIVE; - - case nsIContentPolicy::TYPE_OBJECT: - case nsIContentPolicy::TYPE_OBJECT_SUBREQUEST: - return nsIContentSecurityPolicy::OBJECT_SRC_DIRECTIVE; - - case nsIContentPolicy::TYPE_XBL: - case nsIContentPolicy::TYPE_DTD: - case nsIContentPolicy::TYPE_OTHER: - return nsIContentSecurityPolicy::DEFAULT_SRC_DIRECTIVE; - - // csp shold not block top level loads, e.g. in case - // of a redirect. - case nsIContentPolicy::TYPE_DOCUMENT: - // CSP can not block csp reports - case nsIContentPolicy::TYPE_CSP_REPORT: - return nsIContentSecurityPolicy::NO_DIRECTIVE; - - case nsIContentPolicy::TYPE_SAVEAS_DOWNLOAD: - return nsIContentSecurityPolicy::NO_DIRECTIVE; - - // Fall through to error for all other directives - default: - MOZ_ASSERT(false, "Can not map nsContentPolicyType to CSPDirective"); - } - return nsIContentSecurityPolicy::DEFAULT_SRC_DIRECTIVE; -} - -nsCSPHostSrc* -CSP_CreateHostSrcFromSelfURI(nsIURI* aSelfURI) -{ - // Create the host first - nsCString host; - aSelfURI->GetAsciiHost(host); - nsCSPHostSrc *hostsrc = new nsCSPHostSrc(NS_ConvertUTF8toUTF16(host)); - hostsrc->setGeneratedFromSelfKeyword(); - - // Add the scheme. - nsCString scheme; - aSelfURI->GetScheme(scheme); - hostsrc->setScheme(NS_ConvertUTF8toUTF16(scheme)); - - int32_t port; - aSelfURI->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) -{ - uint32_t numDirs = (sizeof(CSPStrDirectives) / sizeof(CSPStrDirectives[0])); - - for (uint32_t i = 0; i < numDirs; i++) { - if (aDir.LowerCaseEqualsASCII(CSPStrDirectives[i])) { - return true; - } - } - return false; -} -bool -CSP_IsDirective(const nsAString& aValue, CSPDirective aDir) -{ - return aValue.LowerCaseEqualsASCII(CSP_CSPDirectiveToString(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; -} - -/* - * Checks whether the current directive permits a specific - * scheme. This function is called from nsCSPSchemeSrc() and - * also nsCSPHostSrc. - * @param aEnforcementScheme - * The scheme that this directive allows - * @param aUri - * The uri of the subresource load. - * @param aReportOnly - * Whether the enforced policy is report only or not. - * @param aUpgradeInsecure - * Whether the policy makes use of the directive - * 'upgrade-insecure-requests'. - * @param aFromSelfURI - * Whether a scheme was generated from the keyword 'self' - * which then allows schemeless sources to match ws and wss. - */ - -bool -permitsScheme(const nsAString& aEnforcementScheme, - nsIURI* aUri, - bool aReportOnly, - bool aUpgradeInsecure, - bool aFromSelfURI) -{ - nsAutoCString scheme; - nsresult rv = aUri->GetScheme(scheme); - NS_ENSURE_SUCCESS(rv, false); - - // no scheme to enforce, let's allow the load (e.g. script-src *) - if (aEnforcementScheme.IsEmpty()) { - return true; - } - - // if the scheme matches, all good - allow the load - if (aEnforcementScheme.EqualsASCII(scheme.get())) { - return true; - } - - // allow scheme-less sources where the protected resource is http - // and the load is https, see: - // http://www.w3.org/TR/CSP2/#match-source-expression - if (aEnforcementScheme.EqualsASCII("http")) { - if (scheme.EqualsASCII("https")) { - return true; - } - if ((scheme.EqualsASCII("ws") || scheme.EqualsASCII("wss")) && aFromSelfURI) { - return true; - } - } - if (aEnforcementScheme.EqualsASCII("https")) { - if (scheme.EqualsLiteral("wss") && aFromSelfURI) { - return true; - } - } - if (aEnforcementScheme.EqualsASCII("ws") && scheme.EqualsASCII("wss")) { - return true; - } - - // Allow the load when enforcing upgrade-insecure-requests with the - // promise the request gets upgraded from http to https and ws to wss. - // See nsHttpChannel::Connect() and also WebSocket.cpp. Please note, - // the report only policies should not allow the load and report - // the error back to the page. - return ((aUpgradeInsecure && !aReportOnly) && - ((scheme.EqualsASCII("http") && aEnforcementScheme.EqualsASCII("https")) || - (scheme.EqualsASCII("ws") && aEnforcementScheme.EqualsASCII("wss")))); -} - -/* - * A helper function for appending a CSP header to an existing CSP - * policy. - * - * @param aCsp the CSP policy - * @param aHeaderValue the header - * @param aReportOnly is this a report-only header? - */ - -nsresult -CSP_AppendCSPFromHeader(nsIContentSecurityPolicy* aCsp, - const nsAString& aHeaderValue, - bool aReportOnly) -{ - NS_ENSURE_ARG(aCsp); - - // Need to tokenize the header value since multiple headers could be - // concatenated into one comma-separated list of policies. - // See RFC2616 section 4.2 (last paragraph) - nsresult rv = NS_OK; - nsCharSeparatedTokenizer tokenizer(aHeaderValue, ','); - while (tokenizer.hasMoreTokens()) { - const nsSubstring& policy = tokenizer.nextToken(); - rv = aCsp->AppendPolicy(policy, aReportOnly, false); - NS_ENSURE_SUCCESS(rv, rv); - { - CSPUTILSLOG(("CSP refined with policy: \"%s\"", - NS_ConvertUTF16toUTF8(policy).get())); - } - } - return NS_OK; -} - -/* ===== nsCSPSrc ============================ */ - -nsCSPBaseSrc::nsCSPBaseSrc() - : mInvalidated(false) -{ -} - -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, bool aWasRedirected, - bool aReportOnly, bool aUpgradeInsecure, bool aParserCreated) const -{ - if (CSPUTILSLOGENABLED()) { - CSPUTILSLOG(("nsCSPBaseSrc::permits, aUri: %s", - aUri->GetSpecOrDefault().get())); - } - 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, - bool aParserCreated) 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, bool aWasRedirected, - bool aReportOnly, bool aUpgradeInsecure, bool aParserCreated) const -{ - if (CSPUTILSLOGENABLED()) { - CSPUTILSLOG(("nsCSPSchemeSrc::permits, aUri: %s", - aUri->GetSpecOrDefault().get())); - } - MOZ_ASSERT((!mScheme.EqualsASCII("")), "scheme can not be the empty string"); - if (mInvalidated) { - return false; - } - return permitsScheme(mScheme, aUri, aReportOnly, aUpgradeInsecure, false); -} - -bool -nsCSPSchemeSrc::visit(nsCSPSrcVisitor* aVisitor) const -{ - return aVisitor->visitSchemeSrc(*this); -} - -void -nsCSPSchemeSrc::toString(nsAString& outStr) const -{ - outStr.Append(mScheme); - outStr.AppendASCII(":"); -} - -/* ===== nsCSPHostSrc ======================== */ - -nsCSPHostSrc::nsCSPHostSrc(const nsAString& aHost) - : mHost(aHost) - , mGeneratedFromSelfKeyword(false) - , mWithinFrameAncstorsDir(false) -{ - ToLowerCase(mHost); -} - -nsCSPHostSrc::~nsCSPHostSrc() -{ -} - -/* - * Checks whether the current directive permits a specific port. - * @param aEnforcementScheme - * The scheme that this directive allows - * (used to query the default port for that scheme) - * @param aEnforcementPort - * The port that this directive allows - * @param aResourceURI - * The uri of the subresource load - */ -bool -permitsPort(const nsAString& aEnforcementScheme, - const nsAString& aEnforcementPort, - nsIURI* aResourceURI) -{ - // If enforcement port is the wildcard, don't block the load. - if (aEnforcementPort.EqualsASCII("*")) { - return true; - } - - int32_t resourcePort; - nsresult rv = aResourceURI->GetPort(&resourcePort); - if (NS_FAILED(rv) && aEnforcementPort.IsEmpty()) { - // If we cannot get a Port (e.g. because of an Custom Protocol handler) - // we need to check if a default port is associated with the Scheme - if (aEnforcementScheme.IsEmpty()) { - return false; - } - int defaultPortforScheme = - NS_GetDefaultPort(NS_ConvertUTF16toUTF8(aEnforcementScheme).get()); - - // If there is no default port associated with the Scheme ( - // defaultPortforScheme == -1) or it is an externally handled protocol ( - // defaultPortforScheme == 0 ) and the csp does not enforce a port - we can - // allow not having a port - return (defaultPortforScheme == -1 || defaultPortforScheme == 0); - } - - // Avoid unnecessary string creation/manipulation and don't block the - // load if the resource to be loaded uses the default port for that - // scheme and there is no port to be enforced. - // Note, this optimization relies on scheme checks within permitsScheme(). - if (resourcePort == DEFAULT_PORT && aEnforcementPort.IsEmpty()) { - return true; - } - - // By now we know at that either the resourcePort does not use the default - // port or there is a port restriction to be enforced. A port value of -1 - // corresponds to the protocol's default port (eg. -1 implies port 80 for - // http URIs), in such a case we have to query the default port of the - // resource to be loaded. - if (resourcePort == DEFAULT_PORT) { - nsAutoCString resourceScheme; - rv = aResourceURI->GetScheme(resourceScheme); - NS_ENSURE_SUCCESS(rv, false); - resourcePort = NS_GetDefaultPort(resourceScheme.get()); - } - - // If there is a port to be enforced and the ports match, then - // don't block the load. - nsString resourcePortStr; - resourcePortStr.AppendInt(resourcePort); - if (aEnforcementPort.Equals(resourcePortStr)) { - return true; - } - - // If there is no port to be enforced, query the default port for the load. - nsString enforcementPort(aEnforcementPort); - if (enforcementPort.IsEmpty()) { - // For scheme less sources, our parser always generates a scheme - // which is the scheme of the protected resource. - MOZ_ASSERT(!aEnforcementScheme.IsEmpty(), - "need a scheme to query default port"); - int32_t defaultEnforcementPort = - NS_GetDefaultPort(NS_ConvertUTF16toUTF8(aEnforcementScheme).get()); - enforcementPort.Truncate(); - enforcementPort.AppendInt(defaultEnforcementPort); - } - - // If default ports match, don't block the load - if (enforcementPort.Equals(resourcePortStr)) { - return true; - } - - // Additional port matching where the regular URL matching algorithm - // treats insecure ports as matching their secure variants. - // default port for http is :80 - // default port for https is :443 - if (enforcementPort.EqualsLiteral("80") && - resourcePortStr.EqualsLiteral("443")) { - return true; - } - - // ports do not match, block the load. - return false; -} - -bool -nsCSPHostSrc::permits(nsIURI* aUri, const nsAString& aNonce, bool aWasRedirected, - bool aReportOnly, bool aUpgradeInsecure, bool aParserCreated) const -{ - if (CSPUTILSLOGENABLED()) { - CSPUTILSLOG(("nsCSPHostSrc::permits, aUri: %s", - aUri->GetSpecOrDefault().get())); - } - - if (mInvalidated) { - return false; - } - - // we are following the enforcement rules from the spec, see: - // http://www.w3.org/TR/CSP11/#match-source-expression - - // 4.3) scheme matching: Check if the scheme matches. - if (!permitsScheme(mScheme, aUri, aReportOnly, aUpgradeInsecure, mGeneratedFromSelfKeyword)) { - 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"); - - // Before we can check if the host matches, we have to - // extract the host part from aUri. - nsAutoCString uriHost; - nsresult rv = aUri->GetAsciiHost(uriHost); - NS_ENSURE_SUCCESS(rv, false); - - nsString decodedUriHost; - CSP_PercentDecodeStr(NS_ConvertUTF8toUTF16(uriHost), decodedUriHost); - - // 2) host matching: Enforce a single * - if (mHost.EqualsASCII("*")) { - // The single ASTERISK character (*) does not match a URI's scheme of a type - // designating a globally unique identifier (such as blob:, data:, or filesystem:) - // At the moment UXP does not support "filesystem:" but for future compatibility - // we support it in CSP according to the spec, see: 4.2.2 Matching Source Expressions - // Note: whitelisting any of these schemes would call nsCSPSchemeSrc::permits(). - bool isBlobScheme = - (NS_SUCCEEDED(aUri->SchemeIs("blob", &isBlobScheme)) && isBlobScheme); - bool isDataScheme = - (NS_SUCCEEDED(aUri->SchemeIs("data", &isDataScheme)) && isDataScheme); - bool isFileScheme = - (NS_SUCCEEDED(aUri->SchemeIs("filesystem", &isFileScheme)) && isFileScheme); - - if (isBlobScheme || isDataScheme || isFileScheme) { - return false; - } - - // If no scheme is present there also won't be a port and folder to check - // which means we can return early. - if (mScheme.IsEmpty()) { - return true; - } - } - // 4.5) host matching: Check if the allowed host starts with a wilcard. - else if (mHost.First() == '*') { - NS_ASSERTION(mHost[1] == '.', "Second character needs to be '.' whenever host starts with '*'"); - - // Eliminate leading "*", but keeping the FULL STOP (.) thereafter before checking - // if the remaining characters match - nsString wildCardHost = mHost; - wildCardHost = Substring(wildCardHost, 1, wildCardHost.Length() - 1); - if (!StringEndsWith(decodedUriHost, wildCardHost)) { - return false; - } - } - // 4.6) host matching: Check if hosts match. - else if (!mHost.Equals(decodedUriHost)) { - return false; - } - - // Port matching: Check if the ports match. - if (!permitsPort(mScheme, mPort, aUri)) { - return false; - } - - // 4.9) Path matching: If there is a path, we have to enforce - // path-level matching, unless the channel got redirected, see: - // http://www.w3.org/TR/CSP11/#source-list-paths-and-redirects - if (!aWasRedirected && !mPath.IsEmpty()) { - // converting aUri into nsIURL so we can strip query and ref - // example.com/test#foo -> example.com/test - // example.com/test?val=foo -> example.com/test - nsCOMPtr<nsIURL> url = do_QueryInterface(aUri); - if (!url) { - NS_ASSERTION(false, "can't QI into nsIURI"); - return false; - } - nsAutoCString uriPath; - rv = url->GetFilePath(uriPath); - NS_ENSURE_SUCCESS(rv, false); - - if (mWithinFrameAncstorsDir) { - // no path matching for frame-ancestors to not leak any path information. - return true; - } - - nsString decodedUriPath; - CSP_PercentDecodeStr(NS_ConvertUTF8toUTF16(uriPath), decodedUriPath); - - // check if the last character of mPath is '/'; if so - // we just have to check loading resource is within - // the allowed path. - if (mPath.Last() == '/') { - if (!StringBeginsWith(decodedUriPath, mPath)) { - return false; - } - } - // otherwise mPath whitelists a specific file, and we have to - // check if the loading resource matches that whitelisted file. - else { - if (!mPath.Equals(decodedUriPath)) { - return false; - } - } - } - - // At the end: scheme, host, port and path match -> allow the load. - return true; -} - -bool -nsCSPHostSrc::visit(nsCSPSrcVisitor* aVisitor) const -{ - return aVisitor->visitHostSrc(*this); -} - -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); - } - - // append path - outStr.Append(mPath); -} - -void -nsCSPHostSrc::setScheme(const nsAString& aScheme) -{ - mScheme = aScheme; - ToLowerCase(mScheme); -} - -void -nsCSPHostSrc::setPort(const nsAString& aPort) -{ - mPort = aPort; -} - -void -nsCSPHostSrc::appendPath(const nsAString& aPath) -{ - mPath.Append(aPath); -} - -/* ===== nsCSPKeywordSrc ===================== */ - -nsCSPKeywordSrc::nsCSPKeywordSrc(enum CSPKeyword aKeyword) - : mKeyword(aKeyword) -{ - NS_ASSERTION((aKeyword != CSP_SELF), - "'self' should have been replaced in the parser"); -} - -nsCSPKeywordSrc::~nsCSPKeywordSrc() -{ -} - -bool -nsCSPKeywordSrc::permits(nsIURI* aUri, const nsAString& aNonce, bool aWasRedirected, - bool aReportOnly, bool aUpgradeInsecure, bool aParserCreated) const -{ - // no need to check for invalidated, this will always return false unless - // it is an nsCSPKeywordSrc for 'strict-dynamic', which should allow non - // parser created scripts. - return ((mKeyword == CSP_STRICT_DYNAMIC) && !aParserCreated); -} - -bool -nsCSPKeywordSrc::allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce, - bool aParserCreated) const -{ - CSPUTILSLOG(("nsCSPKeywordSrc::allows, aKeyWord: %s, aHashOrNonce: %s, mInvalidated: %s", - CSP_EnumToKeyword(aKeyword), - CSP_EnumToKeyword(mKeyword), - NS_ConvertUTF16toUTF8(aHashOrNonce).get(), - mInvalidated ? "yes" : "false")); - - if (mInvalidated) { - // only 'self' and 'unsafe-inline' are keywords that can be ignored. Please note that - // the parser already translates 'self' into a uri (see assertion in constructor). - MOZ_ASSERT(mKeyword == CSP_UNSAFE_INLINE, - "should only invalidate unsafe-inline"); - return false; - } - // either the keyword allows the load or the policy contains 'strict-dynamic', in which - // case we have to make sure the script is not parser created before allowing the load. - return ((mKeyword == aKeyword) || - ((mKeyword == CSP_STRICT_DYNAMIC) && !aParserCreated)); -} - -bool -nsCSPKeywordSrc::visit(nsCSPSrcVisitor* aVisitor) const -{ - return aVisitor->visitKeywordSrc(*this); -} - -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, bool aWasRedirected, - bool aReportOnly, bool aUpgradeInsecure, bool aParserCreated) const -{ - if (CSPUTILSLOGENABLED()) { - CSPUTILSLOG(("nsCSPNonceSrc::permits, aUri: %s, aNonce: %s", - aUri->GetSpecOrDefault().get(), - NS_ConvertUTF16toUTF8(aNonce).get())); - } - - // nonces can not be invalidated by strict-dynamic - return mNonce.Equals(aNonce); -} - -bool -nsCSPNonceSrc::allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce, - bool aParserCreated) const -{ - CSPUTILSLOG(("nsCSPNonceSrc::allows, aKeyWord: %s, a HashOrNonce: %s", - CSP_EnumToKeyword(aKeyword), NS_ConvertUTF16toUTF8(aHashOrNonce).get())); - - if (aKeyword != CSP_NONCE) { - return false; - } - // nonces can not be invalidated by strict-dynamic - return mNonce.Equals(aHashOrNonce); -} - -bool -nsCSPNonceSrc::visit(nsCSPSrcVisitor* aVisitor) const -{ - return aVisitor->visitNonceSrc(*this); -} - -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, - bool aParserCreated) const -{ - CSPUTILSLOG(("nsCSPHashSrc::allows, aKeyWord: %s, a HashOrNonce: %s", - CSP_EnumToKeyword(aKeyword), NS_ConvertUTF16toUTF8(aHashOrNonce).get())); - - if (aKeyword != CSP_HASH) { - return false; - } - - // hashes can not be invalidated by strict-dynamic - - // 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); -} - -bool -nsCSPHashSrc::visit(nsCSPSrcVisitor* aVisitor) const -{ - return aVisitor->visitHashSrc(*this); -} - -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() -{ -} - -bool -nsCSPReportURI::visit(nsCSPSrcVisitor* aVisitor) const -{ - return false; -} - -void -nsCSPReportURI::toString(nsAString& outStr) const -{ - nsAutoCString spec; - nsresult rv = mReportURI->GetSpec(spec); - if (NS_FAILED(rv)) { - return; - } - outStr.AppendASCII(spec.get()); -} - -/* ===== nsCSPSandboxFlags ===================== */ - -nsCSPSandboxFlags::nsCSPSandboxFlags(const nsAString& aFlags) - : mFlags(aFlags) -{ - ToLowerCase(mFlags); -} - -nsCSPSandboxFlags::~nsCSPSandboxFlags() -{ -} - -bool -nsCSPSandboxFlags::visit(nsCSPSrcVisitor* aVisitor) const -{ - return false; -} - -void -nsCSPSandboxFlags::toString(nsAString& outStr) const -{ - outStr.Append(mFlags); -} - -/* ===== nsCSPDirective ====================== */ - -nsCSPDirective::nsCSPDirective(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, bool aWasRedirected, - bool aReportOnly, bool aUpgradeInsecure, bool aParserCreated) const -{ - if (CSPUTILSLOGENABLED()) { - CSPUTILSLOG(("nsCSPDirective::permits, aUri: %s", - aUri->GetSpecOrDefault().get())); - } - - for (uint32_t i = 0; i < mSrcs.Length(); i++) { - if (mSrcs[i]->permits(aUri, aNonce, aWasRedirected, aReportOnly, aUpgradeInsecure, aParserCreated)) { - return true; - } - } - return false; -} - -bool -nsCSPDirective::allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce, - bool aParserCreated) 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, aParserCreated)) { - return true; - } - } - return false; -} - -void -nsCSPDirective::toString(nsAString& outStr) const -{ - // Append directive name - outStr.AppendASCII(CSP_CSPDirectiveToString(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(" "); - } - } -} - -void -nsCSPDirective::toDomCSPStruct(mozilla::dom::CSP& outCSP) const -{ - mozilla::dom::Sequence<nsString> srcs; - nsString src; - for (uint32_t i = 0; i < mSrcs.Length(); i++) { - src.Truncate(); - mSrcs[i]->toString(src); - srcs.AppendElement(src, mozilla::fallible); - } - - switch(mDirective) { - case nsIContentSecurityPolicy::DEFAULT_SRC_DIRECTIVE: - outCSP.mDefault_src.Construct(); - outCSP.mDefault_src.Value() = mozilla::Move(srcs); - return; - - case nsIContentSecurityPolicy::SCRIPT_SRC_DIRECTIVE: - outCSP.mScript_src.Construct(); - outCSP.mScript_src.Value() = mozilla::Move(srcs); - return; - - case nsIContentSecurityPolicy::OBJECT_SRC_DIRECTIVE: - outCSP.mObject_src.Construct(); - outCSP.mObject_src.Value() = mozilla::Move(srcs); - return; - - case nsIContentSecurityPolicy::STYLE_SRC_DIRECTIVE: - outCSP.mStyle_src.Construct(); - outCSP.mStyle_src.Value() = mozilla::Move(srcs); - return; - - case nsIContentSecurityPolicy::IMG_SRC_DIRECTIVE: - outCSP.mImg_src.Construct(); - outCSP.mImg_src.Value() = mozilla::Move(srcs); - return; - - case nsIContentSecurityPolicy::MEDIA_SRC_DIRECTIVE: - outCSP.mMedia_src.Construct(); - outCSP.mMedia_src.Value() = mozilla::Move(srcs); - return; - - case nsIContentSecurityPolicy::FRAME_SRC_DIRECTIVE: - outCSP.mFrame_src.Construct(); - outCSP.mFrame_src.Value() = mozilla::Move(srcs); - return; - - case nsIContentSecurityPolicy::FONT_SRC_DIRECTIVE: - outCSP.mFont_src.Construct(); - outCSP.mFont_src.Value() = mozilla::Move(srcs); - return; - - case nsIContentSecurityPolicy::CONNECT_SRC_DIRECTIVE: - outCSP.mConnect_src.Construct(); - outCSP.mConnect_src.Value() = mozilla::Move(srcs); - return; - - case nsIContentSecurityPolicy::REPORT_URI_DIRECTIVE: - outCSP.mReport_uri.Construct(); - outCSP.mReport_uri.Value() = mozilla::Move(srcs); - return; - - case nsIContentSecurityPolicy::FRAME_ANCESTORS_DIRECTIVE: - outCSP.mFrame_ancestors.Construct(); - outCSP.mFrame_ancestors.Value() = mozilla::Move(srcs); - return; - - case nsIContentSecurityPolicy::WEB_MANIFEST_SRC_DIRECTIVE: - outCSP.mManifest_src.Construct(); - outCSP.mManifest_src.Value() = mozilla::Move(srcs); - return; - // not supporting REFLECTED_XSS_DIRECTIVE - - case nsIContentSecurityPolicy::BASE_URI_DIRECTIVE: - outCSP.mBase_uri.Construct(); - outCSP.mBase_uri.Value() = mozilla::Move(srcs); - return; - - case nsIContentSecurityPolicy::FORM_ACTION_DIRECTIVE: - outCSP.mForm_action.Construct(); - outCSP.mForm_action.Value() = mozilla::Move(srcs); - return; - - case nsIContentSecurityPolicy::BLOCK_ALL_MIXED_CONTENT: - outCSP.mBlock_all_mixed_content.Construct(); - // does not have any srcs - return; - - case nsIContentSecurityPolicy::UPGRADE_IF_INSECURE_DIRECTIVE: - outCSP.mUpgrade_insecure_requests.Construct(); - // does not have any srcs - return; - - case nsIContentSecurityPolicy::CHILD_SRC_DIRECTIVE: - outCSP.mChild_src.Construct(); - outCSP.mChild_src.Value() = mozilla::Move(srcs); - return; - - case nsIContentSecurityPolicy::SANDBOX_DIRECTIVE: - outCSP.mSandbox.Construct(); - outCSP.mSandbox.Value() = mozilla::Move(srcs); - return; - - case nsIContentSecurityPolicy::WORKER_SRC_DIRECTIVE: - outCSP.mWorker_src.Construct(); - outCSP.mWorker_src.Value() = mozilla::Move(srcs); - return; - - // REFERRER_DIRECTIVE and REQUIRE_SRI_FOR are handled in nsCSPPolicy::toDomCSPStruct() - - default: - NS_ASSERTION(false, "cannot find directive to convert CSP to JSON"); - } -} - - -bool -nsCSPDirective::restrictsContentType(nsContentPolicyType aContentType) const -{ - // make sure we do not check for the default src before any other sources - if (isDefaultDirective()) { - return false; - } - return mDirective == CSP_ContentTypeToDirective(aContentType); -} - -void -nsCSPDirective::getReportURIs(nsTArray<nsString> &outReportURIs) const -{ - NS_ASSERTION((mDirective == nsIContentSecurityPolicy::REPORT_URI_DIRECTIVE), "not a report-uri directive"); - - // append uris - nsString tmpReportURI; - for (uint32_t i = 0; i < mSrcs.Length(); i++) { - tmpReportURI.Truncate(); - mSrcs[i]->toString(tmpReportURI); - outReportURIs.AppendElement(tmpReportURI); - } -} - -bool -nsCSPDirective::visitSrcs(nsCSPSrcVisitor* aVisitor) const -{ - for (uint32_t i = 0; i < mSrcs.Length(); i++) { - if (!mSrcs[i]->visit(aVisitor)) { - return false; - } - } - return true; -} - -bool nsCSPDirective::equals(CSPDirective aDirective) const -{ - return (mDirective == aDirective); -} - -/* =============== nsCSPChildSrcDirective ============= */ - -nsCSPChildSrcDirective::nsCSPChildSrcDirective(CSPDirective aDirective) - : nsCSPDirective(aDirective) - , mRestrictFrames(false) - , mRestrictWorkers(false) -{ -} - -nsCSPChildSrcDirective::~nsCSPChildSrcDirective() -{ -} - -bool nsCSPChildSrcDirective::restrictsContentType(nsContentPolicyType aContentType) const -{ - if (aContentType == nsIContentPolicy::TYPE_SUBDOCUMENT) { - return mRestrictFrames; - } - if (aContentType == nsIContentPolicy::TYPE_INTERNAL_WORKER || - aContentType == nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER || - aContentType == nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER) { - return mRestrictWorkers; - } - return false; -} - -bool nsCSPChildSrcDirective::equals(CSPDirective aDirective) const -{ - if (aDirective == nsIContentSecurityPolicy::FRAME_SRC_DIRECTIVE) { - return mRestrictFrames; - } - if (aDirective == nsIContentSecurityPolicy::WORKER_SRC_DIRECTIVE) { - return mRestrictWorkers; - } - return (mDirective == aDirective); -} - -/* =============== nsCSPScriptSrcDirective ============= */ - -nsCSPScriptSrcDirective::nsCSPScriptSrcDirective(CSPDirective aDirective) - : nsCSPDirective(aDirective) - , mRestrictWorkers(false) -{ -} - -nsCSPScriptSrcDirective::~nsCSPScriptSrcDirective() -{ -} - -bool nsCSPScriptSrcDirective::restrictsContentType(nsContentPolicyType aContentType) const -{ - if (aContentType == nsIContentPolicy::TYPE_INTERNAL_WORKER || - aContentType == nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER || - aContentType == nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER) { - return mRestrictWorkers; - } - return mDirective == CSP_ContentTypeToDirective(aContentType); -} - -bool nsCSPScriptSrcDirective::equals(CSPDirective aDirective) const -{ - if (aDirective == nsIContentSecurityPolicy::WORKER_SRC_DIRECTIVE) { - return mRestrictWorkers; - } - return (mDirective == aDirective); -} - -/* =============== nsBlockAllMixedContentDirective ============= */ - -nsBlockAllMixedContentDirective::nsBlockAllMixedContentDirective(CSPDirective aDirective) -: nsCSPDirective(aDirective) -{ -} - -nsBlockAllMixedContentDirective::~nsBlockAllMixedContentDirective() -{ -} - -void -nsBlockAllMixedContentDirective::toString(nsAString& outStr) const -{ - outStr.AppendASCII(CSP_CSPDirectiveToString( - nsIContentSecurityPolicy::BLOCK_ALL_MIXED_CONTENT)); -} - -/* =============== nsUpgradeInsecureDirective ============= */ - -nsUpgradeInsecureDirective::nsUpgradeInsecureDirective(CSPDirective aDirective) -: nsCSPDirective(aDirective) -{ -} - -nsUpgradeInsecureDirective::~nsUpgradeInsecureDirective() -{ -} - -void -nsUpgradeInsecureDirective::toString(nsAString& outStr) const -{ - outStr.AppendASCII(CSP_CSPDirectiveToString( - nsIContentSecurityPolicy::UPGRADE_IF_INSECURE_DIRECTIVE)); -} - -/* ===== nsRequireSRIForDirective ========================= */ - -nsRequireSRIForDirective::nsRequireSRIForDirective(CSPDirective aDirective) -: nsCSPDirective(aDirective) -{ -} - -nsRequireSRIForDirective::~nsRequireSRIForDirective() -{ -} - -void -nsRequireSRIForDirective::toString(nsAString &outStr) const -{ - outStr.AppendASCII(CSP_CSPDirectiveToString( - nsIContentSecurityPolicy::REQUIRE_SRI_FOR)); - for (uint32_t i = 0; i < mTypes.Length(); i++) { - if (mTypes[i] == nsIContentPolicy::TYPE_SCRIPT) { - outStr.AppendASCII(" script"); - } - else if (mTypes[i] == nsIContentPolicy::TYPE_STYLESHEET) { - outStr.AppendASCII(" style"); - } - } -} - -bool -nsRequireSRIForDirective::hasType(nsContentPolicyType aType) const -{ - for (uint32_t i = 0; i < mTypes.Length(); i++) { - if (mTypes[i] == aType) { - return true; - } - } - return false; -} - -bool -nsRequireSRIForDirective::restrictsContentType(const nsContentPolicyType aType) const -{ - return this->hasType(aType); -} - -bool -nsRequireSRIForDirective::allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce, - bool aParserCreated) const -{ - // can only disallow CSP_REQUIRE_SRI_FOR. - return (aKeyword != CSP_REQUIRE_SRI_FOR); -} - -/* ===== nsCSPPolicy ========================= */ - -nsCSPPolicy::nsCSPPolicy() - : mUpgradeInsecDir(nullptr) - , 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(CSPDirective aDir, - nsIURI* aUri, - bool aSpecific) const -{ - nsString outp; - return this->permits(aDir, aUri, EmptyString(), false, aSpecific, false, outp); -} - -bool -nsCSPPolicy::permits(CSPDirective aDir, - nsIURI* aUri, - const nsAString& aNonce, - bool aWasRedirected, - bool aSpecific, - bool aParserCreated, - nsAString& outViolatedDirective) const -{ - if (CSPUTILSLOGENABLED()) { - CSPUTILSLOG(("nsCSPPolicy::permits, aUri: %s, aDir: %d, aSpecific: %s", - aUri->GetSpecOrDefault().get(), aDir, - aSpecific ? "true" : "false")); - } - - NS_ASSERTION(aUri, "permits needs an uri to perform the check!"); - outViolatedDirective.Truncate(); - - nsCSPDirective* defaultDir = nullptr; - - // Try to find a relevant directive - // These directive arrays are short (1-5 elements), not worth using a hashtable. - for (uint32_t i = 0; i < mDirectives.Length(); i++) { - if (mDirectives[i]->equals(aDir)) { - if (!mDirectives[i]->permits(aUri, aNonce, aWasRedirected, mReportOnly, - mUpgradeInsecDir, aParserCreated)) { - 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 (!aSpecific && defaultDir) { - if (!defaultDir->permits(aUri, aNonce, aWasRedirected, mReportOnly, - mUpgradeInsecDir, aParserCreated)) { - defaultDir->toString(outViolatedDirective); - return false; - } - return true; - } - - // Nothing restricts this, so we're allowing the load - // See bug 764937 - return true; -} - -bool -nsCSPPolicy::allows(nsContentPolicyType aContentType, - enum CSPKeyword aKeyword, - const nsAString& aHashOrNonce, - bool aParserCreated) 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]->restrictsContentType(aContentType)) { - if (mDirectives[i]->allows(aKeyword, aHashOrNonce, aParserCreated)) { - return true; - } - return false; - } - if (mDirectives[i]->isDefaultDirective()) { - defaultDir = mDirectives[i]; - } - } - - // {nonce,hash}-source should not consult default-src: - // * return false if default-src is specified - // * but allow the load if default-src is *not* specified (Bug 1198422) - if (aKeyword == CSP_NONCE || aKeyword == CSP_HASH) { - if (!defaultDir) { - return true; - } - 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, aParserCreated); - } - - // 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(""), false); -} - -void -nsCSPPolicy::toString(nsAString& outStr) const -{ - uint32_t length = mDirectives.Length(); - for (uint32_t i = 0; i < length; ++i) { - - if (mDirectives[i]->equals(nsIContentSecurityPolicy::REFERRER_DIRECTIVE)) { - outStr.AppendASCII(CSP_CSPDirectiveToString(nsIContentSecurityPolicy::REFERRER_DIRECTIVE)); - outStr.AppendASCII(" "); - outStr.Append(mReferrerPolicy); - } else { - mDirectives[i]->toString(outStr); - } - if (i != (length - 1)) { - outStr.AppendASCII("; "); - } - } -} - -void -nsCSPPolicy::toDomCSPStruct(mozilla::dom::CSP& outCSP) const -{ - outCSP.mReport_only = mReportOnly; - - for (uint32_t i = 0; i < mDirectives.Length(); ++i) { - if (mDirectives[i]->equals(nsIContentSecurityPolicy::REFERRER_DIRECTIVE)) { - mozilla::dom::Sequence<nsString> srcs; - srcs.AppendElement(mReferrerPolicy, mozilla::fallible); - outCSP.mReferrer.Construct(); - outCSP.mReferrer.Value() = srcs; - } else { - mDirectives[i]->toDomCSPStruct(outCSP); - } - } -} - -bool -nsCSPPolicy::hasDirective(CSPDirective aDir) const -{ - for (uint32_t i = 0; i < mDirectives.Length(); i++) { - if (mDirectives[i]->equals(aDir)) { - return true; - } - } - return false; -} - -/* - * Use this function only after ::allows() returned 'false'. Most and - * foremost it's used to get the violated directive before sending reports. - * The parameter outDirective is the equivalent of 'outViolatedDirective' - * for the ::permits() function family. - */ -void -nsCSPPolicy::getDirectiveStringForContentType(nsContentPolicyType aContentType, - nsAString& outDirective) const -{ - nsCSPDirective* defaultDir = nullptr; - for (uint32_t i = 0; i < mDirectives.Length(); i++) { - if (mDirectives[i]->restrictsContentType(aContentType)) { - mDirectives[i]->toString(outDirective); - return; - } - if (mDirectives[i]->isDefaultDirective()) { - defaultDir = mDirectives[i]; - } - } - // if we haven't found a matching directive yet, - // the contentType must be restricted by the default directive - if (defaultDir) { - defaultDir->toString(outDirective); - return; - } - NS_ASSERTION(false, "Can not query directive string for contentType!"); - outDirective.AppendASCII("couldNotQueryViolatedDirective"); -} - -void -nsCSPPolicy::getDirectiveAsString(CSPDirective aDir, nsAString& outDirective) const -{ - for (uint32_t i = 0; i < mDirectives.Length(); i++) { - if (mDirectives[i]->equals(aDir)) { - mDirectives[i]->toString(outDirective); - return; - } - } -} - -/* - * Helper function that returns the underlying bit representation of sandbox - * flags. The function returns SANDBOXED_NONE if there are no sandbox - * directives. - */ -uint32_t -nsCSPPolicy::getSandboxFlags() const -{ - for (uint32_t i = 0; i < mDirectives.Length(); i++) { - if (mDirectives[i]->equals(nsIContentSecurityPolicy::SANDBOX_DIRECTIVE)) { - nsAutoString flags; - mDirectives[i]->toString(flags); - - if (flags.IsEmpty()) { - return SANDBOX_ALL_FLAGS; - } - - nsAttrValue attr; - attr.ParseAtomArray(flags); - - return nsContentUtils::ParseSandboxAttributeToFlags(&attr); - } - } - - return SANDBOXED_NONE; -} - -void -nsCSPPolicy::getReportURIs(nsTArray<nsString>& outReportURIs) const -{ - for (uint32_t i = 0; i < mDirectives.Length(); i++) { - if (mDirectives[i]->equals(nsIContentSecurityPolicy::REPORT_URI_DIRECTIVE)) { - mDirectives[i]->getReportURIs(outReportURIs); - return; - } - } -} - -bool -nsCSPPolicy::visitDirectiveSrcs(CSPDirective aDir, nsCSPSrcVisitor* aVisitor) const -{ - for (uint32_t i = 0; i < mDirectives.Length(); i++) { - if (mDirectives[i]->equals(aDir)) { - return mDirectives[i]->visitSrcs(aVisitor); - } - } - return false; -} - -bool -nsCSPPolicy::requireSRIForType(nsContentPolicyType aContentType) -{ - for (uint32_t i = 0; i < mDirectives.Length(); i++) { - if (mDirectives[i]->equals(nsIContentSecurityPolicy::REQUIRE_SRI_FOR)) { - return static_cast<nsRequireSRIForDirective*>(mDirectives[i])->hasType(aContentType); - } - } - return false; -} |