summaryrefslogtreecommitdiff
path: root/dom/security/nsCSPUtils.cpp
diff options
context:
space:
mode:
authorMatt A. Tobin <email@mattatobin.com>2021-11-23 03:11:24 -0500
committerMatt A. Tobin <email@mattatobin.com>2021-11-23 03:11:24 -0500
commit54276d068cdb87c3600233b66702b12be697ed68 (patch)
tree5bf85359e76eebd9a92baf7f8c703d3e93ac1f29 /dom/security/nsCSPUtils.cpp
parentbaad25e39733dfc3dd310198fdcced00ffbef68b (diff)
downloadaura-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.cpp1721
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;
-}