1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #ifndef NET_COOKIES_CANONICAL_COOKIE_H_
6 #define NET_COOKIES_CANONICAL_COOKIE_H_
7
8 #include <memory>
9 #include <string>
10 #include <tuple>
11 #include <vector>
12
13 #include "base/feature_list.h"
14 #include "base/gtest_prod_util.h"
15 #include "base/strings/string_piece.h"
16 #include "base/time/time.h"
17 #include "base/types/pass_key.h"
18 #include "net/base/features.h"
19 #include "net/base/net_export.h"
20 #include "net/cookies/cookie_access_result.h"
21 #include "net/cookies/cookie_constants.h"
22 #include "net/cookies/cookie_inclusion_status.h"
23 #include "net/cookies/cookie_options.h"
24 #include "net/cookies/cookie_partition_key.h"
25 #include "third_party/abseil-cpp/absl/types/optional.h"
26 #include "url/third_party/mozilla/url_parse.h"
27
28 class GURL;
29
30 namespace net {
31
32 class ParsedCookie;
33 class CanonicalCookie;
34
35 struct CookieWithAccessResult;
36 struct CookieAndLineWithAccessResult;
37
38 using CookieList = std::vector<CanonicalCookie>;
39 using CookieAndLineAccessResultList =
40 std::vector<CookieAndLineWithAccessResult>;
41 using CookieAccessResultList = std::vector<CookieWithAccessResult>;
42
43 struct NET_EXPORT CookieAccessParams {
44 CookieAccessParams() = delete;
45 CookieAccessParams(CookieAccessSemantics access_semantics,
46 bool delegate_treats_url_as_trustworthy);
47
48 // |access_semantics| is the access mode of the cookie access check.
49 CookieAccessSemantics access_semantics = CookieAccessSemantics::UNKNOWN;
50 // |delegate_treats_url_as_trustworthy| should be true iff the
51 // CookieAccessDelegate has authorized access to secure cookies from URLs
52 // which might not otherwise be able to do so.
53 bool delegate_treats_url_as_trustworthy = false;
54 };
55
56 class NET_EXPORT CanonicalCookie {
57 public:
58 // StrictlyUniqueCookieKey always populates the cookie's source scheme and
59 // source port.
60 using StrictlyUniqueCookieKey = std::tuple<absl::optional<CookiePartitionKey>,
61 /*name=*/std::string,
62 /*domain=*/std::string,
63 /*path=*/std::string,
64 CookieSourceScheme,
65 /*source_port=*/int>;
66
67 // Conditionally populates the source scheme and source port depending on the
68 // state of their associated feature.
69 using UniqueCookieKey = std::tuple<absl::optional<CookiePartitionKey>,
70 /*name=*/std::string,
71 /*domain=*/std::string,
72 /*path=*/std::string,
73 absl::optional<CookieSourceScheme>,
74 /*source_port=*/absl::optional<int>>;
75
76 CanonicalCookie();
77 CanonicalCookie(const CanonicalCookie& other);
78 CanonicalCookie(CanonicalCookie&& other);
79 CanonicalCookie& operator=(const CanonicalCookie& other);
80 CanonicalCookie& operator=(CanonicalCookie&& other);
81 ~CanonicalCookie();
82
83 // This constructor does not validate or canonicalize their inputs;
84 // the resulting CanonicalCookies should not be relied on to be canonical
85 // unless the caller has done appropriate validation and canonicalization
86 // themselves.
87 // NOTE: Prefer using CreateSanitizedCookie() over directly using this
88 // constructor.
89 CanonicalCookie(base::PassKey<CanonicalCookie>,
90 std::string name,
91 std::string value,
92 std::string domain,
93 std::string path,
94 base::Time creation,
95 base::Time expiration,
96 base::Time last_access,
97 base::Time last_update,
98 bool secure,
99 bool httponly,
100 CookieSameSite same_site,
101 CookiePriority priority,
102 absl::optional<CookiePartitionKey> partition_key,
103 CookieSourceScheme scheme_secure = CookieSourceScheme::kUnset,
104 int source_port = url::PORT_UNSPECIFIED);
105
106 // Creates a new |CanonicalCookie| from the |cookie_line| and the
107 // |creation_time|. Canonicalizes inputs. May return nullptr if
108 // an attribute value is invalid. |url| must be valid. |creation_time| may
109 // not be null. Sets optional |status| to the relevant CookieInclusionStatus
110 // if provided. |server_time| indicates what the server sending us the Cookie
111 // thought the current time was when the cookie was produced. This is used to
112 // adjust for clock skew between server and host.
113 //
114 // SameSite and HttpOnly related parameters are not checked here,
115 // so creation of CanonicalCookies with e.g. SameSite=Strict from a cross-site
116 // context is allowed. Create() also does not check whether |url| has a secure
117 // scheme if attempting to create a Secure cookie. The Secure, SameSite, and
118 // HttpOnly related parameters should be checked when setting the cookie in
119 // the CookieStore.
120 //
121 // The partition_key argument only needs to be present if the cookie line
122 // contains the Partitioned attribute. If the cookie line will never contain
123 // that attribute, you should use absl::nullopt to indicate you intend to
124 // always create an unpartitioned cookie. If partition_key has a value but the
125 // cookie line does not contain the Partitioned attribute, the resulting
126 // cookie will be unpartitioned. If the partition_key is null, then the cookie
127 // will be unpartitioned even when the cookie line has the Partitioned
128 // attribute.
129 //
130 // The `block_truncated` argument indicates whether the '\0', '\n', and '\r'
131 // characters should cause the cookie to fail to be created if present
132 // (instead of truncating `cookie_line` at the first occurrence).
133 //
134 // If a cookie is returned, |cookie->IsCanonical()| will be true.
135 static std::unique_ptr<CanonicalCookie> Create(
136 const GURL& url,
137 const std::string& cookie_line,
138 const base::Time& creation_time,
139 absl::optional<base::Time> server_time,
140 absl::optional<CookiePartitionKey> cookie_partition_key,
141 bool block_truncated = true,
142 CookieInclusionStatus* status = nullptr);
143
144 // Create a canonical cookie based on sanitizing the passed inputs in the
145 // context of the passed URL. Returns a null unique pointer if the inputs
146 // cannot be sanitized. If `status` is provided it will have any relevant
147 // CookieInclusionStatus rejection reasons set. If a cookie is created,
148 // `cookie->IsCanonical()` will be true.
149 static std::unique_ptr<CanonicalCookie> CreateSanitizedCookie(
150 const GURL& url,
151 const std::string& name,
152 const std::string& value,
153 const std::string& domain,
154 const std::string& path,
155 base::Time creation_time,
156 base::Time expiration_time,
157 base::Time last_access_time,
158 bool secure,
159 bool http_only,
160 CookieSameSite same_site,
161 CookiePriority priority,
162 absl::optional<CookiePartitionKey> partition_key,
163 CookieInclusionStatus* status = nullptr);
164
165 // FromStorage is a factory method which is meant for creating a new
166 // CanonicalCookie using properties of a previously existing cookie
167 // that was already ingested into the cookie store.
168 // This should NOT be used to create a new CanonicalCookie that was not
169 // already in the store.
170 // Returns nullptr if the resulting cookie is not canonical,
171 // i.e. cc->IsCanonical() returns false.
172 static std::unique_ptr<CanonicalCookie> FromStorage(
173 std::string name,
174 std::string value,
175 std::string domain,
176 std::string path,
177 base::Time creation,
178 base::Time expiration,
179 base::Time last_access,
180 base::Time last_update,
181 bool secure,
182 bool httponly,
183 CookieSameSite same_site,
184 CookiePriority priority,
185 absl::optional<CookiePartitionKey> partition_key,
186 CookieSourceScheme source_scheme,
187 int source_port);
188
189 // Create a CanonicalCookie that is not guaranteed to actually be Canonical
190 // for tests. This factory should NOT be used in production.
191 static std::unique_ptr<CanonicalCookie> CreateUnsafeCookieForTesting(
192 const std::string& name,
193 const std::string& value,
194 const std::string& domain,
195 const std::string& path,
196 const base::Time& creation,
197 const base::Time& expiration,
198 const base::Time& last_access,
199 const base::Time& last_update,
200 bool secure,
201 bool httponly,
202 CookieSameSite same_site,
203 CookiePriority priority,
204 absl::optional<CookiePartitionKey> partition_key = absl::nullopt,
205 CookieSourceScheme scheme_secure = CookieSourceScheme::kUnset,
206 int source_port = url::PORT_UNSPECIFIED);
207
208 bool operator<(const CanonicalCookie& other) const {
209 // Use the cookie properties that uniquely identify a cookie to determine
210 // ordering.
211 return UniqueKey() < other.UniqueKey();
212 }
213
214 bool operator==(const CanonicalCookie& other) const {
215 return IsEquivalent(other);
216 }
217
Name()218 const std::string& Name() const { return name_; }
Value()219 const std::string& Value() const { return value_; }
220 // We represent the cookie's host-only-flag as the absence of a leading dot in
221 // Domain(). See IsDomainCookie() and IsHostCookie() below.
222 // If you want the "cookie's domain" as described in RFC 6265bis, use
223 // DomainWithoutDot().
Domain()224 const std::string& Domain() const { return domain_; }
Path()225 const std::string& Path() const { return path_; }
CreationDate()226 const base::Time& CreationDate() const { return creation_date_; }
ExpiryDate()227 const base::Time& ExpiryDate() const { return expiry_date_; }
LastAccessDate()228 const base::Time& LastAccessDate() const { return last_access_date_; }
LastUpdateDate()229 const base::Time& LastUpdateDate() const { return last_update_date_; }
IsPersistent()230 bool IsPersistent() const { return !expiry_date_.is_null(); }
IsSecure()231 bool IsSecure() const { return secure_; }
IsHttpOnly()232 bool IsHttpOnly() const { return httponly_; }
SameSite()233 CookieSameSite SameSite() const { return same_site_; }
Priority()234 CookiePriority Priority() const { return priority_; }
IsPartitioned()235 bool IsPartitioned() const { return partition_key_.has_value(); }
PartitionKey()236 const absl::optional<CookiePartitionKey>& PartitionKey() const {
237 return partition_key_;
238 }
239
240 // Returns an enum indicating the scheme of the origin that
241 // set this cookie. This is not part of the cookie spec but is being used to
242 // collect metrics for a potential change to the cookie spec
243 // (https://tools.ietf.org/html/draft-west-cookie-incrementalism-01#section-3.4)
SourceScheme()244 CookieSourceScheme SourceScheme() const { return source_scheme_; }
245 // Returns the port of the origin that originally set this cookie (the
246 // source port). This is not part of the cookie spec but is being used to
247 // collect metrics for a potential change to the cookie spec.
SourcePort()248 int SourcePort() const { return source_port_; }
IsDomainCookie()249 bool IsDomainCookie() const {
250 return !domain_.empty() && domain_[0] == '.'; }
IsHostCookie()251 bool IsHostCookie() const { return !IsDomainCookie(); }
252
253 // Returns whether this cookie is Partitioned and its partition key matches a
254 // a same-site context by checking if the cookies domain site is the same as
255 // the partition key's site.
256 // Returns false if the cookie has no partition key.
257 bool IsFirstPartyPartitioned() const;
258
259 // Returns whether the cookie is partitioned in a third-party context.
260 bool IsThirdPartyPartitioned() const;
261
262 // Returns the cookie's domain, with the leading dot removed, if present.
263 // This corresponds to the "cookie's domain" as described in RFC 6265bis.
264 std::string DomainWithoutDot() const;
265
IsExpired(const base::Time & current)266 bool IsExpired(const base::Time& current) const {
267 return !expiry_date_.is_null() && current >= expiry_date_;
268 }
269
270 // Are the cookies considered equivalent in the eyes of RFC 2965.
271 // The RFC says that name must match (case-sensitive), domain must
272 // match (case insensitive), and path must match (case sensitive).
273 // For the case insensitive domain compare, we rely on the domain
274 // having been canonicalized (in
275 // GetCookieDomainWithString->CanonicalizeHost).
276 // If partitioned cookies are enabled, then we check the cookies have the same
277 // partition key in addition to the checks in RFC 2965.
IsEquivalent(const CanonicalCookie & ecc)278 bool IsEquivalent(const CanonicalCookie& ecc) const {
279 // It seems like it would make sense to take secure, httponly, and samesite
280 // into account, but the RFC doesn't specify this.
281 // NOTE: Keep this logic in-sync with TrimDuplicateCookiesForKey().
282 return UniqueKey() == ecc.UniqueKey();
283 }
284
StrictlyUniqueKey()285 StrictlyUniqueCookieKey StrictlyUniqueKey() const {
286 return std::make_tuple(partition_key_, name_, domain_, path_,
287 source_scheme_, source_port_);
288 }
289
290 // Returns a key such that two cookies with the same UniqueKey() are
291 // guaranteed to be equivalent in the sense of IsEquivalent().
292 // The `partition_key_` field will always be nullopt when partitioned cookies
293 // are not enabled.
294 // The source_scheme and source_port fields depend on whether or not their
295 // associated features are enabled.
296 UniqueCookieKey UniqueKey() const;
297
298 // Checks a looser set of equivalency rules than 'IsEquivalent()' in order
299 // to support the stricter 'Secure' behaviors specified in Step 12 of
300 // https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-05#section-5.4
301 // which originated from the proposal in
302 // https://tools.ietf.org/html/draft-ietf-httpbis-cookie-alone#section-3
303 //
304 // Returns 'true' if this cookie's name matches |secure_cookie|, and this
305 // cookie is a domain-match for |secure_cookie| (or vice versa), and
306 // |secure_cookie|'s path is "on" this cookie's path (as per 'IsOnPath()').
307 // If partitioned cookies are enabled, it also checks that the cookie has
308 // the same partition key as |secure_cookie|.
309 //
310 // Note that while the domain-match cuts both ways (e.g. 'example.com'
311 // matches 'www.example.com' in either direction), the path-match is
312 // unidirectional (e.g. '/login/en' matches '/login' and '/', but
313 // '/login' and '/' do not match '/login/en').
314 //
315 // Conceptually:
316 // If new_cookie.IsEquivalentForSecureCookieMatching(secure_cookie) is true,
317 // this means that new_cookie would "shadow" secure_cookie: they would would
318 // be indistinguishable when serialized into a Cookie header. This is
319 // important because, if an attacker is attempting to set new_cookie, it
320 // should not be allowed to mislead the server into using new_cookie's value
321 // instead of secure_cookie's.
322 //
323 // The reason for the asymmetric path comparison ("cookie1=bad; path=/a/b"
324 // from an insecure source is not allowed if "cookie1=good; secure; path=/a"
325 // exists, but "cookie2=bad; path=/a" from an insecure source is allowed if
326 // "cookie2=good; secure; path=/a/b" exists) is because cookies in the Cookie
327 // header are serialized with longer path first. (See CookieSorter in
328 // cookie_monster.cc.) That is, they would be serialized as "Cookie:
329 // cookie1=bad; cookie1=good" in one case, and "Cookie: cookie2=good;
330 // cookie2=bad" in the other case. The first scenario is not allowed because
331 // the attacker injects the bad value, whereas the second scenario is ok
332 // because the good value is still listed first.
333 bool IsEquivalentForSecureCookieMatching(
334 const CanonicalCookie& secure_cookie) const;
335
336 // Returns true if the |other| cookie's data members (instance variables)
337 // match, for comparing cookies in colletions.
HasEquivalentDataMembers(const CanonicalCookie & other)338 bool HasEquivalentDataMembers(const CanonicalCookie& other) const {
339 return creation_date_ == other.creation_date_ &&
340 last_access_date_ == other.last_access_date_ &&
341 expiry_date_ == other.expiry_date_ && secure_ == other.secure_ &&
342 httponly_ == other.httponly_ && same_site_ == other.same_site_ &&
343 priority_ == other.priority_ &&
344 partition_key_ == other.partition_key_ && name_ == other.name_ &&
345 value_ == other.value_ && domain_ == other.domain_ &&
346 path_ == other.path_ &&
347 last_update_date_ == other.last_update_date_ &&
348 source_scheme_ == other.source_scheme_ &&
349 source_port_ == other.source_port_;
350 }
351
352 // Similar to operator<, but considers all data members.
DataMembersPrecede(const CanonicalCookie & other)353 bool DataMembersPrecede(const CanonicalCookie& other) const {
354 auto f = [](const CanonicalCookie& c) {
355 return std::tie(c.creation_date_, c.last_access_date_, c.expiry_date_,
356 c.secure_, c.httponly_, c.same_site_, c.priority_,
357 c.partition_key_, c.name_, c.value_, c.domain_, c.path_,
358 c.last_update_date_, c.source_scheme_, c.source_port_);
359 };
360 return f(*this) < f(other);
361 }
362
SetSourceScheme(CookieSourceScheme source_scheme)363 void SetSourceScheme(CookieSourceScheme source_scheme) {
364 source_scheme_ = source_scheme;
365 }
366
367 // Set the source port value. Performs a range check and sets the port to
368 // url::PORT_INVALID if value isn't in [0,65535] or url::PORT_UNSPECIFIED.
369 void SetSourcePort(int port);
370
SetLastAccessDate(const base::Time & date)371 void SetLastAccessDate(const base::Time& date) {
372 last_access_date_ = date;
373 }
SetCreationDate(const base::Time & date)374 void SetCreationDate(const base::Time& date) { creation_date_ = date; }
375
376 // Returns true if the given |url_path| path-matches this cookie's cookie-path
377 // as described in section 5.1.4 in RFC 6265. This returns true if |path_| and
378 // |url_path| are identical, or if |url_path| is a subdirectory of |path_|.
379 bool IsOnPath(const std::string& url_path) const;
380
381 // This returns true if this cookie's |domain_| indicates that it can be
382 // accessed by |host|.
383 //
384 // In the case where |domain_| has no leading dot, this is a host cookie and
385 // will only domain match if |host| is identical to |domain_|.
386 //
387 // In the case where |domain_| has a leading dot, this is a domain cookie. It
388 // will match |host| if |domain_| is a suffix of |host|, or if |domain_| is
389 // exactly equal to |host| plus a leading dot.
390 //
391 // Note that this isn't quite the same as the "domain-match" algorithm in RFC
392 // 6265bis, since our implementation uses the presence of a leading dot in the
393 // |domain_| string in place of the spec's host-only-flag. That is, if
394 // |domain_| has no leading dot, then we only consider it matching if |host|
395 // is identical (which reflects the intended behavior when the cookie has a
396 // host-only-flag), whereas the RFC also treats them as domain-matching if
397 // |domain_| is a subdomain of |host|.
398 bool IsDomainMatch(const std::string& host) const;
399
400 // Returns if the cookie should be included (and if not, why) for the given
401 // request |url| using the CookieInclusionStatus enum. HTTP only cookies can
402 // be filter by using appropriate cookie |options|.
403 //
404 // PLEASE NOTE that this method does not check whether a cookie is expired or
405 // not!
406 CookieAccessResult IncludeForRequestURL(
407 const GURL& url,
408 const CookieOptions& options,
409 const CookieAccessParams& params) const;
410
411 // Returns if the cookie with given attributes can be set in context described
412 // by |options| and |params|, and if no, describes why.
413 //
414 // |cookie_access_result| is an optional input status, to allow for status
415 // chaining from callers. It helps callers provide the status of a
416 // canonical cookie that may have warnings associated with it.
417 CookieAccessResult IsSetPermittedInContext(
418 const GURL& source_url,
419 const CookieOptions& options,
420 const CookieAccessParams& params,
421 const std::vector<std::string>& cookieable_schemes,
422 const absl::optional<CookieAccessResult>& cookie_access_result =
423 absl::nullopt) const;
424
425 std::string DebugString() const;
426
427 // Returns the canonical path based on the specified url and path attribute
428 // value. Note that this method does not enforce character set or size
429 // checks on `path_string`.
430 static std::string CanonPathWithString(const GURL& url,
431 const std::string& path_string);
432
433 // Returns a "null" time if expiration was unspecified or invalid.
434 static base::Time ParseExpiration(const ParsedCookie& pc,
435 const base::Time& current,
436 const base::Time& server_time);
437
438 // Per rfc6265bis the maximum expiry date is no further than 400 days in the
439 // future.
440 static base::Time ValidateAndAdjustExpiryDate(
441 const base::Time& expiry_date,
442 const base::Time& creation_date);
443
444 // Cookie ordering methods.
445
446 // Returns true if the cookie is less than |other|, considering only name,
447 // domain and path. In particular, two equivalent cookies (see IsEquivalent())
448 // are identical for PartialCompare().
449 bool PartialCompare(const CanonicalCookie& other) const;
450
451 // Return whether this object is a valid CanonicalCookie(). Invalid
452 // cookies may be constructed by the detailed constructor.
453 // A cookie is considered canonical if-and-only-if:
454 // * It can be created by CanonicalCookie::Create, or
455 // * It is identical to a cookie created by CanonicalCookie::Create except
456 // that the creation time is null, or
457 // * It can be derived from a cookie created by CanonicalCookie::Create by
458 // entry into and retrieval from a cookie store (specifically, this means
459 // by the setting of an creation time in place of a null creation time, and
460 // the setting of a last access time).
461 // An additional requirement on a CanonicalCookie is that if the last
462 // access time is non-null, the creation time must also be non-null and
463 // greater than the last access time.
464 bool IsCanonical() const;
465
466 // Return whether this object is a valid CanonicalCookie() when retrieving the
467 // cookie from the persistent store. Cookie that exist in the persistent store
468 // may have been created before more recent changes to the definition of
469 // "canonical". To ease the transition to the new definitions, and to prevent
470 // users from having their cookies deleted, this function supports the older
471 // definition of canonical. This function is intended to be temporary because
472 // as the number of older cookies (which are non-compliant with the newer
473 // definition of canonical) decay toward zero it can eventually be replaced
474 // by `IsCanonical()` to enforce the newer definition of canonical.
475 //
476 // A cookie is considered canonical by this function if-and-only-if:
477 // * It is considered canonical by IsCanonical()
478 // * TODO(crbug.com/1244172): Add exceptions once IsCanonical() starts
479 // enforcing them.
480 bool IsCanonicalForFromStorage() const;
481
482 // Returns whether the effective SameSite mode is SameSite=None (i.e. no
483 // SameSite restrictions).
484 bool IsEffectivelySameSiteNone(CookieAccessSemantics access_semantics =
485 CookieAccessSemantics::UNKNOWN) const;
486
487 CookieEffectiveSameSite GetEffectiveSameSiteForTesting(
488 CookieAccessSemantics access_semantics =
489 CookieAccessSemantics::UNKNOWN) const;
490
491 // Returns the cookie line (e.g. "cookie1=value1; cookie2=value2") represented
492 // by |cookies|. The string is built in the same order as the given list.
493 static std::string BuildCookieLine(const CookieList& cookies);
494
495 // Same as above but takes a CookieAccessResultList
496 // (ignores the access result).
497 static std::string BuildCookieLine(const CookieAccessResultList& cookies);
498
499 // Takes a single CanonicalCookie and returns a cookie line containing the
500 // attributes of |cookie| formatted like a http set cookie header.
501 // (e.g. "cookie1=value1; domain=abc.com; path=/; secure").
502 static std::string BuildCookieAttributesLine(const CanonicalCookie& cookie);
503
504 private:
505 FRIEND_TEST_ALL_PREFIXES(CanonicalCookieTest,
506 TestGetAndAdjustPortForTrustworthyUrls);
507 FRIEND_TEST_ALL_PREFIXES(CanonicalCookieTest, TestPrefixHistograms);
508 FRIEND_TEST_ALL_PREFIXES(CanonicalCookieTest, TestHasHiddenPrefixName);
509
510 // The special cookie prefixes as defined in
511 // https://tools.ietf.org/html/draft-west-cookie-prefixes
512 //
513 // This enum is being histogrammed; do not reorder or remove values.
514 enum CookiePrefix {
515 COOKIE_PREFIX_NONE = 0,
516 COOKIE_PREFIX_SECURE,
517 COOKIE_PREFIX_HOST,
518 COOKIE_PREFIX_LAST
519 };
520
521 // Returns the CookiePrefix (or COOKIE_PREFIX_NONE if none) that
522 // applies to the given cookie |name|.
GetCookiePrefix(const std::string & name)523 static CookiePrefix GetCookiePrefix(const std::string& name) {
524 return GetCookiePrefix(name,
525 base::FeatureList::IsEnabled(
526 net::features::kCaseInsensitiveCookiePrefix));
527 }
528
529 // Returns the CookiePrefix (or COOKIE_PREFIX_NONE if none) that
530 // applies to the given cookie |name|. If `check_insensitively` is true then
531 // the string comparison will be performed case insensitively.
532 static CookiePrefix GetCookiePrefix(const std::string& name,
533 bool check_insensitively);
534 // Records histograms to measure how often cookie prefixes appear in
535 // the wild and how often they would be blocked.
536 static void RecordCookiePrefixMetrics(CookiePrefix prefix_case_sensitive,
537 CookiePrefix prefix_case_insensitive,
538 bool is_insensitive_prefix_valid);
539 // Returns true if a prefixed cookie does not violate any of the rules
540 // for that cookie.
541 static bool IsCookiePrefixValid(CookiePrefix prefix,
542 const GURL& url,
543 const ParsedCookie& parsed_cookie);
544 static bool IsCookiePrefixValid(CookiePrefix prefix,
545 const GURL& url,
546 bool secure,
547 const std::string& domain,
548 const std::string& path);
549
550 // Returns the effective SameSite mode to apply to this cookie. Depends on the
551 // value of the given SameSite attribute and the access semantics of the
552 // cookie.
553 // Note: If you are converting to a different representation of a cookie, you
554 // probably want to use SameSite() instead of this method. Otherwise, if you
555 // are considering using this method, consider whether you should use
556 // IncludeForRequestURL() or IsSetPermittedInContext() instead of doing the
557 // SameSite computation yourself.
558 CookieEffectiveSameSite GetEffectiveSameSite(
559 CookieAccessSemantics access_semantics) const;
560
561 // Returns the appropriate port value for the given `source_url` depending on
562 // if the url is considered trustworthy or not.
563 //
564 // This function normally returns source_url.EffectiveIntPort(), but it can
565 // return a different port value if:
566 // * `source_url`'s scheme isn't cryptographically secure
567 // * `url_is_trustworthy` is true
568 // * `source_url`'s port is the default port for the scheme i.e.: 80
569 // If all these conditions are true then the returned value will be 443 to
570 // indicate that we're treating `source_url` as if it was secure.
571 static int GetAndAdjustPortForTrustworthyUrls(const GURL& source_url,
572 bool url_is_trustworthy);
573
574 // Checks for values that could be misinterpreted as a cookie name prefix.
575 static bool HasHiddenPrefixName(const base::StringPiece cookie_value);
576
577 // Returns whether the cookie was created at most |age_threshold| ago.
578 bool IsRecentlyCreated(base::TimeDelta age_threshold) const;
579
580 // Returns true iff the cookie is a partitioned cookie with a nonce or that
581 // does not violate the semantics of the Partitioned attribute:
582 // - Must have the Secure attribute OR the cookie partition contains a nonce.
583 static bool IsCookiePartitionedValid(const GURL& url,
584 const ParsedCookie& parsed_cookie,
585 bool partition_has_nonce);
586 static bool IsCookiePartitionedValid(const GURL& url,
587 bool secure,
588 bool is_partitioned,
589 bool partition_has_nonce);
590
591 // Keep defaults here in sync with
592 // services/network/public/interfaces/cookie_manager.mojom.
593 std::string name_;
594 std::string value_;
595 std::string domain_;
596 std::string path_;
597 base::Time creation_date_;
598 base::Time expiry_date_;
599 base::Time last_access_date_;
600 base::Time last_update_date_;
601 bool secure_{false};
602 bool httponly_{false};
603 CookieSameSite same_site_{CookieSameSite::NO_RESTRICTION};
604 CookiePriority priority_{COOKIE_PRIORITY_MEDIUM};
605 // This will be absl::nullopt for all cookies not set with the Partitioned
606 // attribute or without a nonce. If the value is non-null, then the cookie
607 // will only be delivered when the top-frame site matches the partition key
608 // and the nonce (if present). If the partition key is non-null and opaque,
609 // this means the Partitioned cookie was created on an opaque origin or with
610 // a nonce.
611 absl::optional<CookiePartitionKey> partition_key_;
612 CookieSourceScheme source_scheme_{CookieSourceScheme::kUnset};
613 // This can be [0,65535], PORT_UNSPECIFIED, or PORT_INVALID.
614 // PORT_UNSPECIFIED is used for cookies which already existed in the cookie
615 // store prior to this change and therefore their port is unknown.
616 // PORT_INVALID is an error for when an out of range port is provided.
617 int source_port_{url::PORT_UNSPECIFIED};
618 };
619
620 // Used to pass excluded cookie information when it's possible that the
621 // canonical cookie object may not be available.
622 struct NET_EXPORT CookieAndLineWithAccessResult {
623 CookieAndLineWithAccessResult();
624 CookieAndLineWithAccessResult(absl::optional<CanonicalCookie> cookie,
625 std::string cookie_string,
626 CookieAccessResult access_result);
627 CookieAndLineWithAccessResult(
628 const CookieAndLineWithAccessResult& cookie_and_line_with_access_result);
629
630 CookieAndLineWithAccessResult& operator=(
631 const CookieAndLineWithAccessResult& cookie_and_line_with_access_result);
632
633 CookieAndLineWithAccessResult(
634 CookieAndLineWithAccessResult&& cookie_and_line_with_access_result);
635
636 ~CookieAndLineWithAccessResult();
637
638 absl::optional<CanonicalCookie> cookie;
639 std::string cookie_string;
640 CookieAccessResult access_result;
641 };
642
643 struct CookieWithAccessResult {
644 CanonicalCookie cookie;
645 CookieAccessResult access_result;
646 };
647
648 // Provided to allow gtest to create more helpful error messages, instead of
649 // printing hex.
PrintTo(const CanonicalCookie & cc,std::ostream * os)650 inline void PrintTo(const CanonicalCookie& cc, std::ostream* os) {
651 *os << "{ name=" << cc.Name() << ", value=" << cc.Value() << " }";
652 }
PrintTo(const CookieWithAccessResult & cwar,std::ostream * os)653 inline void PrintTo(const CookieWithAccessResult& cwar, std::ostream* os) {
654 *os << "{ ";
655 PrintTo(cwar.cookie, os);
656 *os << ", ";
657 PrintTo(cwar.access_result, os);
658 *os << " }";
659 }
PrintTo(const CookieAndLineWithAccessResult & calwar,std::ostream * os)660 inline void PrintTo(const CookieAndLineWithAccessResult& calwar,
661 std::ostream* os) {
662 *os << "{ ";
663 if (calwar.cookie) {
664 PrintTo(*calwar.cookie, os);
665 } else {
666 *os << "nullopt";
667 }
668 *os << ", " << calwar.cookie_string << ", ";
669 PrintTo(calwar.access_result, os);
670 *os << " }";
671 }
672
673 } // namespace net
674
675 #endif // NET_COOKIES_CANONICAL_COOKIE_H_
676