blob: 00228577598ccb1a8f53129c8931e83e7368edc6 [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/cookies/cookie_inclusion_status.h"
#include <initializer_list>
#include <utility>
#include "base/strings/strcat.h"
#include "url/gurl.h"
namespace net {
CookieInclusionStatus::CookieInclusionStatus() = default;
CookieInclusionStatus::CookieInclusionStatus(ExclusionReason reason) {
exclusion_reasons_[reason] = true;
}
CookieInclusionStatus::CookieInclusionStatus(ExclusionReason reason,
WarningReason warning) {
exclusion_reasons_[reason] = true;
warning_reasons_[warning] = true;
}
CookieInclusionStatus::CookieInclusionStatus(WarningReason warning) {
warning_reasons_[warning] = true;
}
CookieInclusionStatus::CookieInclusionStatus(
const CookieInclusionStatus& other) = default;
CookieInclusionStatus& CookieInclusionStatus::operator=(
const CookieInclusionStatus& other) = default;
bool CookieInclusionStatus::operator==(
const CookieInclusionStatus& other) const {
return exclusion_reasons_ == other.exclusion_reasons_ &&
warning_reasons_ == other.warning_reasons_;
}
bool CookieInclusionStatus::operator!=(
const CookieInclusionStatus& other) const {
return !operator==(other);
}
bool CookieInclusionStatus::IsInclude() const {
return exclusion_reasons_.none();
}
bool CookieInclusionStatus::HasExclusionReason(ExclusionReason reason) const {
return exclusion_reasons_[reason];
}
bool CookieInclusionStatus::HasOnlyExclusionReason(
ExclusionReason reason) const {
return exclusion_reasons_[reason] && exclusion_reasons_.count() == 1;
}
void CookieInclusionStatus::AddExclusionReason(ExclusionReason reason) {
exclusion_reasons_[reason] = true;
// If the cookie would be excluded for reasons other than the new SameSite
// rules, don't bother warning about it.
MaybeClearSameSiteWarning();
}
void CookieInclusionStatus::RemoveExclusionReason(ExclusionReason reason) {
exclusion_reasons_[reason] = false;
}
void CookieInclusionStatus::RemoveExclusionReasons(
const std::vector<ExclusionReason>& reasons) {
exclusion_reasons_ = ExclusionReasonsWithout(reasons);
}
CookieInclusionStatus::ExclusionReasonBitset
CookieInclusionStatus::ExclusionReasonsWithout(
const std::vector<ExclusionReason>& reasons) const {
CookieInclusionStatus::ExclusionReasonBitset result(exclusion_reasons_);
for (const ExclusionReason reason : reasons) {
result[reason] = false;
}
return result;
}
void CookieInclusionStatus::MaybeClearSameSiteWarning() {
if (ExclusionReasonsWithout({
EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX,
EXCLUDE_SAMESITE_NONE_INSECURE,
}) != 0u) {
RemoveWarningReason(WARN_SAMESITE_UNSPECIFIED_CROSS_SITE_CONTEXT);
RemoveWarningReason(WARN_SAMESITE_NONE_INSECURE);
RemoveWarningReason(WARN_SAMESITE_UNSPECIFIED_LAX_ALLOW_UNSAFE);
}
if (!ShouldRecordDowngradeMetrics()) {
RemoveWarningReason(WARN_STRICT_LAX_DOWNGRADE_STRICT_SAMESITE);
RemoveWarningReason(WARN_STRICT_CROSS_DOWNGRADE_STRICT_SAMESITE);
RemoveWarningReason(WARN_STRICT_CROSS_DOWNGRADE_LAX_SAMESITE);
RemoveWarningReason(WARN_LAX_CROSS_DOWNGRADE_STRICT_SAMESITE);
RemoveWarningReason(WARN_LAX_CROSS_DOWNGRADE_LAX_SAMESITE);
RemoveWarningReason(WARN_CROSS_SITE_REDIRECT_DOWNGRADE_CHANGES_INCLUSION);
}
}
bool CookieInclusionStatus::ShouldRecordDowngradeMetrics() const {
return ExclusionReasonsWithout({
EXCLUDE_SAMESITE_STRICT,
EXCLUDE_SAMESITE_LAX,
EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX,
}) == 0u;
}
bool CookieInclusionStatus::ShouldWarn() const {
return warning_reasons_.any();
}
bool CookieInclusionStatus::HasWarningReason(WarningReason reason) const {
return warning_reasons_[reason];
}
bool CookieInclusionStatus::HasSchemefulDowngradeWarning(
CookieInclusionStatus::WarningReason* reason) const {
if (!ShouldWarn())
return false;
const CookieInclusionStatus::WarningReason kDowngradeWarnings[] = {
WARN_STRICT_LAX_DOWNGRADE_STRICT_SAMESITE,
WARN_STRICT_CROSS_DOWNGRADE_STRICT_SAMESITE,
WARN_STRICT_CROSS_DOWNGRADE_LAX_SAMESITE,
WARN_LAX_CROSS_DOWNGRADE_STRICT_SAMESITE,
WARN_LAX_CROSS_DOWNGRADE_LAX_SAMESITE,
};
for (auto warning : kDowngradeWarnings) {
if (!HasWarningReason(warning))
continue;
if (reason)
*reason = warning;
return true;
}
return false;
}
void CookieInclusionStatus::AddWarningReason(WarningReason reason) {
warning_reasons_[reason] = true;
}
void CookieInclusionStatus::RemoveWarningReason(WarningReason reason) {
warning_reasons_[reason] = false;
}
CookieInclusionStatus::ContextDowngradeMetricValues
CookieInclusionStatus::GetBreakingDowngradeMetricsEnumValue(
const GURL& url) const {
bool url_is_secure = url.SchemeIsCryptographic();
// Start the |reason| as something other than the downgrade warnings.
WarningReason reason = WarningReason::NUM_WARNING_REASONS;
// Don't bother checking the return value because the default switch case
// will handle if no reason was found.
HasSchemefulDowngradeWarning(&reason);
switch (reason) {
case WarningReason::WARN_STRICT_LAX_DOWNGRADE_STRICT_SAMESITE:
return url_is_secure
? ContextDowngradeMetricValues::kStrictLaxStrictSecure
: ContextDowngradeMetricValues::kStrictLaxStrictInsecure;
case WarningReason::WARN_STRICT_CROSS_DOWNGRADE_STRICT_SAMESITE:
return url_is_secure
? ContextDowngradeMetricValues::kStrictCrossStrictSecure
: ContextDowngradeMetricValues::kStrictCrossStrictInsecure;
case WarningReason::WARN_STRICT_CROSS_DOWNGRADE_LAX_SAMESITE:
return url_is_secure
? ContextDowngradeMetricValues::kStrictCrossLaxSecure
: ContextDowngradeMetricValues::kStrictCrossLaxInsecure;
case WarningReason::WARN_LAX_CROSS_DOWNGRADE_STRICT_SAMESITE:
return url_is_secure
? ContextDowngradeMetricValues::kLaxCrossStrictSecure
: ContextDowngradeMetricValues::kLaxCrossStrictInsecure;
case WarningReason::WARN_LAX_CROSS_DOWNGRADE_LAX_SAMESITE:
return url_is_secure ? ContextDowngradeMetricValues::kLaxCrossLaxSecure
: ContextDowngradeMetricValues::kLaxCrossLaxInsecure;
default:
return url_is_secure ? ContextDowngradeMetricValues::kNoDowngradeSecure
: ContextDowngradeMetricValues::kNoDowngradeInsecure;
}
}
std::string CookieInclusionStatus::GetDebugString() const {
std::string out;
if (IsInclude())
base::StrAppend(&out, {"INCLUDE, "});
for (const auto& reason :
std::initializer_list<std::pair<ExclusionReason, std::string>>{
{EXCLUDE_UNKNOWN_ERROR, "EXCLUDE_UNKNOWN_ERROR"},
{EXCLUDE_HTTP_ONLY, "EXCLUDE_HTTP_ONLY"},
{EXCLUDE_SECURE_ONLY, "EXCLUDE_SECURE_ONLY"},
{EXCLUDE_DOMAIN_MISMATCH, "EXCLUDE_DOMAIN_MISMATCH"},
{EXCLUDE_NOT_ON_PATH, "EXCLUDE_NOT_ON_PATH"},
{EXCLUDE_SAMESITE_STRICT, "EXCLUDE_SAMESITE_STRICT"},
{EXCLUDE_SAMESITE_LAX, "EXCLUDE_SAMESITE_LAX"},
{EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX,
"EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX"},
{EXCLUDE_SAMESITE_NONE_INSECURE, "EXCLUDE_SAMESITE_NONE_INSECURE"},
{EXCLUDE_USER_PREFERENCES, "EXCLUDE_USER_PREFERENCES"},
{EXCLUDE_SAMEPARTY_CROSS_PARTY_CONTEXT,
"EXCLUDE_SAMEPARTY_CROSS_PARTY_CONTEXT"},
{EXCLUDE_FAILURE_TO_STORE, "EXCLUDE_FAILURE_TO_STORE"},
{EXCLUDE_NONCOOKIEABLE_SCHEME, "EXCLUDE_NONCOOKIEABLE_SCHEME"},
{EXCLUDE_OVERWRITE_SECURE, "EXCLUDE_OVERWRITE_SECURE"},
{EXCLUDE_OVERWRITE_HTTP_ONLY, "EXCLUDE_OVERWRITE_HTTP_ONLY"},
{EXCLUDE_INVALID_DOMAIN, "EXCLUDE_INVALID_DOMAIN"},
{EXCLUDE_INVALID_PREFIX, "EXCLUDE_INVALID_PREFIX"},
{EXCLUDE_INVALID_SAMEPARTY, "EXCLUDE_INVALID_SAMEPARTY"},
{EXCLUDE_INVALID_PARTITIONED, "EXCLUDE_INVALID_PARTITIONED"},
{EXCLUDE_NAME_VALUE_PAIR_EXCEEDS_MAX_SIZE,
"EXCLUDE_NAME_VALUE_PAIR_EXCEEDS_MAX_SIZE"},
{EXCLUDE_ATTRIBUTE_VALUE_EXCEEDS_MAX_SIZE,
"EXCLUDE_ATTRIBUTE_VALUE_EXCEEDS_MAX_SIZE"},
{EXCLUDE_DOMAIN_NON_ASCII, "EXCLUDE_DOMAIN_NON_ASCII"},
}) {
if (HasExclusionReason(reason.first))
base::StrAppend(&out, {reason.second, ", "});
}
// Add warning
if (!ShouldWarn()) {
base::StrAppend(&out, {"DO_NOT_WARN"});
return out;
}
for (const auto& reason :
std::initializer_list<std::pair<WarningReason, std::string>>{
{WARN_SAMESITE_UNSPECIFIED_CROSS_SITE_CONTEXT,
"WARN_SAMESITE_UNSPECIFIED_CROSS_SITE_CONTEXT"},
{WARN_SAMESITE_NONE_INSECURE, "WARN_SAMESITE_NONE_INSECURE"},
{WARN_SAMESITE_UNSPECIFIED_LAX_ALLOW_UNSAFE,
"WARN_SAMESITE_UNSPECIFIED_LAX_ALLOW_UNSAFE"},
{WARN_STRICT_LAX_DOWNGRADE_STRICT_SAMESITE,
"WARN_STRICT_LAX_DOWNGRADE_STRICT_SAMESITE"},
{WARN_STRICT_CROSS_DOWNGRADE_STRICT_SAMESITE,
"WARN_STRICT_CROSS_DOWNGRADE_STRICT_SAMESITE"},
{WARN_STRICT_CROSS_DOWNGRADE_LAX_SAMESITE,
"WARN_STRICT_CROSS_DOWNGRADE_LAX_SAMESITE"},
{WARN_LAX_CROSS_DOWNGRADE_STRICT_SAMESITE,
"WARN_LAX_CROSS_DOWNGRADE_STRICT_SAMESITE"},
{WARN_LAX_CROSS_DOWNGRADE_LAX_SAMESITE,
"WARN_LAX_CROSS_DOWNGRADE_LAX_SAMESITE"},
{WARN_SECURE_ACCESS_GRANTED_NON_CRYPTOGRAPHIC,
"WARN_SECURE_ACCESS_GRANTED_NON_CRYPTOGRAPHIC"},
{WARN_SAMEPARTY_EXCLUSION_OVERRULED_SAMESITE,
"WARN_SAMEPARTY_EXCLUSION_OVERRULED_SAMESITE"},
{WARN_SAMEPARTY_INCLUSION_OVERRULED_SAMESITE,
"WARN_SAMEPARTY_INCLUSION_OVERRULED_SAMESITE"},
{WARN_CROSS_SITE_REDIRECT_DOWNGRADE_CHANGES_INCLUSION,
"WARN_CROSS_SITE_REDIRECT_DOWNGRADE_CHANGES_INCLUSION"},
{WARN_ATTRIBUTE_VALUE_EXCEEDS_MAX_SIZE,
"WARN_ATTRIBUTE_VALUE_EXCEEDS_MAX_SIZE"},
{WARN_DOMAIN_NON_ASCII, "WARN_DOMAIN_NON_ASCII"},
}) {
if (HasWarningReason(reason.first))
base::StrAppend(&out, {reason.second, ", "});
}
// Strip trailing comma and space.
out.erase(out.end() - 2, out.end());
return out;
}
bool CookieInclusionStatus::HasExactlyExclusionReasonsForTesting(
std::vector<CookieInclusionStatus::ExclusionReason> reasons) const {
CookieInclusionStatus expected = MakeFromReasonsForTesting(reasons);
return expected.exclusion_reasons_ == exclusion_reasons_;
}
bool CookieInclusionStatus::HasExactlyWarningReasonsForTesting(
std::vector<WarningReason> reasons) const {
CookieInclusionStatus expected = MakeFromReasonsForTesting({}, reasons);
return expected.warning_reasons_ == warning_reasons_;
}
// static
bool CookieInclusionStatus::ValidateExclusionAndWarningFromWire(
uint32_t exclusion_reasons,
uint32_t warning_reasons) {
uint32_t exclusion_mask =
static_cast<uint32_t>(~0ul << ExclusionReason::NUM_EXCLUSION_REASONS);
uint32_t warning_mask =
static_cast<uint32_t>(~0ul << WarningReason::NUM_WARNING_REASONS);
return (exclusion_reasons & exclusion_mask) == 0 &&
(warning_reasons & warning_mask) == 0;
}
CookieInclusionStatus CookieInclusionStatus::MakeFromReasonsForTesting(
std::vector<ExclusionReason> reasons,
std::vector<WarningReason> warnings) {
CookieInclusionStatus status;
for (ExclusionReason reason : reasons) {
status.AddExclusionReason(reason);
}
for (WarningReason warning : warnings) {
status.AddWarningReason(warning);
}
return status;
}
bool CookieInclusionStatus::ExcludedByUserPreferences() const {
if (HasOnlyExclusionReason(ExclusionReason::EXCLUDE_USER_PREFERENCES))
return true;
return exclusion_reasons_.count() == 2 &&
exclusion_reasons_[ExclusionReason::EXCLUDE_USER_PREFERENCES] &&
exclusion_reasons_
[ExclusionReason::
EXCLUDE_THIRD_PARTY_BLOCKED_WITHIN_FIRST_PARTY_SET];
}
} // namespace net