// 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 #include #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& reasons) { exclusion_reasons_ = ExclusionReasonsWithout(reasons); } CookieInclusionStatus::ExclusionReasonBitset CookieInclusionStatus::ExclusionReasonsWithout( const std::vector& 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>{ {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>{ {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 reasons) const { CookieInclusionStatus expected = MakeFromReasonsForTesting(reasons); return expected.exclusion_reasons_ == exclusion_reasons_; } bool CookieInclusionStatus::HasExactlyWarningReasonsForTesting( std::vector 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(~0ul << ExclusionReason::NUM_EXCLUSION_REASONS); uint32_t warning_mask = static_cast(~0ul << WarningReason::NUM_WARNING_REASONS); return (exclusion_reasons & exclusion_mask) == 0 && (warning_reasons & warning_mask) == 0; } CookieInclusionStatus CookieInclusionStatus::MakeFromReasonsForTesting( std::vector reasons, std::vector 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