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