blob: 78f2c8a53416c6edd83216d986dfc4ff3e13c166 [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef NET_COOKIES_COOKIE_BASE_H_
#define NET_COOKIES_COOKIE_BASE_H_
#include <optional>
#include <string>
#include <tuple>
#include "base/types/pass_key.h"
#include "net/base/net_export.h"
#include "net/cookies/cookie_access_result.h"
#include "net/cookies/cookie_constants.h"
#include "net/cookies/cookie_options.h"
#include "net/cookies/cookie_partition_key.h"
class GURL;
namespace net {
class RefUniqueCookieKey;
class UniqueCookieKey;
struct CookieAccessParams;
struct CookieAccessResult;
// A base class for cookies and cookie-like objects. Encapsulates logic for
// determining whether a cookie could be sent/set, based on its attributes and
// the request context.
class NET_EXPORT CookieBase {
public:
// Returns if the cookie should be included (and if not, why) for the given
// request |url| using the CookieInclusionStatus enum. HTTP only cookies can
// be filter by using appropriate cookie |options|.
//
// PLEASE NOTE that this method does not check whether a cookie is expired or
// not!
CookieAccessResult IncludeForRequestURL(
const GURL& url,
const CookieOptions& options,
const CookieAccessParams& params) const;
// Returns if the cookie with given attributes can be set in context described
// by |options| and |params|, and if no, describes why.
//
// |cookie_access_result| is an optional input status, to allow for status
// chaining from callers. It helps callers provide the status of a
// cookie that may have warnings associated with it.
CookieAccessResult IsSetPermittedInContext(
const GURL& source_url,
const CookieOptions& options,
const CookieAccessParams& params,
const std::vector<std::string>& cookieable_schemes,
const std::optional<CookieAccessResult>& cookie_access_result =
std::nullopt) const;
// Returns true if the given |url_path| path-matches this cookie's cookie-path
// as described in section 5.1.4 in RFC 6265. This returns true if |path_| and
// |url_path| are identical, or if |url_path| is a subdirectory of |path_|.
bool IsOnPath(const std::string& url_path) const;
// This returns true if this cookie's |domain_| indicates that it can be
// accessed by |host|.
//
// In the case where |domain_| has no leading dot, this is a host cookie and
// will only domain match if |host| is identical to |domain_|.
//
// In the case where |domain_| has a leading dot, this is a domain cookie. It
// will match |host| if |domain_| is a suffix of |host|, or if |domain_| is
// exactly equal to |host| plus a leading dot.
//
// Note that this isn't quite the same as the "domain-match" algorithm in RFC
// 6265bis, since our implementation uses the presence of a leading dot in the
// |domain_| string in place of the spec's host-only-flag. That is, if
// |domain_| has no leading dot, then we only consider it matching if |host|
// is identical (which reflects the intended behavior when the cookie has a
// host-only-flag), whereas the RFC also treats them as domain-matching if
// |domain_| is a subdomain of |host|.
bool IsDomainMatch(const std::string& host) const;
const std::string& Name() const { return name_; }
// We represent the cookie's host-only-flag as the absence of a leading dot in
// Domain(). See IsDomainCookie() and IsHostCookie() below.
// If you want the "cookie's domain" as described in RFC 6265bis, use
// DomainWithoutDot().
const std::string& Domain() const { return domain_; }
const std::string& Path() const { return path_; }
base::Time CreationDate() const { return creation_date_; }
bool SecureAttribute() const { return secure_; }
bool IsHttpOnly() const { return httponly_; }
CookieSameSite SameSite() const { return same_site_; }
// Returns true if this cookie can only be accessed in a secure context.
bool IsSecure() const;
bool IsPartitioned() const { return partition_key_.has_value(); }
const std::optional<CookiePartitionKey>& PartitionKey() const {
return partition_key_;
}
// Returns whether this cookie is Partitioned and its partition key matches a
// a same-site context by checking if the cookies domain site is the same as
// the partition key's site.
// This function should not be used for third-party cookie blocking
// enforcement-related decisions. That logic should rely on `IsPartitioned`.
// These functions are for recording metrics about partitioned cookie usage.
// Returns false if the cookie has no partition key.
bool IsFirstPartyPartitioned() const;
// Returns whether the cookie is partitioned in a third-party context.
// This function should not be used for third-party cookie blocking
// enforcement-related decisions. That logic should rely on `IsPartitioned`.
// These functions are for recording metrics about partitioned cookie usage.
// Returns false if the cookie has no partition key.
bool IsThirdPartyPartitioned() const;
// Returns an enum indicating the scheme of the origin that
// set this cookie. This is not part of the cookie spec but is being used to
// collect metrics for a potential change to the cookie spec
// (https://tools.ietf.org/html/draft-west-cookie-incrementalism-01#section-3.4)
CookieSourceScheme SourceScheme() const { return source_scheme_; }
// Returns the port of the origin that originally set this cookie (the
// source port). This is not part of the cookie spec but is being used to
// collect metrics for a potential change to the cookie spec.
int SourcePort() const { return source_port_; }
bool IsDomainCookie() const { return !domain_.empty() && domain_[0] == '.'; }
bool IsHostCookie() const { return !IsDomainCookie(); }
// Returns the cookie's domain, with the leading dot removed, if present.
// This corresponds to the "cookie's domain" as described in RFC 6265bis.
std::string DomainWithoutDot() const;
// StrictlyUniqueKey always includes the cookie's source scheme and source
// port.
UniqueCookieKey StrictlyUniqueKey() const;
// Returns a key such that two cookies with the same UniqueKey() are
// guaranteed to be equivalent in the sense of IsEquivalent().
// The `partition_key_` field will always be nullopt when partitioned cookies
// are not enabled.
// The source_scheme and source_port fields depend on whether or not their
// associated features are enabled.
UniqueCookieKey UniqueKey() const;
// Returns a non-owning key such that two cookies with the same RefUniqueKey()
// are guaranteed to be equivalent in the sense of IsEquivalent().
// The `partition_key_` field will always be nullopt when partitioned cookies
// are not enabled.
// The source_scheme and source_port fields depend on whether or not their
// associated features are enabled.
// A RefUniqueKey keeps references that point to data in the CookieBase, so it
// must not be stored beyond the lifetime of the CookieBase.
RefUniqueCookieKey RefUniqueKey() const;
// Same as UniqueKey() except it does not contain a source_port or
// source_scheme field. For use for determining aliasing cookies, which do not
// consider the source_port or source_scheme.
UniqueCookieKey LegacyUniqueKey() const;
void SetSourceScheme(CookieSourceScheme source_scheme) {
source_scheme_ = source_scheme;
}
// Set the source port value. Performs a range check and sets the port to
// url::PORT_INVALID if value isn't in [0,65535] or url::PORT_UNSPECIFIED.
void SetSourcePort(int port);
void SetCreationDate(base::Time date) { creation_date_ = date; }
protected:
CookieBase();
CookieBase(const CookieBase& other);
CookieBase(CookieBase&& other);
CookieBase& operator=(const CookieBase& other);
CookieBase& operator=(CookieBase&& other);
virtual ~CookieBase();
CookieBase(std::string name,
std::string domain,
std::string path,
base::Time creation,
bool secure,
bool httponly,
CookieSameSite same_site,
std::optional<CookiePartitionKey> partition_key,
CookieSourceScheme source_scheme = CookieSourceScheme::kUnset,
int source_port = url::PORT_UNSPECIFIED);
// Returns the effective SameSite mode to apply to this cookie. Depends on the
// value of the given SameSite attribute and the access semantics of the
// cookie.
// Note: If you are converting to a different representation of a cookie, you
// probably want to use SameSite() instead of this method. Otherwise, if you
// are considering using this method, consider whether you should use
// IncludeForRequestURL() or IsSetPermittedInContext() instead of doing the
// SameSite computation yourself.
CookieEffectiveSameSite GetEffectiveSameSite(
CookieAccessSemantics access_semantics) const;
// Returns the threshold age for lax-allow-unsafe behavior, below which the
// effective SameSite behavior for a cookie that does not specify SameSite is
// lax-allow-unsafe, and above which the effective SameSite is just lax.
// Lax-allow-unsafe behavior (a.k.a. Lax+POST) is a temporary mitigation for
// compatibility reasons that allows a cookie which doesn't specify SameSite
// to still be sent on non-safe requests like POST requests for a short amount
// of time after creation, despite the default enforcement for most (i.e.
// older) SameSite-unspecified cookies being Lax. Implementations should
// override this method if they want to enable Lax-allow-unsafe behavior; by
// default, this method returns base::TimeDelta::Min(), i.e. no cookies will
// ever be lax-allow-unsafe.
virtual base::TimeDelta GetLaxAllowUnsafeThresholdAge() const;
// Returns whether the cookie was created at most |age_threshold| ago.
bool IsRecentlyCreated(base::TimeDelta age_threshold) const;
// Checks if `port` is within [0,65535] or url::PORT_UNSPECIFIED. Returns
// `port` if so and url::PORT_INVALID otherwise.
static int ValidateAndAdjustSourcePort(int port);
private:
// Allows subclasses to add custom logic for e.g. logging metrics. Called
// after inclusion has been determined for the respective access.
virtual void PostIncludeForRequestURL(
const CookieAccessResult& access_result,
const CookieOptions& options_used,
CookieOptions::SameSiteCookieContext::ContextType
cookie_inclusion_context_used) const {}
virtual void PostIsSetPermittedInContext(
const CookieAccessResult& access_result,
const CookieOptions& options_used) const {}
// Keep defaults here in sync with
// services/network/public/interfaces/cookie_manager.mojom.
std::string name_;
std::string domain_;
std::string path_;
base::Time creation_date_;
bool secure_{false};
bool httponly_{false};
CookieSameSite same_site_{CookieSameSite::NO_RESTRICTION};
// This will be std::nullopt for all cookies not set with the Partitioned
// attribute or without a nonce. If the value is non-null, then the cookie
// will only be delivered when the top-frame site matches the partition key
// and the nonce (if present). If the partition key is non-null and opaque,
// this means the Partitioned cookie was created on an opaque origin or with
// a nonce.
std::optional<CookiePartitionKey> partition_key_;
CookieSourceScheme source_scheme_{CookieSourceScheme::kUnset};
// This can be [0,65535], PORT_UNSPECIFIED, or PORT_INVALID.
// PORT_UNSPECIFIED is used for cookies which already existed in the cookie
// store prior to this change and therefore their port is unknown.
// PORT_INVALID is an error for when an out of range port is provided.
int source_port_{url::PORT_UNSPECIFIED};
};
} // namespace net
#endif // NET_COOKIES_COOKIE_BASE_H_