xref: /aosp_15_r20/external/cronet/net/cookies/cookie_inclusion_status.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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 <string_view>
9 #include <tuple>
10 #include <utility>
11 
12 #include "base/notreached.h"
13 #include "base/ranges/algorithm.h"
14 #include "base/strings/strcat.h"
15 #include "url/gurl.h"
16 
17 namespace net {
18 
19 CookieInclusionStatus::CookieInclusionStatus() = default;
20 
CookieInclusionStatus(ExclusionReason reason)21 CookieInclusionStatus::CookieInclusionStatus(ExclusionReason reason) {
22   exclusion_reasons_[reason] = true;
23 }
24 
CookieInclusionStatus(ExclusionReason reason,WarningReason warning)25 CookieInclusionStatus::CookieInclusionStatus(ExclusionReason reason,
26                                              WarningReason warning) {
27   exclusion_reasons_[reason] = true;
28   warning_reasons_[warning] = true;
29 }
30 
CookieInclusionStatus(WarningReason warning)31 CookieInclusionStatus::CookieInclusionStatus(WarningReason warning) {
32   warning_reasons_[warning] = true;
33 }
34 
CookieInclusionStatus(std::vector<ExclusionReason> exclusions,std::vector<WarningReason> warnings,ExemptionReason exemption)35 CookieInclusionStatus::CookieInclusionStatus(
36     std::vector<ExclusionReason> exclusions,
37     std::vector<WarningReason> warnings,
38     ExemptionReason exemption) {
39   for (ExclusionReason reason : exclusions) {
40     exclusion_reasons_[reason] = true;
41   }
42   for (WarningReason warning : warnings) {
43     warning_reasons_[warning] = true;
44   }
45   exemption_reason_ = exemption;
46 }
47 
48 CookieInclusionStatus::CookieInclusionStatus(
49     const CookieInclusionStatus& other) = default;
50 
51 CookieInclusionStatus& CookieInclusionStatus::operator=(
52     const CookieInclusionStatus& other) = default;
53 
operator ==(const CookieInclusionStatus & other) const54 bool CookieInclusionStatus::operator==(
55     const CookieInclusionStatus& other) const {
56   return exclusion_reasons_ == other.exclusion_reasons_ &&
57          warning_reasons_ == other.warning_reasons_ &&
58          exemption_reason_ == other.exemption_reason_;
59 }
60 
operator !=(const CookieInclusionStatus & other) const61 bool CookieInclusionStatus::operator!=(
62     const CookieInclusionStatus& other) const {
63   return !operator==(other);
64 }
65 
operator <(const CookieInclusionStatus & other) const66 bool CookieInclusionStatus::operator<(
67     const CookieInclusionStatus& other) const {
68   static_assert(NUM_EXCLUSION_REASONS <= sizeof(unsigned long) * CHAR_BIT,
69                 "use .ullong() instead");
70   static_assert(NUM_WARNING_REASONS <= sizeof(unsigned long) * CHAR_BIT,
71                 "use .ullong() instead");
72   return std::make_tuple(exclusion_reasons_.to_ulong(),
73                          warning_reasons_.to_ulong(), exemption_reason_) <
74          std::make_tuple(other.exclusion_reasons_.to_ulong(),
75                          other.warning_reasons_.to_ulong(),
76                          other.exemption_reason_);
77 }
78 
IsInclude() const79 bool CookieInclusionStatus::IsInclude() const {
80   return exclusion_reasons_.none();
81 }
82 
HasExclusionReason(ExclusionReason reason) const83 bool CookieInclusionStatus::HasExclusionReason(ExclusionReason reason) const {
84   return exclusion_reasons_[reason];
85 }
86 
HasOnlyExclusionReason(ExclusionReason reason) const87 bool CookieInclusionStatus::HasOnlyExclusionReason(
88     ExclusionReason reason) const {
89   return exclusion_reasons_[reason] && exclusion_reasons_.count() == 1;
90 }
91 
AddExclusionReason(ExclusionReason reason)92 void CookieInclusionStatus::AddExclusionReason(ExclusionReason reason) {
93   exclusion_reasons_[reason] = true;
94   // If the cookie would be excluded for reasons other than the new SameSite
95   // rules, don't bother warning about it.
96   MaybeClearSameSiteWarning();
97   // If the cookie would be excluded for reasons unrelated to 3pcd, don't bother
98   // warning about 3pcd.
99   MaybeClearThirdPartyPhaseoutReason();
100   // If the cookie would have been excluded, clear the exemption reason.
101   exemption_reason_ = ExemptionReason::kNone;
102 }
103 
RemoveExclusionReason(ExclusionReason reason)104 void CookieInclusionStatus::RemoveExclusionReason(ExclusionReason reason) {
105   exclusion_reasons_[reason] = false;
106 }
107 
RemoveExclusionReasons(const std::vector<ExclusionReason> & reasons)108 void CookieInclusionStatus::RemoveExclusionReasons(
109     const std::vector<ExclusionReason>& reasons) {
110   exclusion_reasons_ = ExclusionReasonsWithout(reasons);
111 }
112 
MaybeSetExemptionReason(ExemptionReason reason)113 void CookieInclusionStatus::MaybeSetExemptionReason(ExemptionReason reason) {
114   if (IsInclude() && exemption_reason_ == ExemptionReason::kNone) {
115     exemption_reason_ = reason;
116   }
117 }
118 
119 CookieInclusionStatus::ExclusionReasonBitset
ExclusionReasonsWithout(const std::vector<ExclusionReason> & reasons) const120 CookieInclusionStatus::ExclusionReasonsWithout(
121     const std::vector<ExclusionReason>& reasons) const {
122   CookieInclusionStatus::ExclusionReasonBitset result(exclusion_reasons_);
123   for (const ExclusionReason reason : reasons) {
124     result[reason] = false;
125   }
126   return result;
127 }
128 
MaybeClearSameSiteWarning()129 void CookieInclusionStatus::MaybeClearSameSiteWarning() {
130   if (ExclusionReasonsWithout({
131           EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX,
132           EXCLUDE_SAMESITE_NONE_INSECURE,
133       }) != 0u) {
134     RemoveWarningReason(WARN_SAMESITE_UNSPECIFIED_CROSS_SITE_CONTEXT);
135     RemoveWarningReason(WARN_SAMESITE_NONE_INSECURE);
136     RemoveWarningReason(WARN_SAMESITE_UNSPECIFIED_LAX_ALLOW_UNSAFE);
137   }
138 
139   if (!ShouldRecordDowngradeMetrics()) {
140     RemoveWarningReason(WARN_STRICT_LAX_DOWNGRADE_STRICT_SAMESITE);
141     RemoveWarningReason(WARN_STRICT_CROSS_DOWNGRADE_STRICT_SAMESITE);
142     RemoveWarningReason(WARN_STRICT_CROSS_DOWNGRADE_LAX_SAMESITE);
143     RemoveWarningReason(WARN_LAX_CROSS_DOWNGRADE_STRICT_SAMESITE);
144     RemoveWarningReason(WARN_LAX_CROSS_DOWNGRADE_LAX_SAMESITE);
145 
146     RemoveWarningReason(WARN_CROSS_SITE_REDIRECT_DOWNGRADE_CHANGES_INCLUSION);
147   }
148 }
149 
MaybeClearThirdPartyPhaseoutReason()150 void CookieInclusionStatus::MaybeClearThirdPartyPhaseoutReason() {
151   if (!IsInclude()) {
152     RemoveWarningReason(WARN_THIRD_PARTY_PHASEOUT);
153   }
154   if (ExclusionReasonsWithout(
155           {EXCLUDE_THIRD_PARTY_PHASEOUT,
156            EXCLUDE_THIRD_PARTY_BLOCKED_WITHIN_FIRST_PARTY_SET}) != 0u) {
157     RemoveExclusionReason(EXCLUDE_THIRD_PARTY_PHASEOUT);
158     RemoveExclusionReason(EXCLUDE_THIRD_PARTY_BLOCKED_WITHIN_FIRST_PARTY_SET);
159   }
160 }
161 
ShouldRecordDowngradeMetrics() const162 bool CookieInclusionStatus::ShouldRecordDowngradeMetrics() const {
163   return ExclusionReasonsWithout({
164              EXCLUDE_SAMESITE_STRICT,
165              EXCLUDE_SAMESITE_LAX,
166              EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX,
167          }) == 0u;
168 }
169 
ShouldWarn() const170 bool CookieInclusionStatus::ShouldWarn() const {
171   return warning_reasons_.any();
172 }
173 
HasWarningReason(WarningReason reason) const174 bool CookieInclusionStatus::HasWarningReason(WarningReason reason) const {
175   return warning_reasons_[reason];
176 }
177 
HasSchemefulDowngradeWarning(CookieInclusionStatus::WarningReason * reason) const178 bool CookieInclusionStatus::HasSchemefulDowngradeWarning(
179     CookieInclusionStatus::WarningReason* reason) const {
180   if (!ShouldWarn())
181     return false;
182 
183   const CookieInclusionStatus::WarningReason kDowngradeWarnings[] = {
184       WARN_STRICT_LAX_DOWNGRADE_STRICT_SAMESITE,
185       WARN_STRICT_CROSS_DOWNGRADE_STRICT_SAMESITE,
186       WARN_STRICT_CROSS_DOWNGRADE_LAX_SAMESITE,
187       WARN_LAX_CROSS_DOWNGRADE_STRICT_SAMESITE,
188       WARN_LAX_CROSS_DOWNGRADE_LAX_SAMESITE,
189   };
190 
191   for (auto warning : kDowngradeWarnings) {
192     if (!HasWarningReason(warning))
193       continue;
194 
195     if (reason)
196       *reason = warning;
197 
198     return true;
199   }
200 
201   return false;
202 }
203 
AddWarningReason(WarningReason reason)204 void CookieInclusionStatus::AddWarningReason(WarningReason reason) {
205   warning_reasons_[reason] = true;
206 }
207 
RemoveWarningReason(WarningReason reason)208 void CookieInclusionStatus::RemoveWarningReason(WarningReason reason) {
209   warning_reasons_[reason] = false;
210 }
211 
212 CookieInclusionStatus::ContextDowngradeMetricValues
GetBreakingDowngradeMetricsEnumValue(const GURL & url) const213 CookieInclusionStatus::GetBreakingDowngradeMetricsEnumValue(
214     const GURL& url) const {
215   bool url_is_secure = url.SchemeIsCryptographic();
216 
217   // Start the |reason| as something other than the downgrade warnings.
218   WarningReason reason = WarningReason::NUM_WARNING_REASONS;
219 
220   // Don't bother checking the return value because the default switch case
221   // will handle if no reason was found.
222   HasSchemefulDowngradeWarning(&reason);
223 
224   switch (reason) {
225     case WarningReason::WARN_STRICT_LAX_DOWNGRADE_STRICT_SAMESITE:
226       return url_is_secure
227                  ? ContextDowngradeMetricValues::kStrictLaxStrictSecure
228                  : ContextDowngradeMetricValues::kStrictLaxStrictInsecure;
229     case WarningReason::WARN_STRICT_CROSS_DOWNGRADE_STRICT_SAMESITE:
230       return url_is_secure
231                  ? ContextDowngradeMetricValues::kStrictCrossStrictSecure
232                  : ContextDowngradeMetricValues::kStrictCrossStrictInsecure;
233     case WarningReason::WARN_STRICT_CROSS_DOWNGRADE_LAX_SAMESITE:
234       return url_is_secure
235                  ? ContextDowngradeMetricValues::kStrictCrossLaxSecure
236                  : ContextDowngradeMetricValues::kStrictCrossLaxInsecure;
237     case WarningReason::WARN_LAX_CROSS_DOWNGRADE_STRICT_SAMESITE:
238       return url_is_secure
239                  ? ContextDowngradeMetricValues::kLaxCrossStrictSecure
240                  : ContextDowngradeMetricValues::kLaxCrossStrictInsecure;
241     case WarningReason::WARN_LAX_CROSS_DOWNGRADE_LAX_SAMESITE:
242       return url_is_secure ? ContextDowngradeMetricValues::kLaxCrossLaxSecure
243                            : ContextDowngradeMetricValues::kLaxCrossLaxInsecure;
244     default:
245       return url_is_secure ? ContextDowngradeMetricValues::kNoDowngradeSecure
246                            : ContextDowngradeMetricValues::kNoDowngradeInsecure;
247   }
248 }
249 
GetDebugString() const250 std::string CookieInclusionStatus::GetDebugString() const {
251   std::string out;
252 
253   if (IsInclude())
254     base::StrAppend(&out, {"INCLUDE, "});
255 
256   constexpr std::pair<ExclusionReason, const char*> exclusion_reasons[] = {
257       {EXCLUDE_UNKNOWN_ERROR, "EXCLUDE_UNKNOWN_ERROR"},
258       {EXCLUDE_HTTP_ONLY, "EXCLUDE_HTTP_ONLY"},
259       {EXCLUDE_SECURE_ONLY, "EXCLUDE_SECURE_ONLY"},
260       {EXCLUDE_DOMAIN_MISMATCH, "EXCLUDE_DOMAIN_MISMATCH"},
261       {EXCLUDE_NOT_ON_PATH, "EXCLUDE_NOT_ON_PATH"},
262       {EXCLUDE_SAMESITE_STRICT, "EXCLUDE_SAMESITE_STRICT"},
263       {EXCLUDE_SAMESITE_LAX, "EXCLUDE_SAMESITE_LAX"},
264       {EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX,
265        "EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX"},
266       {EXCLUDE_SAMESITE_NONE_INSECURE, "EXCLUDE_SAMESITE_NONE_INSECURE"},
267       {EXCLUDE_USER_PREFERENCES, "EXCLUDE_USER_PREFERENCES"},
268       {EXCLUDE_FAILURE_TO_STORE, "EXCLUDE_FAILURE_TO_STORE"},
269       {EXCLUDE_NONCOOKIEABLE_SCHEME, "EXCLUDE_NONCOOKIEABLE_SCHEME"},
270       {EXCLUDE_OVERWRITE_SECURE, "EXCLUDE_OVERWRITE_SECURE"},
271       {EXCLUDE_OVERWRITE_HTTP_ONLY, "EXCLUDE_OVERWRITE_HTTP_ONLY"},
272       {EXCLUDE_INVALID_DOMAIN, "EXCLUDE_INVALID_DOMAIN"},
273       {EXCLUDE_INVALID_PREFIX, "EXCLUDE_INVALID_PREFIX"},
274       {EXCLUDE_INVALID_PARTITIONED, "EXCLUDE_INVALID_PARTITIONED"},
275       {EXCLUDE_NAME_VALUE_PAIR_EXCEEDS_MAX_SIZE,
276        "EXCLUDE_NAME_VALUE_PAIR_EXCEEDS_MAX_SIZE"},
277       {EXCLUDE_ATTRIBUTE_VALUE_EXCEEDS_MAX_SIZE,
278        "EXCLUDE_ATTRIBUTE_VALUE_EXCEEDS_MAX_SIZE"},
279       {EXCLUDE_DOMAIN_NON_ASCII, "EXCLUDE_DOMAIN_NON_ASCII"},
280       {EXCLUDE_THIRD_PARTY_BLOCKED_WITHIN_FIRST_PARTY_SET,
281        "EXCLUDE_THIRD_PARTY_BLOCKED_WITHIN_FIRST_PARTY_SET"},
282       {EXCLUDE_PORT_MISMATCH, "EXCLUDE_PORT_MISMATCH"},
283       {EXCLUDE_SCHEME_MISMATCH, "EXCLUDE_SCHEME_MISMATCH"},
284       {EXCLUDE_SHADOWING_DOMAIN, "EXCLUDE_SHADOWING_DOMAIN"},
285       {EXCLUDE_DISALLOWED_CHARACTER, "EXCLUDE_DISALLOWED_CHARACTER"},
286       {EXCLUDE_THIRD_PARTY_PHASEOUT, "EXCLUDE_THIRD_PARTY_PHASEOUT"},
287       {EXCLUDE_NO_COOKIE_CONTENT, "EXCLUDE_NO_COOKIE_CONTENT"},
288   };
289   static_assert(
290       std::size(exclusion_reasons) == ExclusionReason::NUM_EXCLUSION_REASONS,
291       "Please ensure all ExclusionReason variants are enumerated in "
292       "GetDebugString");
293   static_assert(base::ranges::is_sorted(exclusion_reasons),
294                 "Please keep the ExclusionReason variants sorted in numerical "
295                 "order in GetDebugString");
296 
297   for (const auto& reason : exclusion_reasons) {
298     if (HasExclusionReason(reason.first))
299       base::StrAppend(&out, {reason.second, ", "});
300   }
301 
302   // Add warning
303   if (!ShouldWarn()) {
304     base::StrAppend(&out, {"DO_NOT_WARN, "});
305   }
306 
307   constexpr std::pair<WarningReason, const char*> warning_reasons[] = {
308       {WARN_SAMESITE_UNSPECIFIED_CROSS_SITE_CONTEXT,
309        "WARN_SAMESITE_UNSPECIFIED_CROSS_SITE_CONTEXT"},
310       {WARN_SAMESITE_NONE_INSECURE, "WARN_SAMESITE_NONE_INSECURE"},
311       {WARN_SAMESITE_UNSPECIFIED_LAX_ALLOW_UNSAFE,
312        "WARN_SAMESITE_UNSPECIFIED_LAX_ALLOW_UNSAFE"},
313       {WARN_STRICT_LAX_DOWNGRADE_STRICT_SAMESITE,
314        "WARN_STRICT_LAX_DOWNGRADE_STRICT_SAMESITE"},
315       {WARN_STRICT_CROSS_DOWNGRADE_STRICT_SAMESITE,
316        "WARN_STRICT_CROSS_DOWNGRADE_STRICT_SAMESITE"},
317       {WARN_STRICT_CROSS_DOWNGRADE_LAX_SAMESITE,
318        "WARN_STRICT_CROSS_DOWNGRADE_LAX_SAMESITE"},
319       {WARN_LAX_CROSS_DOWNGRADE_STRICT_SAMESITE,
320        "WARN_LAX_CROSS_DOWNGRADE_STRICT_SAMESITE"},
321       {WARN_LAX_CROSS_DOWNGRADE_LAX_SAMESITE,
322        "WARN_LAX_CROSS_DOWNGRADE_LAX_SAMESITE"},
323       {WARN_SECURE_ACCESS_GRANTED_NON_CRYPTOGRAPHIC,
324        "WARN_SECURE_ACCESS_GRANTED_NON_CRYPTOGRAPHIC"},
325       {WARN_CROSS_SITE_REDIRECT_DOWNGRADE_CHANGES_INCLUSION,
326        "WARN_CROSS_SITE_REDIRECT_DOWNGRADE_CHANGES_INCLUSION"},
327       {WARN_ATTRIBUTE_VALUE_EXCEEDS_MAX_SIZE,
328        "WARN_ATTRIBUTE_VALUE_EXCEEDS_MAX_SIZE"},
329       {WARN_DOMAIN_NON_ASCII, "WARN_DOMAIN_NON_ASCII"},
330       {WARN_PORT_MISMATCH, "WARN_PORT_MISMATCH"},
331       {WARN_SCHEME_MISMATCH, "WARN_SCHEME_MISMATCH"},
332       {WARN_TENTATIVELY_ALLOWING_SECURE_SOURCE_SCHEME,
333        "WARN_TENTATIVELY_ALLOWING_SECURE_SOURCE_SCHEME"},
334       {WARN_SHADOWING_DOMAIN, "WARN_SHADOWING_DOMAIN"},
335       {WARN_THIRD_PARTY_PHASEOUT, "WARN_THIRD_PARTY_PHASEOUT"},
336   };
337   static_assert(
338       std::size(warning_reasons) == WarningReason::NUM_WARNING_REASONS,
339       "Please ensure all WarningReason variants are enumerated in "
340       "GetDebugString");
341   static_assert(base::ranges::is_sorted(warning_reasons),
342                 "Please keep the WarningReason variants sorted in numerical "
343                 "order in GetDebugString");
344 
345   for (const auto& reason : warning_reasons) {
346     if (HasWarningReason(reason.first))
347       base::StrAppend(&out, {reason.second, ", "});
348   }
349 
350   // Add exemption reason
351   if (exemption_reason() == CookieInclusionStatus::ExemptionReason::kNone) {
352     base::StrAppend(&out, {"NO_EXEMPTION"});
353     return out;
354   }
355 
356   std::string_view reason;
357   switch (exemption_reason()) {
358     case ExemptionReason::kUserSetting:
359       reason = "ExemptionUserSetting";
360       break;
361     case ExemptionReason::k3PCDMetadata:
362       reason = "Exemption3PCDMetadata";
363       break;
364     case ExemptionReason::k3PCDDeprecationTrial:
365       reason = "Exemption3PCDDeprecationTrial";
366       break;
367     case ExemptionReason::k3PCDHeuristics:
368       reason = "Exemption3PCDHeuristics";
369       break;
370     case ExemptionReason::kEnterprisePolicy:
371       reason = "ExemptionEnterprisePolicy";
372       break;
373     case ExemptionReason::kStorageAccess:
374       reason = "ExemptionStorageAccess";
375       break;
376     case ExemptionReason::kTopLevelStorageAccess:
377       reason = "ExemptionTopLevelStorageAccess";
378       break;
379     case ExemptionReason::kCorsOptIn:
380       reason = "ExemptionCorsOptIn";
381       break;
382     case ExemptionReason::kNone:
383       NOTREACHED_NORETURN();
384   };
385   base::StrAppend(&out, {reason});
386 
387   return out;
388 }
389 
HasExactlyExclusionReasonsForTesting(std::vector<CookieInclusionStatus::ExclusionReason> reasons) const390 bool CookieInclusionStatus::HasExactlyExclusionReasonsForTesting(
391     std::vector<CookieInclusionStatus::ExclusionReason> reasons) const {
392   CookieInclusionStatus expected = MakeFromReasonsForTesting(reasons);
393   return expected.exclusion_reasons_ == exclusion_reasons_;
394 }
395 
HasExactlyWarningReasonsForTesting(std::vector<WarningReason> reasons) const396 bool CookieInclusionStatus::HasExactlyWarningReasonsForTesting(
397     std::vector<WarningReason> reasons) const {
398   CookieInclusionStatus expected = MakeFromReasonsForTesting({}, reasons);
399   return expected.warning_reasons_ == warning_reasons_;
400 }
401 
402 // static
ValidateExclusionAndWarningFromWire(uint32_t exclusion_reasons,uint32_t warning_reasons)403 bool CookieInclusionStatus::ValidateExclusionAndWarningFromWire(
404     uint32_t exclusion_reasons,
405     uint32_t warning_reasons) {
406   uint32_t exclusion_mask =
407       static_cast<uint32_t>(~0ul << ExclusionReason::NUM_EXCLUSION_REASONS);
408   uint32_t warning_mask =
409       static_cast<uint32_t>(~0ul << WarningReason::NUM_WARNING_REASONS);
410   return (exclusion_reasons & exclusion_mask) == 0 &&
411          (warning_reasons & warning_mask) == 0;
412 }
413 
MakeFromReasonsForTesting(std::vector<ExclusionReason> exclusions,std::vector<WarningReason> warnings,ExemptionReason exemption,bool use_literal)414 CookieInclusionStatus CookieInclusionStatus::MakeFromReasonsForTesting(
415     std::vector<ExclusionReason> exclusions,
416     std::vector<WarningReason> warnings,
417     ExemptionReason exemption,
418     bool use_literal) {
419   CookieInclusionStatus literal_status(exclusions, warnings, exemption);
420   if (use_literal) {
421     return literal_status;
422   }
423   CookieInclusionStatus status;
424   for (ExclusionReason reason : exclusions) {
425     status.AddExclusionReason(reason);
426   }
427   for (WarningReason warning : warnings) {
428     status.AddWarningReason(warning);
429   }
430   status.MaybeSetExemptionReason(exemption);
431 
432   CHECK_EQ(status, literal_status);
433   return status;
434 }
435 
ExcludedByUserPreferencesOrTPCD() const436 bool CookieInclusionStatus::ExcludedByUserPreferencesOrTPCD() const {
437   if (HasOnlyExclusionReason(ExclusionReason::EXCLUDE_USER_PREFERENCES) ||
438       HasOnlyExclusionReason(ExclusionReason::EXCLUDE_THIRD_PARTY_PHASEOUT)) {
439     return true;
440   }
441   return exclusion_reasons_.count() == 2 &&
442          exclusion_reasons_[ExclusionReason::EXCLUDE_THIRD_PARTY_PHASEOUT] &&
443          exclusion_reasons_
444              [ExclusionReason::
445                   EXCLUDE_THIRD_PARTY_BLOCKED_WITHIN_FIRST_PARTY_SET];
446 }
447 
448 }  // namespace net
449