• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2024 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_base.h"
6 
7 #include "base/containers/contains.h"
8 #include "base/feature_list.h"
9 #include "base/strings/strcat.h"
10 #include "net/base/features.h"
11 #include "net/cookies/cookie_inclusion_status.h"
12 #include "net/cookies/cookie_util.h"
13 
14 namespace net {
15 
16 namespace {
17 
18 // Captures Strict -> Lax context downgrade with Strict cookie
IsBreakingStrictToLaxDowngrade(CookieOptions::SameSiteCookieContext::ContextType context,CookieOptions::SameSiteCookieContext::ContextType schemeful_context,CookieEffectiveSameSite effective_same_site,bool is_cookie_being_set)19 bool IsBreakingStrictToLaxDowngrade(
20     CookieOptions::SameSiteCookieContext::ContextType context,
21     CookieOptions::SameSiteCookieContext::ContextType schemeful_context,
22     CookieEffectiveSameSite effective_same_site,
23     bool is_cookie_being_set) {
24   if (context ==
25           CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_STRICT &&
26       schemeful_context ==
27           CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX &&
28       effective_same_site == CookieEffectiveSameSite::STRICT_MODE) {
29     // This downgrade only applies when a SameSite=Strict cookie is being sent.
30     // A Strict -> Lax downgrade will not affect a Strict cookie which is being
31     // set because it will be set in either context.
32     return !is_cookie_being_set;
33   }
34 
35   return false;
36 }
37 
38 // Captures Strict -> Cross-site context downgrade with {Strict, Lax} cookie
39 // Captures Strict -> Lax Unsafe context downgrade with {Strict, Lax} cookie.
40 // This is treated as a cross-site downgrade due to the Lax Unsafe context
41 // behaving like cross-site.
IsBreakingStrictToCrossDowngrade(CookieOptions::SameSiteCookieContext::ContextType context,CookieOptions::SameSiteCookieContext::ContextType schemeful_context,CookieEffectiveSameSite effective_same_site)42 bool IsBreakingStrictToCrossDowngrade(
43     CookieOptions::SameSiteCookieContext::ContextType context,
44     CookieOptions::SameSiteCookieContext::ContextType schemeful_context,
45     CookieEffectiveSameSite effective_same_site) {
46   bool breaking_schemeful_context =
47       schemeful_context ==
48           CookieOptions::SameSiteCookieContext::ContextType::CROSS_SITE ||
49       schemeful_context == CookieOptions::SameSiteCookieContext::ContextType::
50                                SAME_SITE_LAX_METHOD_UNSAFE;
51 
52   bool strict_lax_enforcement =
53       effective_same_site == CookieEffectiveSameSite::STRICT_MODE ||
54       effective_same_site == CookieEffectiveSameSite::LAX_MODE ||
55       // Treat LAX_MODE_ALLOW_UNSAFE the same as LAX_MODE for the purposes of
56       // our SameSite enforcement check.
57       effective_same_site == CookieEffectiveSameSite::LAX_MODE_ALLOW_UNSAFE;
58 
59   if (context ==
60           CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_STRICT &&
61       breaking_schemeful_context && strict_lax_enforcement) {
62     return true;
63   }
64 
65   return false;
66 }
67 
68 // Captures Lax -> Cross context downgrade with {Strict, Lax} cookies.
69 // Ignores Lax Unsafe context.
IsBreakingLaxToCrossDowngrade(CookieOptions::SameSiteCookieContext::ContextType context,CookieOptions::SameSiteCookieContext::ContextType schemeful_context,CookieEffectiveSameSite effective_same_site,bool is_cookie_being_set)70 bool IsBreakingLaxToCrossDowngrade(
71     CookieOptions::SameSiteCookieContext::ContextType context,
72     CookieOptions::SameSiteCookieContext::ContextType schemeful_context,
73     CookieEffectiveSameSite effective_same_site,
74     bool is_cookie_being_set) {
75   bool lax_enforcement =
76       effective_same_site == CookieEffectiveSameSite::LAX_MODE ||
77       // Treat LAX_MODE_ALLOW_UNSAFE the same as LAX_MODE for the purposes of
78       // our SameSite enforcement check.
79       effective_same_site == CookieEffectiveSameSite::LAX_MODE_ALLOW_UNSAFE;
80 
81   if (context ==
82           CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX &&
83       schemeful_context ==
84           CookieOptions::SameSiteCookieContext::ContextType::CROSS_SITE) {
85     // For SameSite=Strict cookies this downgrade only applies when it is being
86     // set. A Lax -> Cross downgrade will not affect a Strict cookie which is
87     // being sent because it wouldn't be sent in either context.
88     return effective_same_site == CookieEffectiveSameSite::STRICT_MODE
89                ? is_cookie_being_set
90                : lax_enforcement;
91   }
92 
93   return false;
94 }
95 
ApplySameSiteCookieWarningToStatus(CookieSameSite samesite,CookieEffectiveSameSite effective_samesite,bool is_secure,const CookieOptions::SameSiteCookieContext & same_site_context,CookieInclusionStatus * status,bool is_cookie_being_set)96 void ApplySameSiteCookieWarningToStatus(
97     CookieSameSite samesite,
98     CookieEffectiveSameSite effective_samesite,
99     bool is_secure,
100     const CookieOptions::SameSiteCookieContext& same_site_context,
101     CookieInclusionStatus* status,
102     bool is_cookie_being_set) {
103   if (samesite == CookieSameSite::UNSPECIFIED &&
104       same_site_context.GetContextForCookieInclusion() <
105           CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX) {
106     status->AddWarningReason(
107         CookieInclusionStatus::WARN_SAMESITE_UNSPECIFIED_CROSS_SITE_CONTEXT);
108   }
109   if (effective_samesite == CookieEffectiveSameSite::LAX_MODE_ALLOW_UNSAFE &&
110       same_site_context.GetContextForCookieInclusion() ==
111           CookieOptions::SameSiteCookieContext::ContextType::
112               SAME_SITE_LAX_METHOD_UNSAFE) {
113     // This warning is more specific so remove the previous, more general,
114     // warning.
115     status->RemoveWarningReason(
116         CookieInclusionStatus::WARN_SAMESITE_UNSPECIFIED_CROSS_SITE_CONTEXT);
117     status->AddWarningReason(
118         CookieInclusionStatus::WARN_SAMESITE_UNSPECIFIED_LAX_ALLOW_UNSAFE);
119   }
120   if (samesite == CookieSameSite::NO_RESTRICTION && !is_secure) {
121     status->AddWarningReason(
122         CookieInclusionStatus::WARN_SAMESITE_NONE_INSECURE);
123   }
124 
125   // Add a warning if the cookie would be accessible in
126   // |same_site_context|::context but not in
127   // |same_site_context|::schemeful_context.
128   if (IsBreakingStrictToLaxDowngrade(same_site_context.context(),
129                                      same_site_context.schemeful_context(),
130                                      effective_samesite, is_cookie_being_set)) {
131     status->AddWarningReason(
132         CookieInclusionStatus::WARN_STRICT_LAX_DOWNGRADE_STRICT_SAMESITE);
133   } else if (IsBreakingStrictToCrossDowngrade(
134                  same_site_context.context(),
135                  same_site_context.schemeful_context(), effective_samesite)) {
136     // Which warning to apply depends on the SameSite value.
137     if (effective_samesite == CookieEffectiveSameSite::STRICT_MODE) {
138       status->AddWarningReason(
139           CookieInclusionStatus::WARN_STRICT_CROSS_DOWNGRADE_STRICT_SAMESITE);
140     } else {
141       // LAX_MODE or LAX_MODE_ALLOW_UNSAFE.
142       status->AddWarningReason(
143           CookieInclusionStatus::WARN_STRICT_CROSS_DOWNGRADE_LAX_SAMESITE);
144     }
145 
146   } else if (IsBreakingLaxToCrossDowngrade(
147                  same_site_context.context(),
148                  same_site_context.schemeful_context(), effective_samesite,
149                  is_cookie_being_set)) {
150     // Which warning to apply depends on the SameSite value.
151     if (effective_samesite == CookieEffectiveSameSite::STRICT_MODE) {
152       status->AddWarningReason(
153           CookieInclusionStatus::WARN_LAX_CROSS_DOWNGRADE_STRICT_SAMESITE);
154     } else {
155       // LAX_MODE or LAX_MODE_ALLOW_UNSAFE.
156       // This warning applies to both set/send.
157       status->AddWarningReason(
158           CookieInclusionStatus::WARN_LAX_CROSS_DOWNGRADE_LAX_SAMESITE);
159     }
160   }
161 
162   // Apply warning for whether inclusion was changed by considering redirects
163   // for the SameSite context calculation. This does not look at the actual
164   // inclusion or exclusion, but only at whether the inclusion differs between
165   // considering redirects and not.
166   using ContextDowngradeType = CookieOptions::SameSiteCookieContext::
167       ContextMetadata::ContextDowngradeType;
168   const auto& metadata = same_site_context.GetMetadataForCurrentSchemefulMode();
169   bool apply_cross_site_redirect_downgrade_warning = false;
170   switch (effective_samesite) {
171     case CookieEffectiveSameSite::STRICT_MODE:
172       // Strict contexts are all normalized to lax for cookie writes, so a
173       // strict-to-{lax,cross} downgrade cannot occur for response cookies.
174       apply_cross_site_redirect_downgrade_warning =
175           is_cookie_being_set ? metadata.cross_site_redirect_downgrade ==
176                                     ContextDowngradeType::kLaxToCross
177                               : (metadata.cross_site_redirect_downgrade ==
178                                      ContextDowngradeType::kStrictToLax ||
179                                  metadata.cross_site_redirect_downgrade ==
180                                      ContextDowngradeType::kStrictToCross);
181       break;
182     case CookieEffectiveSameSite::LAX_MODE:
183     case CookieEffectiveSameSite::LAX_MODE_ALLOW_UNSAFE:
184       // Note that a lax-to-cross downgrade can only happen for response
185       // cookies, because a laxly same-site context only happens for a safe
186       // top-level cross-site request, which cannot be downgraded due to a
187       // cross-site redirect to a non-top-level or unsafe cross-site request.
188       apply_cross_site_redirect_downgrade_warning =
189           metadata.cross_site_redirect_downgrade ==
190           (is_cookie_being_set ? ContextDowngradeType::kLaxToCross
191                                : ContextDowngradeType::kStrictToCross);
192       break;
193     default:
194       break;
195   }
196   if (apply_cross_site_redirect_downgrade_warning) {
197     status->AddWarningReason(
198         CookieInclusionStatus::
199             WARN_CROSS_SITE_REDIRECT_DOWNGRADE_CHANGES_INCLUSION);
200   }
201 
202   // If there are reasons to exclude the cookie other than SameSite, don't warn
203   // about the cookie at all.
204   status->MaybeClearSameSiteWarning();
205 }
206 
207 }  // namespace
208 
IncludeForRequestURL(const GURL & url,const CookieOptions & options,const CookieAccessParams & params) const209 CookieAccessResult CookieBase::IncludeForRequestURL(
210     const GURL& url,
211     const CookieOptions& options,
212     const CookieAccessParams& params) const {
213   CookieInclusionStatus status;
214   // Filter out HttpOnly cookies, per options.
215   if (options.exclude_httponly() && IsHttpOnly()) {
216     status.AddExclusionReason(CookieInclusionStatus::EXCLUDE_HTTP_ONLY);
217   }
218   // Secure cookies should not be included in requests for URLs with an
219   // insecure scheme, unless it is a localhost url, or the CookieAccessDelegate
220   // otherwise denotes them as trustworthy
221   // (`delegate_treats_url_as_trustworthy`).
222   bool is_allowed_to_access_secure_cookies = false;
223   CookieAccessScheme cookie_access_scheme =
224       cookie_util::ProvisionalAccessScheme(url);
225   if (cookie_access_scheme == CookieAccessScheme::kNonCryptographic &&
226       params.delegate_treats_url_as_trustworthy) {
227     cookie_access_scheme = CookieAccessScheme::kTrustworthy;
228   }
229   switch (cookie_access_scheme) {
230     case CookieAccessScheme::kNonCryptographic:
231       if (SecureAttribute()) {
232         status.AddExclusionReason(CookieInclusionStatus::EXCLUDE_SECURE_ONLY);
233       }
234       break;
235     case CookieAccessScheme::kTrustworthy:
236       is_allowed_to_access_secure_cookies = true;
237       if (SecureAttribute() ||
238           (cookie_util::IsSchemeBoundCookiesEnabled() &&
239            source_scheme_ == CookieSourceScheme::kSecure)) {
240         status.AddWarningReason(
241             CookieInclusionStatus::
242                 WARN_SECURE_ACCESS_GRANTED_NON_CRYPTOGRAPHIC);
243       }
244       break;
245     case CookieAccessScheme::kCryptographic:
246       is_allowed_to_access_secure_cookies = true;
247       break;
248   }
249 
250   // For the following two sections we're checking to see if a cookie's
251   // `source_scheme_` and `source_port_` match that of the url's. In most cases
252   // this is a direct comparison but it does get a bit more complicated when
253   // trustworthy origins are taken into accounts. Note that here, a kTrustworthy
254   // url must have a non-secure scheme (http) because otherwise it'd be a
255   // kCryptographic url.
256   //
257   // Trustworthy origins are allowed to both secure and non-secure cookies. This
258   // means that we'll match source_scheme_ for both their usual kNonSecure as
259   // well as KSecure. For source_port_ we'll match per usual as well as any 443
260   // ports, since those are the default values for secure cookies and we still
261   // want to be able to access them.
262 
263   // A cookie with a source scheme of kSecure shouldn't be accessible by
264   // kNonCryptographic urls. But we can skip adding a status if the cookie is
265   // already blocked due to the `Secure` attribute.
266   if (source_scheme_ == CookieSourceScheme::kSecure &&
267       cookie_access_scheme == CookieAccessScheme::kNonCryptographic &&
268       !status.HasExclusionReason(CookieInclusionStatus::EXCLUDE_SECURE_ONLY)) {
269     if (cookie_util::IsSchemeBoundCookiesEnabled()) {
270       status.AddExclusionReason(CookieInclusionStatus::EXCLUDE_SCHEME_MISMATCH);
271     } else {
272       status.AddWarningReason(CookieInclusionStatus::WARN_SCHEME_MISMATCH);
273     }
274   }
275   // A cookie with a source scheme of kNonSecure shouldn't be accessible by
276   // kCryptographic urls.
277   else if (source_scheme_ == CookieSourceScheme::kNonSecure &&
278            cookie_access_scheme == CookieAccessScheme::kCryptographic) {
279     if (cookie_util::IsSchemeBoundCookiesEnabled()) {
280       status.AddExclusionReason(CookieInclusionStatus::EXCLUDE_SCHEME_MISMATCH);
281     } else {
282       status.AddWarningReason(CookieInclusionStatus::WARN_SCHEME_MISMATCH);
283     }
284   }
285   // Else, the cookie has a source scheme of kUnset or the access scheme is
286   // kTrustworthy. Neither of which will block the cookie.
287 
288   int url_port = url.EffectiveIntPort();
289   CHECK(url_port != url::PORT_INVALID);
290   // The cookie's source port either must match the url's port, be
291   // PORT_UNSPECIFIED, or the cookie must be a domain cookie.
292   bool port_matches = url_port == source_port_ ||
293                       source_port_ == url::PORT_UNSPECIFIED || IsDomainCookie();
294 
295   // Or if the url is trustworthy, we'll also match 443 (in order to get secure
296   // cookies).
297   bool trustworthy_and_443 =
298       cookie_access_scheme == CookieAccessScheme::kTrustworthy &&
299       source_port_ == 443;
300   if (!port_matches && !trustworthy_and_443) {
301     if (cookie_util::IsPortBoundCookiesEnabled()) {
302       status.AddExclusionReason(CookieInclusionStatus::EXCLUDE_PORT_MISMATCH);
303     } else {
304       status.AddWarningReason(CookieInclusionStatus::WARN_PORT_MISMATCH);
305     }
306   }
307 
308   // Don't include cookies for requests that don't apply to the cookie domain.
309   if (!IsDomainMatch(url.host())) {
310     status.AddExclusionReason(CookieInclusionStatus::EXCLUDE_DOMAIN_MISMATCH);
311   }
312   // Don't include cookies for requests with a url path that does not path
313   // match the cookie-path.
314   if (!IsOnPath(url.path())) {
315     status.AddExclusionReason(CookieInclusionStatus::EXCLUDE_NOT_ON_PATH);
316   }
317 
318   // For LEGACY cookies we should always return the schemeless context,
319   // otherwise let GetContextForCookieInclusion() decide.
320   const CookieOptions::SameSiteCookieContext::ContextType
321       cookie_inclusion_context =
322           params.access_semantics == CookieAccessSemantics::LEGACY
323               ? options.same_site_cookie_context().context()
324               : options.same_site_cookie_context()
325                     .GetContextForCookieInclusion();
326 
327   // Don't include same-site cookies for cross-site requests.
328   CookieEffectiveSameSite effective_same_site =
329       GetEffectiveSameSite(params.access_semantics);
330   DCHECK(effective_same_site != CookieEffectiveSameSite::UNDEFINED);
331 
332   switch (effective_same_site) {
333     case CookieEffectiveSameSite::STRICT_MODE:
334       if (cookie_inclusion_context <
335           CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_STRICT) {
336         status.AddExclusionReason(
337             CookieInclusionStatus::EXCLUDE_SAMESITE_STRICT);
338       }
339       break;
340     case CookieEffectiveSameSite::LAX_MODE:
341       if (cookie_inclusion_context <
342           CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX) {
343         status.AddExclusionReason(
344             (SameSite() == CookieSameSite::UNSPECIFIED)
345                 ? CookieInclusionStatus::
346                       EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX
347                 : CookieInclusionStatus::EXCLUDE_SAMESITE_LAX);
348       }
349       break;
350     // TODO(crbug.com/40638805): Add a browsertest for this behavior.
351     case CookieEffectiveSameSite::LAX_MODE_ALLOW_UNSAFE:
352       DCHECK(SameSite() == CookieSameSite::UNSPECIFIED);
353       if (cookie_inclusion_context <
354           CookieOptions::SameSiteCookieContext::ContextType::
355               SAME_SITE_LAX_METHOD_UNSAFE) {
356         // TODO(chlily): Do we need a separate CookieInclusionStatus for this?
357         status.AddExclusionReason(
358             CookieInclusionStatus::EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX);
359       }
360       break;
361     default:
362       break;
363   }
364 
365   // Unless legacy access semantics are in effect, SameSite=None cookies without
366   // the Secure attribute should be ignored. This can apply to cookies which
367   // were created before "SameSite=None requires Secure" was enabled (as
368   // SameSite=None insecure cookies cannot be set while the options are on).
369   if (params.access_semantics != CookieAccessSemantics::LEGACY &&
370       SameSite() == CookieSameSite::NO_RESTRICTION && !SecureAttribute()) {
371     status.AddExclusionReason(
372         CookieInclusionStatus::EXCLUDE_SAMESITE_NONE_INSECURE);
373   }
374 
375   ApplySameSiteCookieWarningToStatus(SameSite(), effective_same_site,
376                                      SecureAttribute(),
377                                      options.same_site_cookie_context(),
378                                      &status, false /* is_cookie_being_set */);
379 
380   CookieAccessResult result{effective_same_site, status,
381                             params.access_semantics,
382                             is_allowed_to_access_secure_cookies};
383 
384   PostIncludeForRequestURL(result, options, cookie_inclusion_context);
385 
386   return result;
387 }
388 
IsSetPermittedInContext(const GURL & source_url,const CookieOptions & options,const CookieAccessParams & params,const std::vector<std::string> & cookieable_schemes,const std::optional<CookieAccessResult> & cookie_access_result) const389 CookieAccessResult CookieBase::IsSetPermittedInContext(
390     const GURL& source_url,
391     const CookieOptions& options,
392     const CookieAccessParams& params,
393     const std::vector<std::string>& cookieable_schemes,
394     const std::optional<CookieAccessResult>& cookie_access_result) const {
395   CookieAccessResult access_result;
396   if (cookie_access_result) {
397     access_result = *cookie_access_result;
398   }
399 
400   if (!base::Contains(cookieable_schemes, source_url.scheme())) {
401     access_result.status.AddExclusionReason(
402         CookieInclusionStatus::EXCLUDE_NONCOOKIEABLE_SCHEME);
403   }
404 
405   if (!IsDomainMatch(source_url.host())) {
406     access_result.status.AddExclusionReason(
407         CookieInclusionStatus::EXCLUDE_DOMAIN_MISMATCH);
408   }
409 
410   CookieAccessScheme access_scheme =
411       cookie_util::ProvisionalAccessScheme(source_url);
412   if (access_scheme == CookieAccessScheme::kNonCryptographic &&
413       params.delegate_treats_url_as_trustworthy) {
414     access_scheme = CookieAccessScheme::kTrustworthy;
415   }
416 
417   switch (access_scheme) {
418     case CookieAccessScheme::kNonCryptographic:
419       access_result.is_allowed_to_access_secure_cookies = false;
420       if (SecureAttribute()) {
421         access_result.status.AddExclusionReason(
422             CookieInclusionStatus::EXCLUDE_SECURE_ONLY);
423       }
424       break;
425 
426     case CookieAccessScheme::kCryptographic:
427       // All cool!
428       access_result.is_allowed_to_access_secure_cookies = true;
429       break;
430 
431     case CookieAccessScheme::kTrustworthy:
432       access_result.is_allowed_to_access_secure_cookies = true;
433       if (SecureAttribute()) {
434         // OK, but want people aware of this.
435         // Note, we also want to apply this warning to cookies whose source
436         // scheme is kSecure but are set by non-cryptographic (but trustworthy)
437         // urls. Helpfully, since those cookies only get a kSecure source scheme
438         // when they also specify "Secure" this if statement will already apply
439         // to them.
440         access_result.status.AddWarningReason(
441             CookieInclusionStatus::
442                 WARN_SECURE_ACCESS_GRANTED_NON_CRYPTOGRAPHIC);
443       }
444       break;
445   }
446 
447   access_result.access_semantics = params.access_semantics;
448   if (options.exclude_httponly() && IsHttpOnly()) {
449     DVLOG(net::cookie_util::kVlogSetCookies)
450         << "HttpOnly cookie not permitted in script context.";
451     access_result.status.AddExclusionReason(
452         CookieInclusionStatus::EXCLUDE_HTTP_ONLY);
453   }
454 
455   // Unless legacy access semantics are in effect, SameSite=None cookies without
456   // the Secure attribute will be rejected.
457   if (params.access_semantics != CookieAccessSemantics::LEGACY &&
458       SameSite() == CookieSameSite::NO_RESTRICTION && !SecureAttribute()) {
459     DVLOG(net::cookie_util::kVlogSetCookies)
460         << "SetCookie() rejecting insecure cookie with SameSite=None.";
461     access_result.status.AddExclusionReason(
462         CookieInclusionStatus::EXCLUDE_SAMESITE_NONE_INSECURE);
463   }
464 
465   // For LEGACY cookies we should always return the schemeless context,
466   // otherwise let GetContextForCookieInclusion() decide.
467   CookieOptions::SameSiteCookieContext::ContextType cookie_inclusion_context =
468       params.access_semantics == CookieAccessSemantics::LEGACY
469           ? options.same_site_cookie_context().context()
470           : options.same_site_cookie_context().GetContextForCookieInclusion();
471 
472   access_result.effective_same_site =
473       GetEffectiveSameSite(params.access_semantics);
474   DCHECK(access_result.effective_same_site !=
475          CookieEffectiveSameSite::UNDEFINED);
476   switch (access_result.effective_same_site) {
477     case CookieEffectiveSameSite::STRICT_MODE:
478       // This intentionally checks for `< SAME_SITE_LAX`, as we allow
479       // `SameSite=Strict` cookies to be set for top-level navigations that
480       // qualify for receipt of `SameSite=Lax` cookies.
481       if (cookie_inclusion_context <
482           CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX) {
483         DVLOG(net::cookie_util::kVlogSetCookies)
484             << "Trying to set a `SameSite=Strict` cookie from a "
485                "cross-site URL.";
486         access_result.status.AddExclusionReason(
487             CookieInclusionStatus::EXCLUDE_SAMESITE_STRICT);
488       }
489       break;
490     case CookieEffectiveSameSite::LAX_MODE:
491     case CookieEffectiveSameSite::LAX_MODE_ALLOW_UNSAFE:
492       if (cookie_inclusion_context <
493           CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX) {
494         if (SameSite() == CookieSameSite::UNSPECIFIED) {
495           DVLOG(net::cookie_util::kVlogSetCookies)
496               << "Cookies with no known SameSite attribute being treated as "
497                  "lax; attempt to set from a cross-site URL denied.";
498           access_result.status.AddExclusionReason(
499               CookieInclusionStatus::
500                   EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX);
501         } else {
502           DVLOG(net::cookie_util::kVlogSetCookies)
503               << "Trying to set a `SameSite=Lax` cookie from a cross-site URL.";
504           access_result.status.AddExclusionReason(
505               CookieInclusionStatus::EXCLUDE_SAMESITE_LAX);
506         }
507       }
508       break;
509     default:
510       break;
511   }
512 
513   ApplySameSiteCookieWarningToStatus(
514       SameSite(), access_result.effective_same_site, SecureAttribute(),
515       options.same_site_cookie_context(), &access_result.status,
516       true /* is_cookie_being_set */);
517 
518   PostIsSetPermittedInContext(access_result, options);
519 
520   return access_result;
521 }
522 
IsOnPath(const std::string & url_path) const523 bool CookieBase::IsOnPath(const std::string& url_path) const {
524   return cookie_util::IsOnPath(path_, url_path);
525 }
526 
IsDomainMatch(const std::string & host) const527 bool CookieBase::IsDomainMatch(const std::string& host) const {
528   return cookie_util::IsDomainMatch(domain_, host);
529 }
530 
IsSecure() const531 bool CookieBase::IsSecure() const {
532   return SecureAttribute() || (cookie_util::IsSchemeBoundCookiesEnabled() &&
533                                source_scheme_ == CookieSourceScheme::kSecure);
534 }
535 
IsFirstPartyPartitioned() const536 bool CookieBase::IsFirstPartyPartitioned() const {
537   return IsPartitioned() && !CookiePartitionKey::HasNonce(partition_key_) &&
538          SchemefulSite(GURL(
539              base::StrCat({url::kHttpsScheme, url::kStandardSchemeSeparator,
540                            DomainWithoutDot()}))) == partition_key_->site();
541 }
542 
IsThirdPartyPartitioned() const543 bool CookieBase::IsThirdPartyPartitioned() const {
544   return IsPartitioned() && !IsFirstPartyPartitioned();
545 }
546 
DomainWithoutDot() const547 std::string CookieBase::DomainWithoutDot() const {
548   return cookie_util::CookieDomainAsHost(domain_);
549 }
550 
UniqueKey() const551 CookieBase::UniqueCookieKey CookieBase::UniqueKey() const {
552   std::optional<CookieSourceScheme> source_scheme =
553       cookie_util::IsSchemeBoundCookiesEnabled()
554           ? std::make_optional(source_scheme_)
555           : std::nullopt;
556   std::optional<int> source_port = cookie_util::IsPortBoundCookiesEnabled()
557                                        ? std::make_optional(source_port_)
558                                        : std::nullopt;
559 
560   return std::make_tuple(partition_key_, name_, domain_, path_, source_scheme,
561                          source_port);
562 }
563 
UniqueDomainKey() const564 CookieBase::UniqueDomainCookieKey CookieBase::UniqueDomainKey() const {
565   std::optional<CookieSourceScheme> source_scheme =
566       cookie_util::IsSchemeBoundCookiesEnabled()
567           ? std::make_optional(source_scheme_)
568           : std::nullopt;
569 
570   return std::make_tuple(partition_key_, name_, domain_, path_, source_scheme);
571 }
572 
SetSourcePort(int port)573 void CookieBase::SetSourcePort(int port) {
574   source_port_ = ValidateAndAdjustSourcePort(port);
575 }
576 
577 CookieBase::CookieBase() = default;
578 
579 CookieBase::CookieBase(const CookieBase& other) = default;
580 
581 CookieBase::CookieBase(CookieBase&& other) = default;
582 
583 CookieBase& CookieBase::operator=(const CookieBase& other) = default;
584 
585 CookieBase& CookieBase::operator=(CookieBase&& other) = default;
586 
587 CookieBase::~CookieBase() = default;
588 
CookieBase(std::string name,std::string domain,std::string path,base::Time creation,bool secure,bool httponly,CookieSameSite same_site,std::optional<CookiePartitionKey> partition_key,CookieSourceScheme source_scheme,int source_port)589 CookieBase::CookieBase(std::string name,
590                        std::string domain,
591                        std::string path,
592                        base::Time creation,
593                        bool secure,
594                        bool httponly,
595                        CookieSameSite same_site,
596                        std::optional<CookiePartitionKey> partition_key,
597                        CookieSourceScheme source_scheme,
598                        int source_port)
599     : name_(std::move(name)),
600       domain_(std::move(domain)),
601       path_(std::move(path)),
602       creation_date_(creation),
603       secure_(secure),
604       httponly_(httponly),
605       same_site_(same_site),
606       partition_key_(std::move(partition_key)),
607       source_scheme_(source_scheme),
608       source_port_(source_port) {}
609 
GetEffectiveSameSite(CookieAccessSemantics access_semantics) const610 CookieEffectiveSameSite CookieBase::GetEffectiveSameSite(
611     CookieAccessSemantics access_semantics) const {
612   base::TimeDelta lax_allow_unsafe_threshold_age =
613       GetLaxAllowUnsafeThresholdAge();
614 
615   switch (SameSite()) {
616     // If a cookie does not have a SameSite attribute, the effective SameSite
617     // mode depends on the access semantics and whether the cookie is
618     // recently-created.
619     case CookieSameSite::UNSPECIFIED:
620       return (access_semantics == CookieAccessSemantics::LEGACY)
621                  ? CookieEffectiveSameSite::NO_RESTRICTION
622                  : (IsRecentlyCreated(lax_allow_unsafe_threshold_age)
623                         ? CookieEffectiveSameSite::LAX_MODE_ALLOW_UNSAFE
624                         : CookieEffectiveSameSite::LAX_MODE);
625     case CookieSameSite::NO_RESTRICTION:
626       return CookieEffectiveSameSite::NO_RESTRICTION;
627     case CookieSameSite::LAX_MODE:
628       return CookieEffectiveSameSite::LAX_MODE;
629     case CookieSameSite::STRICT_MODE:
630       return CookieEffectiveSameSite::STRICT_MODE;
631   }
632 }
633 
GetLaxAllowUnsafeThresholdAge() const634 base::TimeDelta CookieBase::GetLaxAllowUnsafeThresholdAge() const {
635   return base::TimeDelta::Min();
636 }
637 
IsRecentlyCreated(base::TimeDelta age_threshold) const638 bool CookieBase::IsRecentlyCreated(base::TimeDelta age_threshold) const {
639   return (base::Time::Now() - creation_date_) <= age_threshold;
640 }
641 
642 // static
ValidateAndAdjustSourcePort(int port)643 int CookieBase::ValidateAndAdjustSourcePort(int port) {
644   if ((port >= 0 && port <= 65535) || port == url::PORT_UNSPECIFIED) {
645     // 0 would be really weird as it has a special meaning, but it's still
646     // technically a valid tcp/ip port so we're going to accept it here.
647     return port;
648   }
649   return url::PORT_INVALID;
650 }
651 
652 }  // namespace net
653