• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "net/cookies/cookie_inclusion_status.h"
6 
7 #include <initializer_list>
8 #include <utility>
9 
10 #include "base/strings/strcat.h"
11 #include "url/gurl.h"
12 
13 namespace net {
14 
15 CookieInclusionStatus::CookieInclusionStatus() = default;
16 
CookieInclusionStatus(ExclusionReason reason)17 CookieInclusionStatus::CookieInclusionStatus(ExclusionReason reason) {
18   exclusion_reasons_[reason] = true;
19 }
20 
CookieInclusionStatus(ExclusionReason reason,WarningReason warning)21 CookieInclusionStatus::CookieInclusionStatus(ExclusionReason reason,
22                                              WarningReason warning) {
23   exclusion_reasons_[reason] = true;
24   warning_reasons_[warning] = true;
25 }
26 
CookieInclusionStatus(WarningReason warning)27 CookieInclusionStatus::CookieInclusionStatus(WarningReason warning) {
28   warning_reasons_[warning] = true;
29 }
30 
31 CookieInclusionStatus::CookieInclusionStatus(
32     const CookieInclusionStatus& other) = default;
33 
34 CookieInclusionStatus& CookieInclusionStatus::operator=(
35     const CookieInclusionStatus& other) = default;
36 
operator ==(const CookieInclusionStatus & other) const37 bool CookieInclusionStatus::operator==(
38     const CookieInclusionStatus& other) const {
39   return exclusion_reasons_ == other.exclusion_reasons_ &&
40          warning_reasons_ == other.warning_reasons_;
41 }
42 
operator !=(const CookieInclusionStatus & other) const43 bool CookieInclusionStatus::operator!=(
44     const CookieInclusionStatus& other) const {
45   return !operator==(other);
46 }
47 
IsInclude() const48 bool CookieInclusionStatus::IsInclude() const {
49   return exclusion_reasons_.none();
50 }
51 
HasExclusionReason(ExclusionReason reason) const52 bool CookieInclusionStatus::HasExclusionReason(ExclusionReason reason) const {
53   return exclusion_reasons_[reason];
54 }
55 
HasOnlyExclusionReason(ExclusionReason reason) const56 bool CookieInclusionStatus::HasOnlyExclusionReason(
57     ExclusionReason reason) const {
58   return exclusion_reasons_[reason] && exclusion_reasons_.count() == 1;
59 }
60 
AddExclusionReason(ExclusionReason reason)61 void CookieInclusionStatus::AddExclusionReason(ExclusionReason reason) {
62   exclusion_reasons_[reason] = true;
63   // If the cookie would be excluded for reasons other than the new SameSite
64   // rules, don't bother warning about it.
65   MaybeClearSameSiteWarning();
66 }
67 
RemoveExclusionReason(ExclusionReason reason)68 void CookieInclusionStatus::RemoveExclusionReason(ExclusionReason reason) {
69   exclusion_reasons_[reason] = false;
70 }
71 
RemoveExclusionReasons(const std::vector<ExclusionReason> & reasons)72 void CookieInclusionStatus::RemoveExclusionReasons(
73     const std::vector<ExclusionReason>& reasons) {
74   exclusion_reasons_ = ExclusionReasonsWithout(reasons);
75 }
76 
77 CookieInclusionStatus::ExclusionReasonBitset
ExclusionReasonsWithout(const std::vector<ExclusionReason> & reasons) const78 CookieInclusionStatus::ExclusionReasonsWithout(
79     const std::vector<ExclusionReason>& reasons) const {
80   CookieInclusionStatus::ExclusionReasonBitset result(exclusion_reasons_);
81   for (const ExclusionReason reason : reasons) {
82     result[reason] = false;
83   }
84   return result;
85 }
86 
MaybeClearSameSiteWarning()87 void CookieInclusionStatus::MaybeClearSameSiteWarning() {
88   if (ExclusionReasonsWithout({
89           EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX,
90           EXCLUDE_SAMESITE_NONE_INSECURE,
91       }) != 0u) {
92     RemoveWarningReason(WARN_SAMESITE_UNSPECIFIED_CROSS_SITE_CONTEXT);
93     RemoveWarningReason(WARN_SAMESITE_NONE_INSECURE);
94     RemoveWarningReason(WARN_SAMESITE_UNSPECIFIED_LAX_ALLOW_UNSAFE);
95   }
96 
97   if (!ShouldRecordDowngradeMetrics()) {
98     RemoveWarningReason(WARN_STRICT_LAX_DOWNGRADE_STRICT_SAMESITE);
99     RemoveWarningReason(WARN_STRICT_CROSS_DOWNGRADE_STRICT_SAMESITE);
100     RemoveWarningReason(WARN_STRICT_CROSS_DOWNGRADE_LAX_SAMESITE);
101     RemoveWarningReason(WARN_LAX_CROSS_DOWNGRADE_STRICT_SAMESITE);
102     RemoveWarningReason(WARN_LAX_CROSS_DOWNGRADE_LAX_SAMESITE);
103 
104     RemoveWarningReason(WARN_CROSS_SITE_REDIRECT_DOWNGRADE_CHANGES_INCLUSION);
105   }
106 }
107 
ShouldRecordDowngradeMetrics() const108 bool CookieInclusionStatus::ShouldRecordDowngradeMetrics() const {
109   return ExclusionReasonsWithout({
110              EXCLUDE_SAMESITE_STRICT,
111              EXCLUDE_SAMESITE_LAX,
112              EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX,
113          }) == 0u;
114 }
115 
ShouldWarn() const116 bool CookieInclusionStatus::ShouldWarn() const {
117   return warning_reasons_.any();
118 }
119 
HasWarningReason(WarningReason reason) const120 bool CookieInclusionStatus::HasWarningReason(WarningReason reason) const {
121   return warning_reasons_[reason];
122 }
123 
HasSchemefulDowngradeWarning(CookieInclusionStatus::WarningReason * reason) const124 bool CookieInclusionStatus::HasSchemefulDowngradeWarning(
125     CookieInclusionStatus::WarningReason* reason) const {
126   if (!ShouldWarn())
127     return false;
128 
129   const CookieInclusionStatus::WarningReason kDowngradeWarnings[] = {
130       WARN_STRICT_LAX_DOWNGRADE_STRICT_SAMESITE,
131       WARN_STRICT_CROSS_DOWNGRADE_STRICT_SAMESITE,
132       WARN_STRICT_CROSS_DOWNGRADE_LAX_SAMESITE,
133       WARN_LAX_CROSS_DOWNGRADE_STRICT_SAMESITE,
134       WARN_LAX_CROSS_DOWNGRADE_LAX_SAMESITE,
135   };
136 
137   for (auto warning : kDowngradeWarnings) {
138     if (!HasWarningReason(warning))
139       continue;
140 
141     if (reason)
142       *reason = warning;
143 
144     return true;
145   }
146 
147   return false;
148 }
149 
AddWarningReason(WarningReason reason)150 void CookieInclusionStatus::AddWarningReason(WarningReason reason) {
151   warning_reasons_[reason] = true;
152 }
153 
RemoveWarningReason(WarningReason reason)154 void CookieInclusionStatus::RemoveWarningReason(WarningReason reason) {
155   warning_reasons_[reason] = false;
156 }
157 
158 CookieInclusionStatus::ContextDowngradeMetricValues
GetBreakingDowngradeMetricsEnumValue(const GURL & url) const159 CookieInclusionStatus::GetBreakingDowngradeMetricsEnumValue(
160     const GURL& url) const {
161   bool url_is_secure = url.SchemeIsCryptographic();
162 
163   // Start the |reason| as something other than the downgrade warnings.
164   WarningReason reason = WarningReason::NUM_WARNING_REASONS;
165 
166   // Don't bother checking the return value because the default switch case
167   // will handle if no reason was found.
168   HasSchemefulDowngradeWarning(&reason);
169 
170   switch (reason) {
171     case WarningReason::WARN_STRICT_LAX_DOWNGRADE_STRICT_SAMESITE:
172       return url_is_secure
173                  ? ContextDowngradeMetricValues::kStrictLaxStrictSecure
174                  : ContextDowngradeMetricValues::kStrictLaxStrictInsecure;
175     case WarningReason::WARN_STRICT_CROSS_DOWNGRADE_STRICT_SAMESITE:
176       return url_is_secure
177                  ? ContextDowngradeMetricValues::kStrictCrossStrictSecure
178                  : ContextDowngradeMetricValues::kStrictCrossStrictInsecure;
179     case WarningReason::WARN_STRICT_CROSS_DOWNGRADE_LAX_SAMESITE:
180       return url_is_secure
181                  ? ContextDowngradeMetricValues::kStrictCrossLaxSecure
182                  : ContextDowngradeMetricValues::kStrictCrossLaxInsecure;
183     case WarningReason::WARN_LAX_CROSS_DOWNGRADE_STRICT_SAMESITE:
184       return url_is_secure
185                  ? ContextDowngradeMetricValues::kLaxCrossStrictSecure
186                  : ContextDowngradeMetricValues::kLaxCrossStrictInsecure;
187     case WarningReason::WARN_LAX_CROSS_DOWNGRADE_LAX_SAMESITE:
188       return url_is_secure ? ContextDowngradeMetricValues::kLaxCrossLaxSecure
189                            : ContextDowngradeMetricValues::kLaxCrossLaxInsecure;
190     default:
191       return url_is_secure ? ContextDowngradeMetricValues::kNoDowngradeSecure
192                            : ContextDowngradeMetricValues::kNoDowngradeInsecure;
193   }
194 }
195 
GetDebugString() const196 std::string CookieInclusionStatus::GetDebugString() const {
197   std::string out;
198 
199   if (IsInclude())
200     base::StrAppend(&out, {"INCLUDE, "});
201   for (const auto& reason :
202        std::initializer_list<std::pair<ExclusionReason, std::string>>{
203            {EXCLUDE_UNKNOWN_ERROR, "EXCLUDE_UNKNOWN_ERROR"},
204            {EXCLUDE_HTTP_ONLY, "EXCLUDE_HTTP_ONLY"},
205            {EXCLUDE_SECURE_ONLY, "EXCLUDE_SECURE_ONLY"},
206            {EXCLUDE_DOMAIN_MISMATCH, "EXCLUDE_DOMAIN_MISMATCH"},
207            {EXCLUDE_NOT_ON_PATH, "EXCLUDE_NOT_ON_PATH"},
208            {EXCLUDE_SAMESITE_STRICT, "EXCLUDE_SAMESITE_STRICT"},
209            {EXCLUDE_SAMESITE_LAX, "EXCLUDE_SAMESITE_LAX"},
210            {EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX,
211             "EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX"},
212            {EXCLUDE_SAMESITE_NONE_INSECURE, "EXCLUDE_SAMESITE_NONE_INSECURE"},
213            {EXCLUDE_USER_PREFERENCES, "EXCLUDE_USER_PREFERENCES"},
214            {EXCLUDE_SAMEPARTY_CROSS_PARTY_CONTEXT,
215             "EXCLUDE_SAMEPARTY_CROSS_PARTY_CONTEXT"},
216            {EXCLUDE_FAILURE_TO_STORE, "EXCLUDE_FAILURE_TO_STORE"},
217            {EXCLUDE_NONCOOKIEABLE_SCHEME, "EXCLUDE_NONCOOKIEABLE_SCHEME"},
218            {EXCLUDE_OVERWRITE_SECURE, "EXCLUDE_OVERWRITE_SECURE"},
219            {EXCLUDE_OVERWRITE_HTTP_ONLY, "EXCLUDE_OVERWRITE_HTTP_ONLY"},
220            {EXCLUDE_INVALID_DOMAIN, "EXCLUDE_INVALID_DOMAIN"},
221            {EXCLUDE_INVALID_PREFIX, "EXCLUDE_INVALID_PREFIX"},
222            {EXCLUDE_INVALID_SAMEPARTY, "EXCLUDE_INVALID_SAMEPARTY"},
223            {EXCLUDE_INVALID_PARTITIONED, "EXCLUDE_INVALID_PARTITIONED"},
224            {EXCLUDE_NAME_VALUE_PAIR_EXCEEDS_MAX_SIZE,
225             "EXCLUDE_NAME_VALUE_PAIR_EXCEEDS_MAX_SIZE"},
226            {EXCLUDE_ATTRIBUTE_VALUE_EXCEEDS_MAX_SIZE,
227             "EXCLUDE_ATTRIBUTE_VALUE_EXCEEDS_MAX_SIZE"},
228            {EXCLUDE_DOMAIN_NON_ASCII, "EXCLUDE_DOMAIN_NON_ASCII"},
229        }) {
230     if (HasExclusionReason(reason.first))
231       base::StrAppend(&out, {reason.second, ", "});
232   }
233 
234   // Add warning
235   if (!ShouldWarn()) {
236     base::StrAppend(&out, {"DO_NOT_WARN"});
237     return out;
238   }
239 
240   for (const auto& reason :
241        std::initializer_list<std::pair<WarningReason, std::string>>{
242            {WARN_SAMESITE_UNSPECIFIED_CROSS_SITE_CONTEXT,
243             "WARN_SAMESITE_UNSPECIFIED_CROSS_SITE_CONTEXT"},
244            {WARN_SAMESITE_NONE_INSECURE, "WARN_SAMESITE_NONE_INSECURE"},
245            {WARN_SAMESITE_UNSPECIFIED_LAX_ALLOW_UNSAFE,
246             "WARN_SAMESITE_UNSPECIFIED_LAX_ALLOW_UNSAFE"},
247            {WARN_STRICT_LAX_DOWNGRADE_STRICT_SAMESITE,
248             "WARN_STRICT_LAX_DOWNGRADE_STRICT_SAMESITE"},
249            {WARN_STRICT_CROSS_DOWNGRADE_STRICT_SAMESITE,
250             "WARN_STRICT_CROSS_DOWNGRADE_STRICT_SAMESITE"},
251            {WARN_STRICT_CROSS_DOWNGRADE_LAX_SAMESITE,
252             "WARN_STRICT_CROSS_DOWNGRADE_LAX_SAMESITE"},
253            {WARN_LAX_CROSS_DOWNGRADE_STRICT_SAMESITE,
254             "WARN_LAX_CROSS_DOWNGRADE_STRICT_SAMESITE"},
255            {WARN_LAX_CROSS_DOWNGRADE_LAX_SAMESITE,
256             "WARN_LAX_CROSS_DOWNGRADE_LAX_SAMESITE"},
257            {WARN_SECURE_ACCESS_GRANTED_NON_CRYPTOGRAPHIC,
258             "WARN_SECURE_ACCESS_GRANTED_NON_CRYPTOGRAPHIC"},
259            {WARN_SAMEPARTY_EXCLUSION_OVERRULED_SAMESITE,
260             "WARN_SAMEPARTY_EXCLUSION_OVERRULED_SAMESITE"},
261            {WARN_SAMEPARTY_INCLUSION_OVERRULED_SAMESITE,
262             "WARN_SAMEPARTY_INCLUSION_OVERRULED_SAMESITE"},
263            {WARN_CROSS_SITE_REDIRECT_DOWNGRADE_CHANGES_INCLUSION,
264             "WARN_CROSS_SITE_REDIRECT_DOWNGRADE_CHANGES_INCLUSION"},
265            {WARN_ATTRIBUTE_VALUE_EXCEEDS_MAX_SIZE,
266             "WARN_ATTRIBUTE_VALUE_EXCEEDS_MAX_SIZE"},
267            {WARN_DOMAIN_NON_ASCII, "WARN_DOMAIN_NON_ASCII"},
268        }) {
269     if (HasWarningReason(reason.first))
270       base::StrAppend(&out, {reason.second, ", "});
271   }
272 
273   // Strip trailing comma and space.
274   out.erase(out.end() - 2, out.end());
275 
276   return out;
277 }
278 
HasExactlyExclusionReasonsForTesting(std::vector<CookieInclusionStatus::ExclusionReason> reasons) const279 bool CookieInclusionStatus::HasExactlyExclusionReasonsForTesting(
280     std::vector<CookieInclusionStatus::ExclusionReason> reasons) const {
281   CookieInclusionStatus expected = MakeFromReasonsForTesting(reasons);
282   return expected.exclusion_reasons_ == exclusion_reasons_;
283 }
284 
HasExactlyWarningReasonsForTesting(std::vector<WarningReason> reasons) const285 bool CookieInclusionStatus::HasExactlyWarningReasonsForTesting(
286     std::vector<WarningReason> reasons) const {
287   CookieInclusionStatus expected = MakeFromReasonsForTesting({}, reasons);
288   return expected.warning_reasons_ == warning_reasons_;
289 }
290 
291 // static
ValidateExclusionAndWarningFromWire(uint32_t exclusion_reasons,uint32_t warning_reasons)292 bool CookieInclusionStatus::ValidateExclusionAndWarningFromWire(
293     uint32_t exclusion_reasons,
294     uint32_t warning_reasons) {
295   uint32_t exclusion_mask =
296       static_cast<uint32_t>(~0ul << ExclusionReason::NUM_EXCLUSION_REASONS);
297   uint32_t warning_mask =
298       static_cast<uint32_t>(~0ul << WarningReason::NUM_WARNING_REASONS);
299   return (exclusion_reasons & exclusion_mask) == 0 &&
300          (warning_reasons & warning_mask) == 0;
301 }
302 
MakeFromReasonsForTesting(std::vector<ExclusionReason> reasons,std::vector<WarningReason> warnings)303 CookieInclusionStatus CookieInclusionStatus::MakeFromReasonsForTesting(
304     std::vector<ExclusionReason> reasons,
305     std::vector<WarningReason> warnings) {
306   CookieInclusionStatus status;
307   for (ExclusionReason reason : reasons) {
308     status.AddExclusionReason(reason);
309   }
310   for (WarningReason warning : warnings) {
311     status.AddWarningReason(warning);
312   }
313   return status;
314 }
315 
ExcludedByUserPreferences() const316 bool CookieInclusionStatus::ExcludedByUserPreferences() const {
317   if (HasOnlyExclusionReason(ExclusionReason::EXCLUDE_USER_PREFERENCES))
318     return true;
319   return exclusion_reasons_.count() == 2 &&
320          exclusion_reasons_[ExclusionReason::EXCLUDE_USER_PREFERENCES] &&
321          exclusion_reasons_
322              [ExclusionReason::
323                   EXCLUDE_THIRD_PARTY_BLOCKED_WITHIN_FIRST_PARTY_SET];
324 }
325 
326 }  // namespace net
327