| // Copyright 2012 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // Brought to you by number 42. |
| |
| #ifndef NET_COOKIES_COOKIE_OPTIONS_H_ |
| #define NET_COOKIES_COOKIE_OPTIONS_H_ |
| |
| #include <ostream> |
| #include <string> |
| |
| #include "net/base/net_export.h" |
| #include "net/cookies/cookie_constants.h" |
| #include "net/cookies/cookie_inclusion_status.h" |
| #include "net/first_party_sets/same_party_context.h" |
| #include "url/gurl.h" |
| |
| namespace net { |
| |
| class NET_EXPORT CookieOptions { |
| public: |
| |
| // Relation between the cookie and the navigational environment. |
| class NET_EXPORT SameSiteCookieContext { |
| public: |
| // CROSS_SITE to SAME_SITE_STRICT are ordered from least to most trusted |
| // environment. Don't renumber, used in histograms. |
| enum class ContextType { |
| CROSS_SITE = 0, |
| // Same rules as lax but the http method is unsafe. |
| SAME_SITE_LAX_METHOD_UNSAFE = 1, |
| SAME_SITE_LAX = 2, |
| SAME_SITE_STRICT = 3, |
| |
| // Keep last, used for histograms. |
| COUNT |
| }; |
| |
| // Holds metadata about the factors that went into deciding the ContextType. |
| // |
| // These values may be used for recording histograms or |
| // CookieInclusionStatus warnings, but SHOULD NOT be relied |
| // upon for cookie inclusion decisions. Use only the ContextTypes for that. |
| // |
| // When adding a field, also update CompleteEquivalenceForTesting. |
| struct NET_EXPORT ContextMetadata { |
| // Possible "downgrades" for the SameSite context type, e.g. from a more |
| // trusted context to a less trusted context, as a result of some behavior |
| // change affecting the same-site calculation. |
| enum class ContextDowngradeType { |
| // Context not downgraded. |
| kNoDowngrade, |
| // Context was originally strictly same-site, was downgraded to laxly |
| // same-site. |
| kStrictToLax, |
| // Context was originally strictly same-site, was downgraded to |
| // cross-site. |
| kStrictToCross, |
| // Context was originally laxly same-site, was downgraded to cross-site. |
| kLaxToCross, |
| }; |
| |
| // These values are persisted to logs. Entries should not be renumbered |
| // and numeric values should never be reused. |
| // This enum is to help collect metrics for https://crbug.com/1221316. |
| // Specifically it's to help indicate how many cookies are accessed with a |
| // given redirect type in order to provide a denominator for the |
| // Cookie.CrossSiteRedirectDowngradeChangesInclusion2.* metrics. |
| // Note: for this enum the notation A->B->C means that a navigation from |
| // A to B was redirected to C. I.e.: A is the initator of the navigation |
| // and C is the final url. |
| enum class ContextRedirectTypeBug1221316 { |
| kUnset = |
| 0, // Indicates this value was unused and shouldn't be read. E.x.: |
| // A javascript access means this value is meaningless. |
| kNoRedirect = 1, // There weren't any redirects. E.x.: A->B, A->A |
| kCrossSiteRedirect = |
| 2, // There was a redirect but it didn't start and |
| // end at the same site or the redirect was to a different site |
| // than the site-for-cookies. E.x.: A->B->C or B->B->B when the |
| // site-for-cookies is A. |
| kPartialSameSiteRedirect = |
| 3, // There was a redirect and the start and |
| // end are the same site. E.x.: A->B->A. Only this one could |
| // potentially set cross_site_redirect_downgrade. |
| kAllSameSiteRedirect = 4, // There was a redirect and all urls are the |
| // same site. E.x.:, A->A->A |
| kMaxValue = kAllSameSiteRedirect |
| }; |
| |
| // These values are persisted to logs. Entries should not be renumbered |
| // and numeric values should never be reused. |
| enum class HttpMethod { |
| // kUnset indicates this enum wasn't applicable in the context. |
| kUnset = -1, |
| // kUnknown indicates we were unable to convert the method string to |
| // this enum. |
| kUnknown = 0, |
| kGet = 1, |
| kHead = 2, |
| kPost = 3, |
| KPut = 4, |
| kDelete = 5, |
| kConnect = 6, |
| kOptions = 7, |
| kTrace = 8, |
| kPatch = 9, |
| kMaxValue = kPatch |
| }; |
| |
| // Records the type of any context downgrade due to a cross-site redirect, |
| // i.e. whether the spec change in |
| // https://github.com/httpwg/http-extensions/pull/1348 changed the result |
| // of the context calculation. Note that a lax-to-cross downgrade can only |
| // happen for response cookies, because a laxly same-site context only |
| // happens for a top-level cross-site request, which cannot be downgraded |
| // due to a cross-site redirect to a non-top-level cross-site request. |
| // This only records whether the context was downgraded, not whether the |
| // cookie's inclusion result was changed. |
| ContextDowngradeType cross_site_redirect_downgrade = |
| ContextDowngradeType::kNoDowngrade; |
| |
| ContextRedirectTypeBug1221316 redirect_type_bug_1221316 = |
| ContextRedirectTypeBug1221316::kUnset; |
| |
| // Records the HTTP method of requests that result in a cross-site |
| // redirect downgrade. May be kUnset if there wasn't a downgrade or if the |
| // cookie access wasn't due to a request. |
| // |
| // Note that this field is always set when there was a context |
| // downgrade but the associated histrogram is only recorded when that |
| // context downgrade results in a change in inclusion status. |
| HttpMethod http_method_bug_1221316 = HttpMethod::kUnset; |
| }; |
| |
| // The following three constructors apply default values for the metadata |
| // members. |
| SameSiteCookieContext() |
| : SameSiteCookieContext(ContextType::CROSS_SITE, |
| ContextType::CROSS_SITE) {} |
| |
| explicit SameSiteCookieContext(ContextType same_site_context) |
| : SameSiteCookieContext(same_site_context, same_site_context) {} |
| |
| SameSiteCookieContext(ContextType same_site_context, |
| ContextType schemeful_same_site_context) |
| : SameSiteCookieContext(same_site_context, |
| schemeful_same_site_context, |
| ContextMetadata(), |
| ContextMetadata()) {} |
| |
| // Schemeful and schemeless context types are consistency-checked against |
| // each other, but the metadata is stored as-is (i.e. the values in |
| // `metadata` and `schemeful_metadata` may be logically inconsistent), as |
| // the metadata is not relied upon for correctness. |
| SameSiteCookieContext(ContextType same_site_context, |
| ContextType schemeful_same_site_context, |
| ContextMetadata metadata, |
| ContextMetadata schemeful_metadata) |
| : context_(same_site_context), |
| schemeful_context_(schemeful_same_site_context), |
| metadata_(metadata), |
| schemeful_metadata_(schemeful_metadata) { |
| DCHECK_LE(schemeful_context_, context_); |
| } |
| |
| // Convenience method which returns a SameSiteCookieContext with the most |
| // inclusive contexts. This allows access to all SameSite cookies. |
| static SameSiteCookieContext MakeInclusive(); |
| |
| // Convenience method which returns a SameSiteCookieContext with the most |
| // inclusive contexts for set. This allows setting all SameSite cookies. |
| static SameSiteCookieContext MakeInclusiveForSet(); |
| |
| // Returns the context for determining SameSite cookie inclusion. |
| ContextType GetContextForCookieInclusion() const; |
| |
| // Returns the metadata describing how this context was calculated, under |
| // the currently applicable schemeful/schemeless mode. |
| // TODO(chlily): Should take the CookieAccessSemantics as well, to |
| // accurately account for the context actually used for a given cookie. |
| const ContextMetadata& GetMetadataForCurrentSchemefulMode() const; |
| |
| // If you're just trying to determine if a cookie is accessible you likely |
| // want to use GetContextForCookieInclusion() which will return the correct |
| // context regardless the status of same-site features. |
| ContextType context() const { return context_; } |
| ContextType schemeful_context() const { return schemeful_context_; } |
| |
| // You probably want to use GetMetadataForCurrentSchemefulMode() instead of |
| // these getters, since that takes into account the applicable schemeful |
| // mode. |
| const ContextMetadata& metadata() const { return metadata_; } |
| ContextMetadata& metadata() { return metadata_; } |
| const ContextMetadata& schemeful_metadata() const { |
| return schemeful_metadata_; |
| } |
| ContextMetadata& schemeful_metadata() { return schemeful_metadata_; } |
| |
| // Sets context types. Does not check for consistency between context and |
| // schemeful context. Does not touch the metadata. |
| void SetContextTypesForTesting(ContextType context_type, |
| ContextType schemeful_context_type); |
| |
| // Returns whether the context types and all fields of the metadata structs |
| // are the same. |
| bool CompleteEquivalenceForTesting( |
| const SameSiteCookieContext& other) const; |
| |
| // Equality operators disregard any metadata! (Only the context types are |
| // compared, not how they were computed.) |
| NET_EXPORT friend bool operator==( |
| const CookieOptions::SameSiteCookieContext& lhs, |
| const CookieOptions::SameSiteCookieContext& rhs); |
| NET_EXPORT friend bool operator!=( |
| const CookieOptions::SameSiteCookieContext& lhs, |
| const CookieOptions::SameSiteCookieContext& rhs); |
| |
| private: |
| ContextType context_; |
| ContextType schemeful_context_; |
| |
| ContextMetadata metadata_; |
| ContextMetadata schemeful_metadata_; |
| }; |
| |
| // Creates a CookieOptions object which: |
| // |
| // * Excludes HttpOnly cookies |
| // * Excludes SameSite cookies |
| // * Updates last-accessed time. |
| // * Does not report excluded cookies in APIs that can do so. |
| // * Excludes SameParty cookies. |
| // |
| // These settings can be altered by calling: |
| // |
| // * |set_{include,exclude}_httponly()| |
| // * |set_same_site_cookie_context()| |
| // * |set_do_not_update_access_time()| |
| // * |set_same_party_cookie_context_type()| |
| CookieOptions(); |
| CookieOptions(const CookieOptions& other); |
| CookieOptions(CookieOptions&& other); |
| ~CookieOptions(); |
| |
| CookieOptions& operator=(const CookieOptions&); |
| CookieOptions& operator=(CookieOptions&&); |
| |
| void set_exclude_httponly() { exclude_httponly_ = true; } |
| void set_include_httponly() { exclude_httponly_ = false; } |
| bool exclude_httponly() const { return exclude_httponly_; } |
| |
| // How trusted is the current browser environment when it comes to accessing |
| // SameSite cookies. Default is not trusted, e.g. CROSS_SITE. |
| void set_same_site_cookie_context(const SameSiteCookieContext& context) { |
| same_site_cookie_context_ = context; |
| } |
| |
| const SameSiteCookieContext& same_site_cookie_context() const { |
| return same_site_cookie_context_; |
| } |
| |
| void set_update_access_time() { update_access_time_ = true; } |
| void set_do_not_update_access_time() { update_access_time_ = false; } |
| bool update_access_time() const { return update_access_time_; } |
| |
| void set_return_excluded_cookies() { return_excluded_cookies_ = true; } |
| void unset_return_excluded_cookies() { return_excluded_cookies_ = false; } |
| bool return_excluded_cookies() const { return return_excluded_cookies_; } |
| |
| void set_same_party_context(const SamePartyContext& context) { |
| same_party_context_ = context; |
| } |
| const SamePartyContext& same_party_context() const { |
| return same_party_context_; |
| } |
| |
| // Getter/setter of |full_party_context_size_| for logging purposes. |
| void set_full_party_context_size(uint32_t len) { |
| full_party_context_size_ = len; |
| } |
| uint32_t full_party_context_size() const { return full_party_context_size_; } |
| |
| void set_is_in_nontrivial_first_party_set(bool is_member) { |
| is_in_nontrivial_first_party_set_ = is_member; |
| } |
| bool is_in_nontrivial_first_party_set() const { |
| return is_in_nontrivial_first_party_set_; |
| } |
| |
| // Convenience method for where you need a CookieOptions that will |
| // work for getting/setting all types of cookies, including HttpOnly and |
| // SameSite cookies. Also specifies not to update the access time, because |
| // usually this is done to get all the cookies to check that they are correct, |
| // including the creation time. This basically makes a CookieOptions that is |
| // the opposite of the default CookieOptions. |
| static CookieOptions MakeAllInclusive(); |
| |
| private: |
| // Keep default values in sync with |
| // content/public/common/cookie_manager.mojom. |
| bool exclude_httponly_ = true; |
| SameSiteCookieContext same_site_cookie_context_; |
| bool update_access_time_ = true; |
| bool return_excluded_cookies_ = false; |
| |
| SamePartyContext same_party_context_; |
| |
| // The size of the isolation_info.party_context plus the top-frame site. |
| // Stored for logging purposes. |
| uint32_t full_party_context_size_ = 0; |
| // Whether the site requesting cookie access (as opposed to e.g. the |
| // `site_for_cookies`) is a member (or owner) of a nontrivial First-Party |
| // Set. |
| // This is included here temporarily, for the purpose of ignoring SameParty |
| // for sites that are not participating in the Origin Trial. |
| // TODO(https://crbug.com/1163990): remove this field. |
| bool is_in_nontrivial_first_party_set_ = false; |
| }; |
| |
| NET_EXPORT bool operator==( |
| const CookieOptions::SameSiteCookieContext::ContextMetadata& lhs, |
| const CookieOptions::SameSiteCookieContext::ContextMetadata& rhs); |
| NET_EXPORT bool operator!=( |
| const CookieOptions::SameSiteCookieContext::ContextMetadata& lhs, |
| const CookieOptions::SameSiteCookieContext::ContextMetadata& rhs); |
| |
| // Allows gtest to print more helpful error messages instead of printing hex. |
| // (No need to null-check `os` because we can assume gtest will properly pass a |
| // non-null pointer, and it is dereferenced immediately anyway.) |
| inline void PrintTo(CookieOptions::SameSiteCookieContext::ContextType ct, |
| std::ostream* os) { |
| *os << static_cast<int>(ct); |
| } |
| |
| inline void PrintTo( |
| const CookieOptions::SameSiteCookieContext::ContextMetadata& m, |
| std::ostream* os) { |
| *os << "{"; |
| *os << " cross_site_redirect_downgrade: " |
| << static_cast<int>(m.cross_site_redirect_downgrade); |
| *os << ", redirect_type_bug_1221316: " |
| << static_cast<int>(m.redirect_type_bug_1221316); |
| *os << ", http_method_bug_1221316: " |
| << static_cast<int>(m.http_method_bug_1221316); |
| *os << " }"; |
| } |
| |
| inline void PrintTo(const CookieOptions::SameSiteCookieContext& sscc, |
| std::ostream* os) { |
| *os << "{ context: "; |
| PrintTo(sscc.context(), os); |
| *os << ", schemeful_context: "; |
| PrintTo(sscc.schemeful_context(), os); |
| *os << ", metadata: "; |
| PrintTo(sscc.metadata(), os); |
| *os << ", schemeful_metadata: "; |
| PrintTo(sscc.schemeful_metadata(), os); |
| *os << " }"; |
| } |
| |
| } // namespace net |
| |
| #endif // NET_COOKIES_COOKIE_OPTIONS_H_ |