Issue #2402 - Fix SecurityPolicyViolationEvent.violatedDirective. https://bugzilla.mozilla.org/show_bug.cgi?id=1418243

This commit is contained in:
Brian Smith 2024-01-04 05:04:21 -06:00 committed by roytam1
commit 1e0851158e
3 changed files with 80 additions and 12 deletions

View file

@ -3,6 +3,9 @@
* 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 <string>
#include <unordered_set>
#include "nsCOMPtr.h"
#include "nsContentPolicyUtils.h"
#include "nsContentUtils.h"
@ -58,6 +61,29 @@ GetCspContextLog()
static const uint32_t CSP_CACHE_URI_CUTOFF_SIZE = 512;
#ifdef DEBUG
/**
* This function is only used for verification purposes within
* GatherSecurityPolicyViolationEventData.
*/
static bool
ValidateDirectiveName(const nsAString& aDirective)
{
static const auto directives = [] () {
std::unordered_set<std::string> directives;
constexpr size_t dirLen = sizeof(CSPStrDirectives) / sizeof(CSPStrDirectives[0]);
for (size_t i = 0; i < dirLen; ++i) {
directives.insert(CSPStrDirectives[i]);
}
return directives;
} ();
nsAutoString directive(aDirective);
auto itr = directives.find(NS_ConvertUTF16toUTF8(directive).get());
return itr != directives.end();
}
#endif // DEBUG
/**
* Creates a key for use in the ShouldLoad cache.
* Looks like: <uri>!<nsIContentPolicy::LOAD_TYPE>
@ -812,6 +838,8 @@ nsCSPContext::GatherSecurityPolicyViolationEventData(
{
NS_ENSURE_ARG_MAX(aViolatedPolicyIndex, mPolicies.Length() - 1);
MOZ_ASSERT(ValidateDirectiveName(aViolatedDirective), "Invalid directive name");
if (!CSPService::sCSPReportingEnabled) {
// Reporting is pref-disabled. Don't do any actual work and return success.
nsContentUtils::ReportToConsoleNonLocalized(
@ -853,12 +881,15 @@ nsCSPContext::GatherSecurityPolicyViolationEventData(
aViolationEventInit.mBlockedURI = NS_ConvertUTF8toUTF16(reportBlockedURI);
}
// violated-directive
aViolationEventInit.mViolatedDirective = aViolatedDirective;
// effective-directive
// The name of the policy directive that was violated.
aViolationEventInit.mEffectiveDirective = aViolatedDirective;
// violated-directive
// In CSP2, the policy directive that was violated, as it appears in the policy.
// In CSP3, the same as effective-directive.
aViolationEventInit.mViolatedDirective = aViolatedDirective;
// original-policy
nsAutoString originalPolicy;
rv = this->GetPolicyString(aViolatedPolicyIndex, originalPolicy);
@ -1163,20 +1194,23 @@ class CSPReportSenderRunnable final : public Runnable
{
MOZ_ASSERT(NS_IsMainThread());
nsresult rv;
// 0) prepare violation data
mozilla::dom::SecurityPolicyViolationEventInit init;
mCSPContext->GatherSecurityPolicyViolationEventData(
rv = mCSPContext->GatherSecurityPolicyViolationEventData(
mBlockedContentSource, mOriginalURI,
mViolatedDirective, mViolatedPolicyIndex,
mSourceFile, mScriptSample, mLineNum,
init);
NS_ENSURE_SUCCESS(rv, rv);
// 1) notify observers
nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
NS_ASSERTION(observerService, "needs observer service");
nsresult rv = observerService->NotifyObservers(mObserverSubject,
CSP_VIOLATION_TOPIC,
mViolatedDirective.get());
rv = observerService->NotifyObservers(mObserverSubject,
CSP_VIOLATION_TOPIC,
mViolatedDirective.get());
NS_ENSURE_SUCCESS(rv, rv);
// 2) send reports for the policy that was violated

View file

@ -1264,6 +1264,12 @@ bool nsCSPDirective::equals(CSPDirective aDirective) const
return (mDirective == aDirective);
}
void
nsCSPDirective::getDirName(nsAString& outStr) const
{
outStr.AppendASCII(CSP_CSPDirectiveToString(mDirective));
}
/* =============== nsCSPChildSrcDirective ============= */
nsCSPChildSrcDirective::nsCSPChildSrcDirective(CSPDirective aDirective)
@ -1349,6 +1355,13 @@ nsBlockAllMixedContentDirective::toString(nsAString& outStr) const
nsIContentSecurityPolicy::BLOCK_ALL_MIXED_CONTENT));
}
void
nsBlockAllMixedContentDirective::getDirName(nsAString& outStr) const
{
outStr.AppendASCII(CSP_CSPDirectiveToString(
nsIContentSecurityPolicy::BLOCK_ALL_MIXED_CONTENT));
}
/* =============== nsUpgradeInsecureDirective ============= */
nsUpgradeInsecureDirective::nsUpgradeInsecureDirective(CSPDirective aDirective)
@ -1367,6 +1380,13 @@ nsUpgradeInsecureDirective::toString(nsAString& outStr) const
nsIContentSecurityPolicy::UPGRADE_IF_INSECURE_DIRECTIVE));
}
void
nsUpgradeInsecureDirective::getDirName(nsAString& outStr) const
{
outStr.AppendASCII(CSP_CSPDirectiveToString(
nsIContentSecurityPolicy::UPGRADE_IF_INSECURE_DIRECTIVE));
}
/* ===== nsRequireSRIForDirective ========================= */
nsRequireSRIForDirective::nsRequireSRIForDirective(CSPDirective aDirective)
@ -1418,6 +1438,13 @@ nsRequireSRIForDirective::allows(enum CSPKeyword aKeyword, const nsAString& aHas
return (aKeyword != CSP_REQUIRE_SRI_FOR);
}
void
nsRequireSRIForDirective::getDirName(nsAString& outStr) const
{
outStr.AppendASCII(CSP_CSPDirectiveToString(
nsIContentSecurityPolicy::REQUIRE_SRI_FOR));
}
/* ===== nsCSPPolicy ========================= */
nsCSPPolicy::nsCSPPolicy()
@ -1471,7 +1498,7 @@ nsCSPPolicy::permits(CSPDirective aDir,
if (mDirectives[i]->equals(aDir)) {
if (!mDirectives[i]->permits(aUri, aNonce, aWasRedirected, mReportOnly,
mUpgradeInsecDir, aParserCreated)) {
mDirectives[i]->toString(outViolatedDirective);
mDirectives[i]->getDirName(outViolatedDirective);
return false;
}
return true;
@ -1486,7 +1513,7 @@ nsCSPPolicy::permits(CSPDirective aDir,
if (!aSpecific && defaultDir) {
if (!defaultDir->permits(aUri, aNonce, aWasRedirected, mReportOnly,
mUpgradeInsecDir, aParserCreated)) {
defaultDir->toString(outViolatedDirective);
defaultDir->getDirName(outViolatedDirective);
return false;
}
return true;
@ -1612,7 +1639,7 @@ nsCSPPolicy::getDirectiveStringForContentType(nsContentPolicyType aContentType,
nsCSPDirective* defaultDir = nullptr;
for (uint32_t i = 0; i < mDirectives.Length(); i++) {
if (mDirectives[i]->restrictsContentType(aContentType)) {
mDirectives[i]->toString(outDirective);
mDirectives[i]->getDirName(outDirective);
return;
}
if (mDirectives[i]->isDefaultDirective()) {
@ -1622,7 +1649,7 @@ nsCSPPolicy::getDirectiveStringForContentType(nsContentPolicyType aContentType,
// if we haven't found a matching directive yet,
// the contentType must be restricted by the default directive
if (defaultDir) {
defaultDir->toString(outDirective);
defaultDir->getDirName(outDirective);
return;
}
NS_ASSERTION(false, "Can not query directive string for contentType!");

View file

@ -494,6 +494,8 @@ class nsCSPDirective {
bool visitSrcs(nsCSPSrcVisitor* aVisitor) const;
virtual void getDirName(nsAString& outStr) const;
protected:
CSPDirective mDirective;
nsTArray<nsCSPBaseSrc*> mSrcs;
@ -572,6 +574,8 @@ class nsBlockAllMixedContentDirective : public nsCSPDirective {
void addSrcs(const nsTArray<nsCSPBaseSrc*>& aSrcs)
{ MOZ_ASSERT(false, "block-all-mixed-content does not hold any srcs"); }
void getDirName(nsAString& outStr) const override;
};
/* =============== nsUpgradeInsecureDirective === */
@ -625,6 +629,8 @@ class nsUpgradeInsecureDirective : public nsCSPDirective {
void addSrcs(const nsTArray<nsCSPBaseSrc*>& aSrcs)
{ MOZ_ASSERT(false, "upgrade-insecure-requests does not hold any srcs"); }
void getDirName(nsAString& outStr) const override;
};
/* ===== nsRequireSRIForDirective ========================= */
@ -642,6 +648,7 @@ class nsRequireSRIForDirective : public nsCSPDirective {
bool restrictsContentType(nsContentPolicyType aType) const;
bool allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce,
bool aParserCreated) const;
void getDirName(nsAString& outStr) const override;
private:
nsTArray<nsContentPolicyType> mTypes;