• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 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 #ifdef UNSAFE_BUFFERS_BUILD
6 // TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
7 #pragma allow_unsafe_buffers
8 #endif
9 
10 #include "net/cookies/cookie_util.h"
11 
12 #include <cstdio>
13 #include <cstdlib>
14 #include <string>
15 #include <string_view>
16 #include <utility>
17 
18 #include "base/check.h"
19 #include "base/command_line.h"
20 #include "base/feature_list.h"
21 #include "base/functional/bind.h"
22 #include "base/functional/callback.h"
23 #include "base/metrics/histogram_functions.h"
24 #include "base/metrics/histogram_macros.h"
25 #include "base/notreached.h"
26 #include "base/strings/strcat.h"
27 #include "base/strings/string_tokenizer.h"
28 #include "base/strings/string_util.h"
29 #include "base/types/optional_ref.h"
30 #include "base/types/optional_util.h"
31 #include "build/build_config.h"
32 #include "net/base/features.h"
33 #include "net/base/isolation_info.h"
34 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
35 #include "net/base/schemeful_site.h"
36 #include "net/base/url_util.h"
37 #include "net/cookies/cookie_access_delegate.h"
38 #include "net/cookies/cookie_constants.h"
39 #include "net/cookies/cookie_inclusion_status.h"
40 #include "net/cookies/cookie_monster.h"
41 #include "net/cookies/cookie_options.h"
42 #include "net/cookies/cookie_setting_override.h"
43 #include "net/cookies/cookie_switches.h"
44 #include "net/cookies/parsed_cookie.h"
45 #include "net/first_party_sets/first_party_set_metadata.h"
46 #include "net/first_party_sets/first_party_sets_cache_filter.h"
47 #include "net/http/http_util.h"
48 #include "net/storage_access_api/status.h"
49 #include "url/gurl.h"
50 #include "url/url_constants.h"
51 
52 namespace net::cookie_util {
53 
54 namespace {
55 
56 using ContextType = CookieOptions::SameSiteCookieContext::ContextType;
57 using ContextMetadata = CookieOptions::SameSiteCookieContext::ContextMetadata;
58 
MinNonNullTime()59 base::Time MinNonNullTime() {
60   return base::Time::FromInternalValue(1);
61 }
62 
63 // Tries to assemble a base::Time given a base::Time::Exploded representing a
64 // UTC calendar date.
65 //
66 // If the date falls outside of the range supported internally by
67 // FromUTCExploded() on the current platform, then the result is:
68 //
69 // * Time(1) if it's below the range FromUTCExploded() supports.
70 // * Time::Max() if it's above the range FromUTCExploded() supports.
SaturatedTimeFromUTCExploded(const base::Time::Exploded & exploded,base::Time * out)71 bool SaturatedTimeFromUTCExploded(const base::Time::Exploded& exploded,
72                                   base::Time* out) {
73   // Try to calculate the base::Time in the normal fashion.
74   if (base::Time::FromUTCExploded(exploded, out)) {
75     // Don't return Time(0) on success.
76     if (out->is_null())
77       *out = MinNonNullTime();
78     return true;
79   }
80 
81   // base::Time::FromUTCExploded() has platform-specific limits:
82   //
83   // * Windows: Years 1601 - 30827
84   // * 32-bit POSIX: Years 1970 - 2038
85   //
86   // Work around this by returning min/max valid times for times outside those
87   // ranges when imploding the time is doomed to fail.
88   //
89   // Note that the following implementation is NOT perfect. It will accept
90   // some invalid calendar dates in the out-of-range case.
91   if (!exploded.HasValidValues())
92     return false;
93 
94   if (exploded.year > base::Time::kExplodedMaxYear) {
95     *out = base::Time::Max();
96     return true;
97   }
98   if (exploded.year < base::Time::kExplodedMinYear) {
99     *out = MinNonNullTime();
100     return true;
101   }
102 
103   return false;
104 }
105 
106 // Tests that a cookie has the attributes for a valid __Host- prefix without
107 // testing that the prefix is in the cookie name.
HasValidHostPrefixAttributes(const GURL & url,bool secure,const std::string & domain,const std::string & path)108 bool HasValidHostPrefixAttributes(const GURL& url,
109                                   bool secure,
110                                   const std::string& domain,
111                                   const std::string& path) {
112   if (!secure || !url.SchemeIsCryptographic() || path != "/") {
113     return false;
114   }
115   return domain.empty() || (url.HostIsIPAddress() && url.host() == domain);
116 }
117 
118 struct ComputeSameSiteContextResult {
119   ContextType context_type = ContextType::CROSS_SITE;
120   ContextMetadata metadata;
121 };
122 
MakeSameSiteCookieContext(const ComputeSameSiteContextResult & result,const ComputeSameSiteContextResult & schemeful_result)123 CookieOptions::SameSiteCookieContext MakeSameSiteCookieContext(
124     const ComputeSameSiteContextResult& result,
125     const ComputeSameSiteContextResult& schemeful_result) {
126   return CookieOptions::SameSiteCookieContext(
127       result.context_type, schemeful_result.context_type, result.metadata,
128       schemeful_result.metadata);
129 }
130 
131 ContextMetadata::ContextRedirectTypeBug1221316
ComputeContextRedirectTypeBug1221316(bool url_chain_is_length_one,bool same_site_initiator,bool site_for_cookies_is_same_site,bool same_site_redirect_chain)132 ComputeContextRedirectTypeBug1221316(bool url_chain_is_length_one,
133                                      bool same_site_initiator,
134                                      bool site_for_cookies_is_same_site,
135                                      bool same_site_redirect_chain) {
136   if (url_chain_is_length_one)
137     return ContextMetadata::ContextRedirectTypeBug1221316::kNoRedirect;
138 
139   if (!same_site_initiator || !site_for_cookies_is_same_site)
140     return ContextMetadata::ContextRedirectTypeBug1221316::kCrossSiteRedirect;
141 
142   if (!same_site_redirect_chain) {
143     return ContextMetadata::ContextRedirectTypeBug1221316::
144         kPartialSameSiteRedirect;
145   }
146 
147   return ContextMetadata::ContextRedirectTypeBug1221316::kAllSameSiteRedirect;
148 }
149 
150 // This function consolidates the common logic for computing SameSite cookie
151 // access context in various situations (HTTP vs JS; get vs set).
152 //
153 // `is_http` is whether the current cookie access request is associated with a
154 // network request (as opposed to a non-HTTP API, i.e., JavaScript).
155 //
156 // `compute_schemefully` is whether the current computation is for a
157 // schemeful_context, i.e. whether scheme should be considered when comparing
158 // two sites.
159 //
160 // See documentation of `ComputeSameSiteContextForRequest` for explanations of
161 // other parameters.
ComputeSameSiteContext(const std::vector<GURL> & url_chain,const SiteForCookies & site_for_cookies,const std::optional<url::Origin> & initiator,bool is_http,bool is_main_frame_navigation,bool compute_schemefully)162 ComputeSameSiteContextResult ComputeSameSiteContext(
163     const std::vector<GURL>& url_chain,
164     const SiteForCookies& site_for_cookies,
165     const std::optional<url::Origin>& initiator,
166     bool is_http,
167     bool is_main_frame_navigation,
168     bool compute_schemefully) {
169   DCHECK(!url_chain.empty());
170   const GURL& request_url = url_chain.back();
171   const auto is_same_site_with_site_for_cookies =
172       [&site_for_cookies, compute_schemefully](const GURL& url) {
173         return site_for_cookies.IsFirstPartyWithSchemefulMode(
174             url, compute_schemefully);
175       };
176 
177   bool site_for_cookies_is_same_site =
178       is_same_site_with_site_for_cookies(request_url);
179 
180   // If the request is a main frame navigation, site_for_cookies must either be
181   // null (for opaque origins, e.g., data: origins) or same-site with the
182   // request URL (both schemefully and schemelessly), and the URL cannot be
183   // ws/wss (these schemes are not navigable).
184   DCHECK(!is_main_frame_navigation || site_for_cookies_is_same_site ||
185          site_for_cookies.IsNull());
186   DCHECK(!is_main_frame_navigation || !request_url.SchemeIsWSOrWSS());
187 
188   // Defaults to a cross-site context type.
189   ComputeSameSiteContextResult result;
190 
191   // Create a SiteForCookies object from the initiator so that we can reuse
192   // IsFirstPartyWithSchemefulMode().
193   bool same_site_initiator =
194       !initiator ||
195       SiteForCookies::FromOrigin(initiator.value())
196           .IsFirstPartyWithSchemefulMode(request_url, compute_schemefully);
197 
198   // Check that the URLs in the redirect chain are all same-site with the
199   // site_for_cookies and hence (by transitivity) same-site with the request
200   // URL. (If the URL chain only has one member, it's the request_url and we've
201   // already checked it previously.)
202   bool same_site_redirect_chain =
203       url_chain.size() == 1u ||
204       base::ranges::all_of(url_chain, is_same_site_with_site_for_cookies);
205 
206   // Record what type of redirect was experienced.
207 
208   result.metadata.redirect_type_bug_1221316 =
209       ComputeContextRedirectTypeBug1221316(
210           url_chain.size() == 1u, same_site_initiator,
211           site_for_cookies_is_same_site, same_site_redirect_chain);
212 
213   if (!site_for_cookies_is_same_site)
214     return result;
215 
216   // Whether the context would be SAME_SITE_STRICT if not considering redirect
217   // chains, but is different after considering redirect chains.
218   bool cross_site_redirect_downgraded_from_strict = false;
219   // Allows the kCookieSameSiteConsidersRedirectChain feature to override the
220   // result and use SAME_SITE_STRICT.
221   bool use_strict = false;
222 
223   if (same_site_initiator) {
224     if (same_site_redirect_chain) {
225       result.context_type = ContextType::SAME_SITE_STRICT;
226       return result;
227     }
228     cross_site_redirect_downgraded_from_strict = true;
229     // If we are not supposed to consider redirect chains, record that the
230     // context result should ultimately be strictly same-site. We cannot
231     // just return early from here because we don't yet know what the context
232     // gets downgraded to, so we can't return with the correct metadata until we
233     // go through the rest of the logic below to determine that.
234     use_strict = !base::FeatureList::IsEnabled(
235         features::kCookieSameSiteConsidersRedirectChain);
236   }
237 
238   if (!is_http || is_main_frame_navigation) {
239     if (cross_site_redirect_downgraded_from_strict) {
240       result.metadata.cross_site_redirect_downgrade =
241           ContextMetadata::ContextDowngradeType::kStrictToLax;
242     }
243     result.context_type =
244         use_strict ? ContextType::SAME_SITE_STRICT : ContextType::SAME_SITE_LAX;
245     return result;
246   }
247 
248   if (cross_site_redirect_downgraded_from_strict) {
249     result.metadata.cross_site_redirect_downgrade =
250         ContextMetadata::ContextDowngradeType::kStrictToCross;
251   }
252   result.context_type =
253       use_strict ? ContextType::SAME_SITE_STRICT : ContextType::CROSS_SITE;
254 
255   return result;
256 }
257 
258 // Setting any SameSite={Strict,Lax} cookie only requires a LAX context, so
259 // normalize any strictly same-site contexts to Lax for cookie writes.
NormalizeStrictToLaxForSet(ComputeSameSiteContextResult & result)260 void NormalizeStrictToLaxForSet(ComputeSameSiteContextResult& result) {
261   if (result.context_type == ContextType::SAME_SITE_STRICT)
262     result.context_type = ContextType::SAME_SITE_LAX;
263 
264   switch (result.metadata.cross_site_redirect_downgrade) {
265     case ContextMetadata::ContextDowngradeType::kStrictToLax:
266       result.metadata.cross_site_redirect_downgrade =
267           ContextMetadata::ContextDowngradeType::kNoDowngrade;
268       break;
269     case ContextMetadata::ContextDowngradeType::kStrictToCross:
270       result.metadata.cross_site_redirect_downgrade =
271           ContextMetadata::ContextDowngradeType::kLaxToCross;
272       break;
273     default:
274       break;
275   }
276 }
277 
ComputeSameSiteContextForSet(const std::vector<GURL> & url_chain,const SiteForCookies & site_for_cookies,const std::optional<url::Origin> & initiator,bool is_http,bool is_main_frame_navigation)278 CookieOptions::SameSiteCookieContext ComputeSameSiteContextForSet(
279     const std::vector<GURL>& url_chain,
280     const SiteForCookies& site_for_cookies,
281     const std::optional<url::Origin>& initiator,
282     bool is_http,
283     bool is_main_frame_navigation) {
284   CookieOptions::SameSiteCookieContext same_site_context;
285 
286   ComputeSameSiteContextResult result = ComputeSameSiteContext(
287       url_chain, site_for_cookies, initiator, is_http, is_main_frame_navigation,
288       false /* compute_schemefully */);
289   ComputeSameSiteContextResult schemeful_result = ComputeSameSiteContext(
290       url_chain, site_for_cookies, initiator, is_http, is_main_frame_navigation,
291       true /* compute_schemefully */);
292 
293   NormalizeStrictToLaxForSet(result);
294   NormalizeStrictToLaxForSet(schemeful_result);
295 
296   return MakeSameSiteCookieContext(result, schemeful_result);
297 }
298 
CookieWithAccessResultSorter(const CookieWithAccessResult & a,const CookieWithAccessResult & b)299 bool CookieWithAccessResultSorter(const CookieWithAccessResult& a,
300                                   const CookieWithAccessResult& b) {
301   return CookieMonster::CookieSorter(&a.cookie, &b.cookie);
302 }
303 
IsSameSiteIgnoringWebSocketProtocol(const url::Origin & initiator,const GURL & request_url)304 bool IsSameSiteIgnoringWebSocketProtocol(const url::Origin& initiator,
305                                          const GURL& request_url) {
306   if (initiator.IsSameOriginWith(request_url)) {
307     return true;
308   }
309   SchemefulSite request_site(
310       request_url.SchemeIsHTTPOrHTTPS()
311           ? request_url
312           : ChangeWebSocketSchemeToHttpScheme(request_url));
313   return SchemefulSite(initiator) == request_site;
314 }
315 
316 }  // namespace
317 
FireStorageAccessHistogram(StorageAccessResult result)318 void FireStorageAccessHistogram(StorageAccessResult result) {
319   UMA_HISTOGRAM_ENUMERATION("API.StorageAccess.AllowedRequests4", result);
320 }
321 
DomainIsHostOnly(const std::string & domain_string)322 bool DomainIsHostOnly(const std::string& domain_string) {
323   return (domain_string.empty() || domain_string[0] != '.');
324 }
325 
CookieDomainAsHost(const std::string & cookie_domain)326 std::string CookieDomainAsHost(const std::string& cookie_domain) {
327   if (DomainIsHostOnly(cookie_domain))
328     return cookie_domain;
329   return cookie_domain.substr(1);
330 }
331 
GetEffectiveDomain(const std::string & scheme,const std::string & host)332 std::string GetEffectiveDomain(const std::string& scheme,
333                                const std::string& host) {
334   if (scheme == "http" || scheme == "https" || scheme == "ws" ||
335       scheme == "wss") {
336     return registry_controlled_domains::GetDomainAndRegistry(
337         host,
338         registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
339   }
340 
341   return CookieDomainAsHost(host);
342 }
343 
GetCookieDomainWithString(const GURL & url,const std::string & domain_string,CookieInclusionStatus & status,std::string * result)344 bool GetCookieDomainWithString(const GURL& url,
345                                const std::string& domain_string,
346                                CookieInclusionStatus& status,
347                                std::string* result) {
348   // Disallow non-ASCII domain names.
349   if (!base::IsStringASCII(domain_string)) {
350     if (base::FeatureList::IsEnabled(features::kCookieDomainRejectNonASCII)) {
351       status.AddExclusionReason(
352           CookieInclusionStatus::EXCLUDE_DOMAIN_NON_ASCII);
353       return false;
354     }
355     status.AddWarningReason(CookieInclusionStatus::WARN_DOMAIN_NON_ASCII);
356   }
357 
358   const std::string url_host(url.host());
359 
360   // Disallow invalid hostnames containing multiple `.` at the end.
361   // Httpbis-rfc6265bis draft-11, §5.1.2 says to convert the request host "into
362   // a sequence of individual domain name labels"; a label can only be empty if
363   // it is the last label in the name, but a name ending in `..` would have an
364   // empty label in the penultimate position and is thus invalid.
365   if (url_host.ends_with("..")) {
366     return false;
367   }
368   // If no domain was specified in the domain string, default to a host cookie.
369   // We match IE/Firefox in allowing a domain=IPADDR if it matches (case
370   // in-sensitive) the url ip address hostname and ignoring a leading dot if one
371   // exists. It should be treated as a host cookie.
372   if (domain_string.empty() ||
373       (url.HostIsIPAddress() &&
374        (base::EqualsCaseInsensitiveASCII(url_host, domain_string) ||
375         base::EqualsCaseInsensitiveASCII("." + url_host, domain_string)))) {
376     if (url.SchemeIsHTTPOrHTTPS() || url.SchemeIsWSOrWSS()) {
377       *result = url_host;
378     } else {
379       // If the URL uses an unknown scheme, we should ensure the host has been
380       // canonicalized.
381       url::CanonHostInfo ignored;
382       *result = CanonicalizeHost(url_host, &ignored);
383     }
384     // TODO(crbug.com/40271909): Once empty label support is implemented we can
385     // CHECK our assumptions here. For now, we DCHECK as DUMP_WILL_BE_CHECK is
386     // generating too many crash reports and already know why this is failing.
387     DCHECK(DomainIsHostOnly(*result));
388     return true;
389   }
390 
391   // Disallow domain names with %-escaped characters.
392   for (char c : domain_string) {
393     if (c == '%')
394       return false;
395   }
396 
397   url::CanonHostInfo ignored;
398   std::string cookie_domain(CanonicalizeHost(domain_string, &ignored));
399   // Get the normalized domain specified in cookie line.
400   if (cookie_domain.empty())
401     return false;
402   if (cookie_domain[0] != '.')
403     cookie_domain = "." + cookie_domain;
404 
405   // Ensure |url| and |cookie_domain| have the same domain+registry.
406   const std::string url_scheme(url.scheme());
407   const std::string url_domain_and_registry(
408       GetEffectiveDomain(url_scheme, url_host));
409   if (url_domain_and_registry.empty()) {
410     // We match IE/Firefox by treating an exact match between the normalized
411     // domain attribute and the request host to be treated as a host cookie.
412     std::string normalized_domain_string = base::ToLowerASCII(
413         domain_string[0] == '.' ? domain_string.substr(1) : domain_string);
414 
415     if (url_host == normalized_domain_string) {
416       *result = url_host;
417       DCHECK(DomainIsHostOnly(*result));
418       return true;
419     }
420 
421     // Otherwise, IP addresses/intranet hosts/public suffixes can't set
422     // domain cookies.
423     return false;
424   }
425   const std::string cookie_domain_and_registry(
426       GetEffectiveDomain(url_scheme, cookie_domain));
427   if (url_domain_and_registry != cookie_domain_and_registry)
428     return false;  // Can't set a cookie on a different domain + registry.
429 
430   // Ensure |url_host| is |cookie_domain| or one of its subdomains.  Given that
431   // we know the domain+registry are the same from the above checks, this is
432   // basically a simple string suffix check.
433   const bool is_suffix = (url_host.length() < cookie_domain.length()) ?
434       (cookie_domain != ("." + url_host)) :
435       (url_host.compare(url_host.length() - cookie_domain.length(),
436                         cookie_domain.length(), cookie_domain) != 0);
437   if (is_suffix)
438     return false;
439 
440   *result = cookie_domain;
441   return true;
442 }
443 
444 // Parse a cookie expiration time.  We try to be lenient, but we need to
445 // assume some order to distinguish the fields.  The basic rules:
446 //  - The month name must be present and prefix the first 3 letters of the
447 //    full month name (jan for January, jun for June).
448 //  - If the year is <= 2 digits, it must occur after the day of month.
449 //  - The time must be of the format hh:mm:ss.
450 // An average cookie expiration will look something like this:
451 //   Sat, 15-Apr-17 21:01:22 GMT
ParseCookieExpirationTime(const std::string & time_string)452 base::Time ParseCookieExpirationTime(const std::string& time_string) {
453   static const char* const kMonths[] = {
454     "jan", "feb", "mar", "apr", "may", "jun",
455     "jul", "aug", "sep", "oct", "nov", "dec" };
456   // We want to be pretty liberal, and support most non-ascii and non-digit
457   // characters as a delimiter.  We can't treat : as a delimiter, because it
458   // is the delimiter for hh:mm:ss, and we want to keep this field together.
459   // We make sure to include - and +, since they could prefix numbers.
460   // If the cookie attribute came in in quotes (ex expires="XXX"), the quotes
461   // will be preserved, and we will get them here.  So we make sure to include
462   // quote characters, and also \ for anything that was internally escaped.
463   static const char kDelimiters[] = "\t !\"#$%&'()*+,-./;<=>?@[\\]^_`{|}~";
464 
465   base::Time::Exploded exploded = {0};
466 
467   base::StringTokenizer tokenizer(time_string, kDelimiters);
468 
469   bool found_day_of_month = false;
470   bool found_month = false;
471   bool found_time = false;
472   bool found_year = false;
473 
474   while (tokenizer.GetNext()) {
475     const std::string token = tokenizer.token();
476     DCHECK(!token.empty());
477     bool numerical = base::IsAsciiDigit(token[0]);
478 
479     // String field
480     if (!numerical) {
481       if (!found_month) {
482         for (size_t i = 0; i < std::size(kMonths); ++i) {
483           // Match prefix, so we could match January, etc
484           if (base::StartsWith(token, std::string_view(kMonths[i], 3),
485                                base::CompareCase::INSENSITIVE_ASCII)) {
486             exploded.month = static_cast<int>(i) + 1;
487             found_month = true;
488             break;
489           }
490         }
491       } else {
492         // If we've gotten here, it means we've already found and parsed our
493         // month, and we have another string, which we would expect to be the
494         // the time zone name.  According to the RFC and my experiments with
495         // how sites format their expirations, we don't have much of a reason
496         // to support timezones.  We don't want to ever barf on user input,
497         // but this DCHECK should pass for well-formed data.
498         // DCHECK(token == "GMT");
499       }
500     // Numeric field w/ a colon
501     } else if (token.find(':') != std::string::npos) {
502       if (!found_time &&
503 #ifdef COMPILER_MSVC
504           sscanf_s(
505 #else
506           sscanf(
507 #endif
508                  token.c_str(), "%2u:%2u:%2u", &exploded.hour,
509                  &exploded.minute, &exploded.second) == 3) {
510         found_time = true;
511       } else {
512         // We should only ever encounter one time-like thing.  If we're here,
513         // it means we've found a second, which shouldn't happen.  We keep
514         // the first.  This check should be ok for well-formed input:
515         // NOTREACHED();
516       }
517     // Numeric field
518     } else {
519       // Overflow with atoi() is unspecified, so we enforce a max length.
520       if (!found_day_of_month && token.length() <= 2) {
521         exploded.day_of_month = atoi(token.c_str());
522         found_day_of_month = true;
523       } else if (!found_year && token.length() <= 5) {
524         exploded.year = atoi(token.c_str());
525         found_year = true;
526       } else {
527         // If we're here, it means we've either found an extra numeric field,
528         // or a numeric field which was too long.  For well-formed input, the
529         // following check would be reasonable:
530         // NOTREACHED();
531       }
532     }
533   }
534 
535   if (!found_day_of_month || !found_month || !found_time || !found_year) {
536     // We didn't find all of the fields we need.  For well-formed input, the
537     // following check would be reasonable:
538     // NOTREACHED() << "Cookie parse expiration failed: " << time_string;
539     return base::Time();
540   }
541 
542   // Normalize the year to expand abbreviated years to the full year.
543   if (exploded.year >= 70 && exploded.year <= 99)
544     exploded.year += 1900;
545   if (exploded.year >= 0 && exploded.year <= 69)
546     exploded.year += 2000;
547 
548   // Note that clipping the date if it is outside of a platform-specific range
549   // is permitted by: https://tools.ietf.org/html/rfc6265#section-5.2.1
550   base::Time result;
551   if (SaturatedTimeFromUTCExploded(exploded, &result))
552     return result;
553 
554   // One of our values was out of expected range.  For well-formed input,
555   // the following check would be reasonable:
556   // NOTREACHED() << "Cookie exploded expiration failed: " << time_string;
557 
558   return base::Time();
559 }
560 
CanonPathWithString(const GURL & url,const std::string & path_string)561 std::string CanonPathWithString(const GURL& url,
562                                 const std::string& path_string) {
563   // The path was supplied in the cookie, we'll take it.
564   if (!path_string.empty() && path_string[0] == '/') {
565     return path_string;
566   }
567 
568   // The path was not supplied in the cookie or invalid, we will default
569   // to the current URL path.
570   // """Defaults to the path of the request URL that generated the
571   //    Set-Cookie response, up to, but not including, the
572   //    right-most /."""
573   // How would this work for a cookie on /?  We will include it then.
574   const std::string& url_path = url.path();
575 
576   size_t idx = url_path.find_last_of('/');
577 
578   // The cookie path was invalid or a single '/'.
579   if (idx == 0 || idx == std::string::npos) {
580     return std::string("/");
581   }
582 
583   // Return up to the rightmost '/'.
584   return url_path.substr(0, idx);
585 }
586 
CookieDomainAndPathToURL(const std::string & domain,const std::string & path,const std::string & source_scheme)587 GURL CookieDomainAndPathToURL(const std::string& domain,
588                               const std::string& path,
589                               const std::string& source_scheme) {
590   // Note: domain_no_dot could be empty for e.g. file cookies.
591   std::string domain_no_dot = CookieDomainAsHost(domain);
592   if (domain_no_dot.empty() || source_scheme.empty())
593     return GURL();
594   return GURL(base::StrCat(
595       {source_scheme, url::kStandardSchemeSeparator, domain_no_dot, path}));
596 }
597 
CookieDomainAndPathToURL(const std::string & domain,const std::string & path,bool is_https)598 GURL CookieDomainAndPathToURL(const std::string& domain,
599                               const std::string& path,
600                               bool is_https) {
601   return CookieDomainAndPathToURL(
602       domain, path,
603       std::string(is_https ? url::kHttpsScheme : url::kHttpScheme));
604 }
605 
CookieDomainAndPathToURL(const std::string & domain,const std::string & path,CookieSourceScheme source_scheme)606 GURL CookieDomainAndPathToURL(const std::string& domain,
607                               const std::string& path,
608                               CookieSourceScheme source_scheme) {
609   return CookieDomainAndPathToURL(domain, path,
610                                   source_scheme == CookieSourceScheme::kSecure);
611 }
612 
CookieOriginToURL(const std::string & domain,bool is_https)613 GURL CookieOriginToURL(const std::string& domain, bool is_https) {
614   return CookieDomainAndPathToURL(domain, "/", is_https);
615 }
616 
SimulatedCookieSource(const CanonicalCookie & cookie,const std::string & source_scheme)617 GURL SimulatedCookieSource(const CanonicalCookie& cookie,
618                            const std::string& source_scheme) {
619   return CookieDomainAndPathToURL(cookie.Domain(), cookie.Path(),
620                                   source_scheme);
621 }
622 
ProvisionalAccessScheme(const GURL & source_url)623 CookieAccessScheme ProvisionalAccessScheme(const GURL& source_url) {
624   return source_url.SchemeIsCryptographic()
625              ? CookieAccessScheme::kCryptographic
626              : IsLocalhost(source_url) ? CookieAccessScheme::kTrustworthy
627                                        : CookieAccessScheme::kNonCryptographic;
628 }
629 
IsDomainMatch(const std::string & domain,const std::string & host)630 bool IsDomainMatch(const std::string& domain, const std::string& host) {
631   // Can domain match in two ways; as a domain cookie (where the cookie
632   // domain begins with ".") or as a host cookie (where it doesn't).
633 
634   // Some consumers of the CookieMonster expect to set cookies on
635   // URLs like http://.strange.url.  To retrieve cookies in this instance,
636   // we allow matching as a host cookie even when the domain_ starts with
637   // a period.
638   if (host == domain)
639     return true;
640 
641   // Domain cookie must have an initial ".".  To match, it must be
642   // equal to url's host with initial period removed, or a suffix of
643   // it.
644 
645   // Arguably this should only apply to "http" or "https" cookies, but
646   // extension cookie tests currently use the funtionality, and if we
647   // ever decide to implement that it should be done by preventing
648   // such cookies from being set.
649   if (domain.empty() || domain[0] != '.')
650     return false;
651 
652   // The host with a "." prefixed.
653   if (domain.compare(1, std::string::npos, host) == 0)
654     return true;
655 
656   // A pure suffix of the host (ok since we know the domain already
657   // starts with a ".")
658   return (host.length() > domain.length() &&
659           host.compare(host.length() - domain.length(), domain.length(),
660                        domain) == 0);
661 }
662 
IsOnPath(const std::string & cookie_path,const std::string & url_path)663 bool IsOnPath(const std::string& cookie_path, const std::string& url_path) {
664   // A zero length would be unsafe for our trailing '/' checks, and
665   // would also make no sense for our prefix match.  The code that
666   // creates a CanonicalCookie should make sure the path is never zero length,
667   // but we double check anyway.
668   if (cookie_path.empty()) {
669     return false;
670   }
671 
672   // The Mozilla code broke this into three cases, based on if the cookie path
673   // was longer, the same length, or shorter than the length of the url path.
674   // I think the approach below is simpler.
675 
676   // Make sure the cookie path is a prefix of the url path.  If the url path is
677   // shorter than the cookie path, then the cookie path can't be a prefix.
678   if (!url_path.starts_with(cookie_path)) {
679     return false;
680   }
681 
682   // |url_path| is >= |cookie_path|, and |cookie_path| is a prefix of
683   // |url_path|.  If they are the are the same length then they are identical,
684   // otherwise need an additional check:
685 
686   // In order to avoid in correctly matching a cookie path of /blah
687   // with a request path of '/blahblah/', we need to make sure that either
688   // the cookie path ends in a trailing '/', or that we prefix up to a '/'
689   // in the url path.  Since we know that the url path length is greater
690   // than the cookie path length, it's safe to index one byte past.
691   if (cookie_path.length() != url_path.length() && cookie_path.back() != '/' &&
692       url_path[cookie_path.length()] != '/') {
693     return false;
694   }
695 
696   return true;
697 }
698 
GetCookiePrefix(const std::string & name)699 CookiePrefix GetCookiePrefix(const std::string& name) {
700   const char kSecurePrefix[] = "__Secure-";
701   const char kHostPrefix[] = "__Host-";
702 
703   if (base::StartsWith(name, kSecurePrefix,
704                        base::CompareCase::INSENSITIVE_ASCII)) {
705     return COOKIE_PREFIX_SECURE;
706   }
707   if (base::StartsWith(name, kHostPrefix,
708                        base::CompareCase::INSENSITIVE_ASCII)) {
709     return COOKIE_PREFIX_HOST;
710   }
711   return COOKIE_PREFIX_NONE;
712 }
713 
IsCookiePrefixValid(CookiePrefix prefix,const GURL & url,const ParsedCookie & parsed_cookie)714 bool IsCookiePrefixValid(CookiePrefix prefix,
715                          const GURL& url,
716                          const ParsedCookie& parsed_cookie) {
717   return IsCookiePrefixValid(
718       prefix, url, parsed_cookie.IsSecure(),
719       parsed_cookie.HasDomain() ? parsed_cookie.Domain() : "",
720       parsed_cookie.HasPath() ? parsed_cookie.Path() : "");
721 }
722 
IsCookiePrefixValid(CookiePrefix prefix,const GURL & url,bool secure,const std::string & domain,const std::string & path)723 bool IsCookiePrefixValid(CookiePrefix prefix,
724                          const GURL& url,
725                          bool secure,
726                          const std::string& domain,
727                          const std::string& path) {
728   if (prefix == COOKIE_PREFIX_SECURE) {
729     return secure && url.SchemeIsCryptographic();
730   }
731   if (prefix == COOKIE_PREFIX_HOST) {
732     return HasValidHostPrefixAttributes(url, secure, domain, path);
733   }
734   return true;
735 }
736 
IsCookiePartitionedValid(const GURL & url,const ParsedCookie & parsed_cookie,bool partition_has_nonce)737 bool IsCookiePartitionedValid(const GURL& url,
738                               const ParsedCookie& parsed_cookie,
739                               bool partition_has_nonce) {
740   return IsCookiePartitionedValid(
741       url, /*secure=*/parsed_cookie.IsSecure(),
742       /*is_partitioned=*/parsed_cookie.IsPartitioned(), partition_has_nonce);
743 }
744 
IsCookiePartitionedValid(const GURL & url,bool secure,bool is_partitioned,bool partition_has_nonce)745 bool IsCookiePartitionedValid(const GURL& url,
746                               bool secure,
747                               bool is_partitioned,
748                               bool partition_has_nonce) {
749   if (!is_partitioned) {
750     return true;
751   }
752   if (partition_has_nonce) {
753     return true;
754   }
755   CookieAccessScheme scheme = cookie_util::ProvisionalAccessScheme(url);
756   bool result = (scheme != CookieAccessScheme::kNonCryptographic) && secure;
757   DLOG_IF(WARNING, !result) << "Cookie has invalid Partitioned attribute";
758   return result;
759 }
760 
ParseRequestCookieLine(const std::string & header_value,ParsedRequestCookies * parsed_cookies)761 void ParseRequestCookieLine(const std::string& header_value,
762                             ParsedRequestCookies* parsed_cookies) {
763   std::string::const_iterator i = header_value.begin();
764   while (i != header_value.end()) {
765     // Here we are at the beginning of a cookie.
766 
767     // Eat whitespace.
768     while (i != header_value.end() && *i == ' ') ++i;
769     if (i == header_value.end()) return;
770 
771     // Find cookie name.
772     std::string::const_iterator cookie_name_beginning = i;
773     while (i != header_value.end() && *i != '=') ++i;
774     auto cookie_name = base::MakeStringPiece(cookie_name_beginning, i);
775 
776     // Find cookie value.
777     std::string_view cookie_value;
778     // Cookies may have no value, in this case '=' may or may not be there.
779     if (i != header_value.end() && i + 1 != header_value.end()) {
780       ++i;  // Skip '='.
781       std::string::const_iterator cookie_value_beginning = i;
782       if (*i == '"') {
783         ++i;  // Skip '"'.
784         while (i != header_value.end() && *i != '"') ++i;
785         if (i == header_value.end()) return;
786         ++i;  // Skip '"'.
787         cookie_value = base::MakeStringPiece(cookie_value_beginning, i);
788         // i points to character after '"', potentially a ';'.
789       } else {
790         while (i != header_value.end() && *i != ';') ++i;
791         cookie_value = base::MakeStringPiece(cookie_value_beginning, i);
792         // i points to ';' or end of string.
793       }
794     }
795     parsed_cookies->emplace_back(std::string(cookie_name),
796                                  std::string(cookie_value));
797     // Eat ';'.
798     if (i != header_value.end()) ++i;
799   }
800 }
801 
SerializeRequestCookieLine(const ParsedRequestCookies & parsed_cookies)802 std::string SerializeRequestCookieLine(
803     const ParsedRequestCookies& parsed_cookies) {
804   std::string buffer;
805   for (const auto& parsed_cookie : parsed_cookies) {
806     if (!buffer.empty())
807       buffer.append("; ");
808     buffer.append(parsed_cookie.first.begin(), parsed_cookie.first.end());
809     buffer.push_back('=');
810     buffer.append(parsed_cookie.second.begin(), parsed_cookie.second.end());
811   }
812   return buffer;
813 }
814 
ComputeSameSiteContextForRequest(const std::string & http_method,const std::vector<GURL> & url_chain,const SiteForCookies & site_for_cookies,const std::optional<url::Origin> & initiator,bool is_main_frame_navigation,bool force_ignore_site_for_cookies)815 CookieOptions::SameSiteCookieContext ComputeSameSiteContextForRequest(
816     const std::string& http_method,
817     const std::vector<GURL>& url_chain,
818     const SiteForCookies& site_for_cookies,
819     const std::optional<url::Origin>& initiator,
820     bool is_main_frame_navigation,
821     bool force_ignore_site_for_cookies) {
822   // Set SameSiteCookieContext according to the rules laid out in
823   // https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis:
824   //
825   // * Include both "strict" and "lax" same-site cookies if the request's
826   //   |url|, |initiator|, and |site_for_cookies| all have the same
827   //   registrable domain. Note: this also covers the case of a request
828   //   without an initiator (only happens for browser-initiated main frame
829   //   navigations). If computing schemefully, the schemes must also match.
830   //
831   // * Include only "lax" same-site cookies if the request's |URL| and
832   //   |site_for_cookies| have the same registrable domain, _and_ the
833   //   request's |http_method| is "safe" ("GET" or "HEAD"), and the request
834   //   is a main frame navigation.
835   //
836   //   This case should occur only for cross-site requests which
837   //   target a top-level browsing context, with a "safe" method.
838   //
839   // * Include both "strict" and "lax" same-site cookies if the request is
840   //   tagged with a flag allowing it.
841   //
842   //   Note that this can be the case for requests initiated by extensions,
843   //   which need to behave as though they are made by the document itself,
844   //   but appear like cross-site ones.
845   //
846   // * Otherwise, do not include same-site cookies.
847 
848   if (force_ignore_site_for_cookies)
849     return CookieOptions::SameSiteCookieContext::MakeInclusive();
850 
851   ComputeSameSiteContextResult result = ComputeSameSiteContext(
852       url_chain, site_for_cookies, initiator, true /* is_http */,
853       is_main_frame_navigation, false /* compute_schemefully */);
854   ComputeSameSiteContextResult schemeful_result = ComputeSameSiteContext(
855       url_chain, site_for_cookies, initiator, true /* is_http */,
856       is_main_frame_navigation, true /* compute_schemefully */);
857 
858   // If the method is safe, the context is Lax. Otherwise, make a note that
859   // the method is unsafe.
860   if (!net::HttpUtil::IsMethodSafe(http_method)) {
861     if (result.context_type == ContextType::SAME_SITE_LAX)
862       result.context_type = ContextType::SAME_SITE_LAX_METHOD_UNSAFE;
863     if (schemeful_result.context_type == ContextType::SAME_SITE_LAX)
864       schemeful_result.context_type = ContextType::SAME_SITE_LAX_METHOD_UNSAFE;
865   }
866 
867   ContextMetadata::HttpMethod http_method_enum =
868       HttpMethodStringToEnum(http_method);
869 
870   if (result.metadata.cross_site_redirect_downgrade !=
871       ContextMetadata::ContextDowngradeType::kNoDowngrade) {
872     result.metadata.http_method_bug_1221316 = http_method_enum;
873   }
874 
875   if (schemeful_result.metadata.cross_site_redirect_downgrade !=
876       ContextMetadata::ContextDowngradeType::kNoDowngrade) {
877     schemeful_result.metadata.http_method_bug_1221316 = http_method_enum;
878   }
879 
880   return MakeSameSiteCookieContext(result, schemeful_result);
881 }
882 
883 NET_EXPORT CookieOptions::SameSiteCookieContext
ComputeSameSiteContextForScriptGet(const GURL & url,const SiteForCookies & site_for_cookies,const std::optional<url::Origin> & initiator,bool force_ignore_site_for_cookies)884 ComputeSameSiteContextForScriptGet(const GURL& url,
885                                    const SiteForCookies& site_for_cookies,
886                                    const std::optional<url::Origin>& initiator,
887                                    bool force_ignore_site_for_cookies) {
888   if (force_ignore_site_for_cookies)
889     return CookieOptions::SameSiteCookieContext::MakeInclusive();
890 
891   // We don't check the redirect chain for script access to cookies (only the
892   // URL itself).
893   ComputeSameSiteContextResult result = ComputeSameSiteContext(
894       {url}, site_for_cookies, initiator, false /* is_http */,
895       false /* is_main_frame_navigation */, false /* compute_schemefully */);
896   ComputeSameSiteContextResult schemeful_result = ComputeSameSiteContext(
897       {url}, site_for_cookies, initiator, false /* is_http */,
898       false /* is_main_frame_navigation */, true /* compute_schemefully */);
899 
900   return MakeSameSiteCookieContext(result, schemeful_result);
901 }
902 
ComputeSameSiteContextForResponse(const std::vector<GURL> & url_chain,const SiteForCookies & site_for_cookies,const std::optional<url::Origin> & initiator,bool is_main_frame_navigation,bool force_ignore_site_for_cookies)903 CookieOptions::SameSiteCookieContext ComputeSameSiteContextForResponse(
904     const std::vector<GURL>& url_chain,
905     const SiteForCookies& site_for_cookies,
906     const std::optional<url::Origin>& initiator,
907     bool is_main_frame_navigation,
908     bool force_ignore_site_for_cookies) {
909   if (force_ignore_site_for_cookies)
910     return CookieOptions::SameSiteCookieContext::MakeInclusiveForSet();
911 
912   DCHECK(!url_chain.empty());
913   if (is_main_frame_navigation && !site_for_cookies.IsNull()) {
914     // If the request is a main frame navigation, site_for_cookies must either
915     // be null (for opaque origins, e.g., data: origins) or same-site with the
916     // request URL (both schemefully and schemelessly), and the URL cannot be
917     // ws/wss (these schemes are not navigable).
918     DCHECK(
919         site_for_cookies.IsFirstPartyWithSchemefulMode(url_chain.back(), true));
920     DCHECK(!url_chain.back().SchemeIsWSOrWSS());
921     CookieOptions::SameSiteCookieContext result =
922         CookieOptions::SameSiteCookieContext::MakeInclusiveForSet();
923 
924     const GURL& request_url = url_chain.back();
925 
926     for (bool compute_schemefully : {false, true}) {
927       bool same_site_initiator =
928           !initiator ||
929           SiteForCookies::FromOrigin(initiator.value())
930               .IsFirstPartyWithSchemefulMode(request_url, compute_schemefully);
931 
932       const auto is_same_site_with_site_for_cookies =
933           [&site_for_cookies, compute_schemefully](const GURL& url) {
934             return site_for_cookies.IsFirstPartyWithSchemefulMode(
935                 url, compute_schemefully);
936           };
937 
938       bool same_site_redirect_chain =
939           url_chain.size() == 1u ||
940           base::ranges::all_of(url_chain, is_same_site_with_site_for_cookies);
941 
942       CookieOptions::SameSiteCookieContext::ContextMetadata& result_metadata =
943           compute_schemefully ? result.schemeful_metadata() : result.metadata();
944 
945       result_metadata.redirect_type_bug_1221316 =
946           ComputeContextRedirectTypeBug1221316(
947               url_chain.size() == 1u, same_site_initiator,
948               true /* site_for_cookies_is_same_site */,
949               same_site_redirect_chain);
950     }
951     return result;
952   }
953 
954   return ComputeSameSiteContextForSet(url_chain, site_for_cookies, initiator,
955                                       true /* is_http */,
956                                       is_main_frame_navigation);
957 }
958 
ComputeSameSiteContextForScriptSet(const GURL & url,const SiteForCookies & site_for_cookies,bool force_ignore_site_for_cookies)959 CookieOptions::SameSiteCookieContext ComputeSameSiteContextForScriptSet(
960     const GURL& url,
961     const SiteForCookies& site_for_cookies,
962     bool force_ignore_site_for_cookies) {
963   if (force_ignore_site_for_cookies)
964     return CookieOptions::SameSiteCookieContext::MakeInclusiveForSet();
965 
966   // It doesn't matter what initiator origin we pass here. Either way, the
967   // context will be considered same-site iff the site_for_cookies is same-site
968   // with the url. We don't check the redirect chain for script access to
969   // cookies (only the URL itself).
970   return ComputeSameSiteContextForSet(
971       {url}, site_for_cookies, std::nullopt /* initiator */,
972       false /* is_http */, false /* is_main_frame_navigation */);
973 }
974 
ComputeSameSiteContextForSubresource(const GURL & url,const SiteForCookies & site_for_cookies,bool force_ignore_site_for_cookies)975 CookieOptions::SameSiteCookieContext ComputeSameSiteContextForSubresource(
976     const GURL& url,
977     const SiteForCookies& site_for_cookies,
978     bool force_ignore_site_for_cookies) {
979   if (force_ignore_site_for_cookies)
980     return CookieOptions::SameSiteCookieContext::MakeInclusive();
981 
982   // If the URL is same-site as site_for_cookies it's same-site as all frames
983   // in the tree from the initiator frame up --- including the initiator frame.
984 
985   // Schemeless check
986   if (!site_for_cookies.IsFirstPartyWithSchemefulMode(url, false)) {
987     return CookieOptions::SameSiteCookieContext(ContextType::CROSS_SITE,
988                                                 ContextType::CROSS_SITE);
989   }
990 
991   // Schemeful check
992   if (!site_for_cookies.IsFirstPartyWithSchemefulMode(url, true)) {
993     return CookieOptions::SameSiteCookieContext(ContextType::SAME_SITE_STRICT,
994                                                 ContextType::CROSS_SITE);
995   }
996 
997   return CookieOptions::SameSiteCookieContext::MakeInclusive();
998 }
999 
IsPortBoundCookiesEnabled()1000 bool IsPortBoundCookiesEnabled() {
1001   return base::FeatureList::IsEnabled(features::kEnablePortBoundCookies);
1002 }
1003 
IsSchemeBoundCookiesEnabled()1004 bool IsSchemeBoundCookiesEnabled() {
1005   return base::FeatureList::IsEnabled(features::kEnableSchemeBoundCookies);
1006 }
1007 
IsOriginBoundCookiesPartiallyEnabled()1008 bool IsOriginBoundCookiesPartiallyEnabled() {
1009   return IsPortBoundCookiesEnabled() || IsSchemeBoundCookiesEnabled();
1010 }
1011 
IsTimeLimitedInsecureCookiesEnabled()1012 bool IsTimeLimitedInsecureCookiesEnabled() {
1013   return IsSchemeBoundCookiesEnabled() &&
1014          base::FeatureList::IsEnabled(features::kTimeLimitedInsecureCookies);
1015 }
1016 
IsSchemefulSameSiteEnabled()1017 bool IsSchemefulSameSiteEnabled() {
1018   return base::FeatureList::IsEnabled(features::kSchemefulSameSite);
1019 }
1020 
1021 std::optional<
1022     std::pair<FirstPartySetMetadata, FirstPartySetsCacheFilter::MatchInfo>>
ComputeFirstPartySetMetadataMaybeAsync(const SchemefulSite & request_site,const IsolationInfo & isolation_info,const CookieAccessDelegate * cookie_access_delegate,base::OnceCallback<void (FirstPartySetMetadata,FirstPartySetsCacheFilter::MatchInfo)> callback)1023 ComputeFirstPartySetMetadataMaybeAsync(
1024     const SchemefulSite& request_site,
1025     const IsolationInfo& isolation_info,
1026     const CookieAccessDelegate* cookie_access_delegate,
1027     base::OnceCallback<void(FirstPartySetMetadata,
1028                             FirstPartySetsCacheFilter::MatchInfo)> callback) {
1029   if (cookie_access_delegate) {
1030     return cookie_access_delegate->ComputeFirstPartySetMetadataMaybeAsync(
1031         request_site,
1032         base::OptionalToPtr(
1033             isolation_info.network_isolation_key().GetTopFrameSite()),
1034         std::move(callback));
1035   }
1036 
1037   return std::pair(FirstPartySetMetadata(),
1038                    FirstPartySetsCacheFilter::MatchInfo());
1039 }
1040 
1041 CookieOptions::SameSiteCookieContext::ContextMetadata::HttpMethod
HttpMethodStringToEnum(const std::string & in)1042 HttpMethodStringToEnum(const std::string& in) {
1043   using HttpMethod =
1044       CookieOptions::SameSiteCookieContext::ContextMetadata::HttpMethod;
1045   if (in == "GET")
1046     return HttpMethod::kGet;
1047   if (in == "HEAD")
1048     return HttpMethod::kHead;
1049   if (in == "POST")
1050     return HttpMethod::kPost;
1051   if (in == "PUT")
1052     return HttpMethod::KPut;
1053   if (in == "DELETE")
1054     return HttpMethod::kDelete;
1055   if (in == "CONNECT")
1056     return HttpMethod::kConnect;
1057   if (in == "OPTIONS")
1058     return HttpMethod::kOptions;
1059   if (in == "TRACE")
1060     return HttpMethod::kTrace;
1061   if (in == "PATCH")
1062     return HttpMethod::kPatch;
1063 
1064   return HttpMethod::kUnknown;
1065 }
1066 
IsCookieAccessResultInclude(CookieAccessResult cookie_access_result)1067 bool IsCookieAccessResultInclude(CookieAccessResult cookie_access_result) {
1068   return cookie_access_result.status.IsInclude();
1069 }
1070 
StripAccessResults(const CookieAccessResultList & cookie_access_results_list)1071 CookieList StripAccessResults(
1072     const CookieAccessResultList& cookie_access_results_list) {
1073   CookieList cookies;
1074   for (const CookieWithAccessResult& cookie_with_access_result :
1075        cookie_access_results_list) {
1076     cookies.push_back(cookie_with_access_result.cookie);
1077   }
1078   return cookies;
1079 }
1080 
RecordCookiePortOmniboxHistograms(const GURL & url)1081 NET_EXPORT void RecordCookiePortOmniboxHistograms(const GURL& url) {
1082   int port = url.EffectiveIntPort();
1083 
1084   if (port == url::PORT_UNSPECIFIED)
1085     return;
1086 
1087   if (IsLocalhost(url)) {
1088     UMA_HISTOGRAM_ENUMERATION("Cookie.Port.OmniboxURLNavigation.Localhost",
1089                               ReducePortRangeForCookieHistogram(port));
1090   } else {
1091     UMA_HISTOGRAM_ENUMERATION("Cookie.Port.OmniboxURLNavigation.RemoteHost",
1092                               ReducePortRangeForCookieHistogram(port));
1093   }
1094 }
1095 
DCheckIncludedAndExcludedCookieLists(const CookieAccessResultList & included_cookies,const CookieAccessResultList & excluded_cookies)1096 NET_EXPORT void DCheckIncludedAndExcludedCookieLists(
1097     const CookieAccessResultList& included_cookies,
1098     const CookieAccessResultList& excluded_cookies) {
1099   // Check that all elements of `included_cookies` really should be included,
1100   // and that all elements of `excluded_cookies` really should be excluded.
1101   DCHECK(base::ranges::all_of(included_cookies,
1102                               [](const net::CookieWithAccessResult& cookie) {
1103                                 return cookie.access_result.status.IsInclude();
1104                               }));
1105   DCHECK(base::ranges::none_of(excluded_cookies,
1106                                [](const net::CookieWithAccessResult& cookie) {
1107                                  return cookie.access_result.status.IsInclude();
1108                                }));
1109 
1110   // Check that the included cookies are still in the correct order.
1111   DCHECK(
1112       base::ranges::is_sorted(included_cookies, CookieWithAccessResultSorter));
1113 }
1114 
IsForceThirdPartyCookieBlockingEnabled()1115 NET_EXPORT bool IsForceThirdPartyCookieBlockingEnabled() {
1116   return base::FeatureList::IsEnabled(
1117              features::kForceThirdPartyCookieBlocking) &&
1118          base::FeatureList::IsEnabled(features::kThirdPartyStoragePartitioning);
1119 }
1120 
PartitionedCookiesDisabledByCommandLine()1121 bool PartitionedCookiesDisabledByCommandLine() {
1122   const base::CommandLine* command_line =
1123       base::CommandLine::ForCurrentProcess();
1124   if (!command_line) {
1125     return false;
1126   }
1127   return command_line->HasSwitch(kDisablePartitionedCookiesSwitch);
1128 }
1129 
AddOrRemoveStorageAccessApiOverride(const GURL & url,StorageAccessApiStatus api_status,base::optional_ref<const url::Origin> request_initiator,CookieSettingOverrides & overrides)1130 void AddOrRemoveStorageAccessApiOverride(
1131     const GURL& url,
1132     StorageAccessApiStatus api_status,
1133     base::optional_ref<const url::Origin> request_initiator,
1134     CookieSettingOverrides& overrides) {
1135   overrides.PutOrRemove(
1136       CookieSettingOverride::kStorageAccessGrantEligible,
1137       api_status == StorageAccessApiStatus::kAccessViaAPI &&
1138           request_initiator.has_value() &&
1139           IsSameSiteIgnoringWebSocketProtocol(request_initiator.value(), url));
1140 }
1141 
1142 }  // namespace net::cookie_util
1143