xref: /aosp_15_r20/external/cronet/net/cookies/site_for_cookies.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2019 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "net/cookies/site_for_cookies.h"
6 
7 #include <utility>
8 
9 #include "base/strings/strcat.h"
10 #include "base/strings/string_util.h"
11 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
12 #include "net/cookies/cookie_util.h"
13 
14 namespace net {
15 
16 SiteForCookies::SiteForCookies() = default;
17 
SiteForCookies(const SchemefulSite & site)18 SiteForCookies::SiteForCookies(const SchemefulSite& site)
19     : site_(site), schemefully_same_(!site.opaque()) {
20   site_.ConvertWebSocketToHttp();
21 }
22 
23 SiteForCookies::SiteForCookies(const SiteForCookies& other) = default;
24 SiteForCookies::SiteForCookies(SiteForCookies&& other) = default;
25 
26 SiteForCookies::~SiteForCookies() = default;
27 
28 SiteForCookies& SiteForCookies::operator=(const SiteForCookies& other) =
29     default;
30 SiteForCookies& SiteForCookies::operator=(SiteForCookies&& site_for_cookies) =
31     default;
32 
33 // static
FromWire(const SchemefulSite & site,bool schemefully_same,SiteForCookies * out)34 bool SiteForCookies::FromWire(const SchemefulSite& site,
35                               bool schemefully_same,
36                               SiteForCookies* out) {
37   SiteForCookies candidate(site);
38   if (site != candidate.site_)
39     return false;
40 
41   candidate.schemefully_same_ = schemefully_same;
42 
43   *out = std::move(candidate);
44   return true;
45 }
46 
47 // static
FromOrigin(const url::Origin & origin)48 SiteForCookies SiteForCookies::FromOrigin(const url::Origin& origin) {
49   return SiteForCookies(SchemefulSite(origin));
50 }
51 
52 // static
FromUrl(const GURL & url)53 SiteForCookies SiteForCookies::FromUrl(const GURL& url) {
54   return SiteForCookies::FromOrigin(url::Origin::Create(url));
55 }
56 
ToDebugString() const57 std::string SiteForCookies::ToDebugString() const {
58   std::string same_scheme_string = schemefully_same_ ? "true" : "false";
59   return base::StrCat({"SiteForCookies: {site=", site_.Serialize(),
60                        "; schemefully_same=", same_scheme_string, "}"});
61 }
62 
IsFirstParty(const GURL & url) const63 bool SiteForCookies::IsFirstParty(const GURL& url) const {
64   return IsFirstPartyWithSchemefulMode(
65       url, cookie_util::IsSchemefulSameSiteEnabled());
66 }
67 
IsFirstPartyWithSchemefulMode(const GURL & url,bool compute_schemefully) const68 bool SiteForCookies::IsFirstPartyWithSchemefulMode(
69     const GURL& url,
70     bool compute_schemefully) const {
71   if (compute_schemefully)
72     return IsSchemefullyFirstParty(url);
73 
74   return IsSchemelesslyFirstParty(url);
75 }
76 
IsEquivalent(const SiteForCookies & other) const77 bool SiteForCookies::IsEquivalent(const SiteForCookies& other) const {
78   if (IsNull() || other.IsNull()) {
79     // We need to check if `other.IsNull()` explicitly in order to catch if
80     // `other.schemefully_same_` is false when "Schemeful Same-Site" is enabled.
81     return IsNull() && other.IsNull();
82   }
83 
84   // In the case where the site has no registrable domain or host, the scheme
85   // cannot be ws(s) or http(s), so equality of sites implies actual equality of
86   // schemes (not just modulo ws-http and wss-https compatibility).
87   if (cookie_util::IsSchemefulSameSiteEnabled() ||
88       !site_.has_registrable_domain_or_host()) {
89     return site_ == other.site_;
90   }
91 
92   return site_.SchemelesslyEqual(other.site_);
93 }
94 
CompareWithFrameTreeSiteAndRevise(const SchemefulSite & other)95 bool SiteForCookies::CompareWithFrameTreeSiteAndRevise(
96     const SchemefulSite& other) {
97   // Two opaque SFC are considered equivalent.
98   if (site_.opaque() && other.opaque())
99     return true;
100 
101   // But if only one is opaque we should return false.
102   if (site_.opaque())
103     return false;
104 
105   // Nullify `this` if the `other` is opaque
106   if (other.opaque()) {
107     site_ = SchemefulSite();
108     return false;
109   }
110 
111   bool nullify = site_.has_registrable_domain_or_host()
112                      ? !site_.SchemelesslyEqual(other)
113                      : site_ != other;
114 
115   if (nullify) {
116     // We should only nullify this SFC if the registrable domains (or the entire
117     // site for cases without an RD) don't match. We *should not* nullify if
118     // only the schemes mismatch (unless there is no RD) because cookies may be
119     // processed with LEGACY semantics which only use the RDs. Eventually, when
120     // schemeful same-site can no longer be disabled, we can revisit this.
121     site_ = SchemefulSite();
122     return false;
123   }
124 
125   MarkIfCrossScheme(other);
126 
127   return true;
128 }
129 
CompareWithFrameTreeOriginAndRevise(const url::Origin & other)130 bool SiteForCookies::CompareWithFrameTreeOriginAndRevise(
131     const url::Origin& other) {
132   return CompareWithFrameTreeSiteAndRevise(SchemefulSite(other));
133 }
134 
RepresentativeUrl() const135 GURL SiteForCookies::RepresentativeUrl() const {
136   if (IsNull())
137     return GURL();
138   // Cannot use url::Origin::GetURL() because it loses the hostname for file:
139   // scheme origins.
140   GURL result(base::StrCat({scheme(), "://", registrable_domain(), "/"}));
141   DCHECK(result.is_valid());
142   return result;
143 }
144 
IsNull() const145 bool SiteForCookies::IsNull() const {
146   if (cookie_util::IsSchemefulSameSiteEnabled())
147     return site_.opaque() || !schemefully_same_;
148 
149   return site_.opaque();
150 }
151 
IsSchemefullyFirstParty(const GURL & url) const152 bool SiteForCookies::IsSchemefullyFirstParty(const GURL& url) const {
153   // Can't use IsNull() as we want the same behavior regardless of
154   // SchemefulSameSite feature status.
155   if (site_.opaque() || !schemefully_same_ || !url.is_valid())
156     return false;
157 
158   SchemefulSite other_site(url);
159   other_site.ConvertWebSocketToHttp();
160   return site_ == other_site;
161 }
162 
IsSchemelesslyFirstParty(const GURL & url) const163 bool SiteForCookies::IsSchemelesslyFirstParty(const GURL& url) const {
164   // Can't use IsNull() as we want the same behavior regardless of
165   // SchemefulSameSite feature status.
166   if (site_.opaque() || !url.is_valid())
167     return false;
168 
169   // We don't need to bother changing WebSocket schemes to http, because if
170   // there is no registrable domain or host, the scheme cannot be ws(s) or
171   // http(s), and the latter comparison is schemeless anyway.
172   SchemefulSite other_site(url);
173   if (!site_.has_registrable_domain_or_host())
174     return site_ == other_site;
175 
176   return site_.SchemelesslyEqual(other_site);
177 }
178 
MarkIfCrossScheme(const SchemefulSite & other)179 void SiteForCookies::MarkIfCrossScheme(const SchemefulSite& other) {
180   // If `this` is IsNull() then `this` doesn't match anything which means that
181   // the scheme check is pointless. Also exit early if schemefully_same_ is
182   // already false.
183   if (IsNull() || !schemefully_same_)
184     return;
185 
186   // Mark if `other` is opaque. Opaque origins shouldn't match.
187   if (other.opaque()) {
188     schemefully_same_ = false;
189     return;
190   }
191 
192   // Conversion to http/https should have occurred during construction.
193   DCHECK_NE(url::kWsScheme, scheme());
194   DCHECK_NE(url::kWssScheme, scheme());
195 
196   // If the schemes are equal, modulo ws-http and wss-https, don't mark.
197   if (scheme() == other.site_as_origin_.scheme() ||
198       (scheme() == url::kHttpsScheme &&
199        other.site_as_origin_.scheme() == url::kWssScheme) ||
200       (scheme() == url::kHttpScheme &&
201        other.site_as_origin_.scheme() == url::kWsScheme)) {
202     return;
203   }
204 
205   // Mark that the two are cross-scheme to each other.
206   schemefully_same_ = false;
207 }
208 
operator <(const SiteForCookies & lhs,const SiteForCookies & rhs)209 bool operator<(const SiteForCookies& lhs, const SiteForCookies& rhs) {
210   // Similar to IsEquivalent(), if they're both null then they're equivalent
211   // and therefore `lhs` is not < `rhs`.
212   if (lhs.IsNull() && rhs.IsNull())
213     return false;
214 
215   // If only `lhs` is null then it's always < `rhs`.
216   if (lhs.IsNull())
217     return true;
218 
219   // If only `rhs` is null then `lhs` is not < `rhs`.
220   if (rhs.IsNull())
221     return false;
222 
223   // Otherwise neither are null and we need to compare the `site_`s.
224   return lhs.site_ < rhs.site_;
225 }
226 
227 }  // namespace net
228