Jihwan Marc Kim | 3e132f1 | 2020-05-20 17:33:19 | [diff] [blame] | 1 | // Copyright 2020 The Chromium Authors. All rights reserved. |
| 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_COOKIE_INCLUSION_STATUS_H_ |
| 6 | #define NET_COOKIES_COOKIE_INCLUSION_STATUS_H_ |
| 7 | |
| 8 | #include <string> |
| 9 | #include <vector> |
| 10 | |
| 11 | #include "net/base/net_export.h" |
| 12 | |
| 13 | class GURL; |
| 14 | |
| 15 | namespace net { |
| 16 | |
| 17 | // This class represents if a cookie was included or excluded in a cookie get or |
| 18 | // set operation, and if excluded why. It holds a vector of reasons for |
| 19 | // exclusion, where cookie inclusion is represented by the absence of any |
| 20 | // exclusion reasons. Also marks whether a cookie should be warned about, e.g. |
| 21 | // for deprecation or intervention reasons. |
| 22 | class NET_EXPORT CookieInclusionStatus { |
| 23 | public: |
| 24 | // Types of reasons why a cookie might be excluded. |
| 25 | // If adding a ExclusionReason, please also update the GetDebugString() |
| 26 | // method. |
| 27 | enum ExclusionReason { |
| 28 | EXCLUDE_UNKNOWN_ERROR = 0, |
| 29 | |
Lily Chen | c42ac75 | 2020-11-03 19:16:36 | [diff] [blame] | 30 | // Statuses applied when accessing a cookie (either sending or setting): |
Jihwan Marc Kim | 3e132f1 | 2020-05-20 17:33:19 | [diff] [blame] | 31 | |
Lily Chen | c42ac75 | 2020-11-03 19:16:36 | [diff] [blame] | 32 | // Cookie was HttpOnly, but the attempted access was through a non-HTTP API. |
| 33 | EXCLUDE_HTTP_ONLY = 1, |
| 34 | // Cookie was Secure, but the URL was not allowed to access Secure cookies. |
| 35 | EXCLUDE_SECURE_ONLY = 2, |
| 36 | // The cookie's domain attribute did not match the domain of the URL |
| 37 | // attempting access. |
| 38 | EXCLUDE_DOMAIN_MISMATCH = 3, |
| 39 | // The cookie's path attribute did not match the path of the URL attempting |
| 40 | // access. |
| 41 | EXCLUDE_NOT_ON_PATH = 4, |
| 42 | // The cookie had SameSite=Strict, and the attempted access did not have an |
| 43 | // appropriate SameSiteCookieContext. |
| 44 | EXCLUDE_SAMESITE_STRICT = 5, |
| 45 | // The cookie had SameSite=Lax, and the attempted access did not have an |
| 46 | // appropriate SameSiteCookieContext. |
| 47 | EXCLUDE_SAMESITE_LAX = 6, |
| 48 | // The cookie did not specify a SameSite attribute, and therefore was |
| 49 | // treated as if it were SameSite=Lax, and the attempted access did not have |
| 50 | // an appropriate SameSiteCookieContext. |
Jihwan Marc Kim | 3e132f1 | 2020-05-20 17:33:19 | [diff] [blame] | 51 | EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX = 7, |
Lily Chen | c42ac75 | 2020-11-03 19:16:36 | [diff] [blame] | 52 | // The cookie specified SameSite=None, but it was not Secure. |
Jihwan Marc Kim | 3e132f1 | 2020-05-20 17:33:19 | [diff] [blame] | 53 | EXCLUDE_SAMESITE_NONE_INSECURE = 8, |
Lily Chen | c42ac75 | 2020-11-03 19:16:36 | [diff] [blame] | 54 | // Caller did not allow access to the cookie. |
Jihwan Marc Kim | 3e132f1 | 2020-05-20 17:33:19 | [diff] [blame] | 55 | EXCLUDE_USER_PREFERENCES = 9, |
cfredric | 05c78ac | 2021-01-06 18:10:26 | [diff] [blame] | 56 | // The cookie specified SameParty, but was used in a cross-party context. |
| 57 | EXCLUDE_SAMEPARTY_CROSS_PARTY_CONTEXT = 10, |
Jihwan Marc Kim | 3e132f1 | 2020-05-20 17:33:19 | [diff] [blame] | 58 | |
Lily Chen | c42ac75 | 2020-11-03 19:16:36 | [diff] [blame] | 59 | // Statuses only applied when creating/setting cookies: |
| 60 | |
| 61 | // Cookie was malformed and could not be stored. |
cfredric | 05c78ac | 2021-01-06 18:10:26 | [diff] [blame] | 62 | EXCLUDE_FAILURE_TO_STORE = 11, |
Lily Chen | c42ac75 | 2020-11-03 19:16:36 | [diff] [blame] | 63 | // Attempted to set a cookie from a scheme that does not support cookies. |
cfredric | 05c78ac | 2021-01-06 18:10:26 | [diff] [blame] | 64 | EXCLUDE_NONCOOKIEABLE_SCHEME = 12, |
Lily Chen | c42ac75 | 2020-11-03 19:16:36 | [diff] [blame] | 65 | // Cookie would have overwritten a Secure cookie, and was not allowed to do |
| 66 | // so. (See "Leave Secure Cookies Alone": |
| 67 | // https://tools.ietf.org/html/draft-west-leave-secure-cookies-alone-05 ) |
cfredric | 05c78ac | 2021-01-06 18:10:26 | [diff] [blame] | 68 | EXCLUDE_OVERWRITE_SECURE = 13, |
Lily Chen | c42ac75 | 2020-11-03 19:16:36 | [diff] [blame] | 69 | // Cookie would have overwritten an HttpOnly cookie, and was not allowed to |
| 70 | // do so. |
cfredric | 05c78ac | 2021-01-06 18:10:26 | [diff] [blame] | 71 | EXCLUDE_OVERWRITE_HTTP_ONLY = 14, |
Lily Chen | c42ac75 | 2020-11-03 19:16:36 | [diff] [blame] | 72 | // Cookie was set with an invalid Domain attribute. |
cfredric | 05c78ac | 2021-01-06 18:10:26 | [diff] [blame] | 73 | EXCLUDE_INVALID_DOMAIN = 15, |
Lily Chen | c42ac75 | 2020-11-03 19:16:36 | [diff] [blame] | 74 | // Cookie was set with an invalid __Host- or __Secure- prefix. |
cfredric | 05c78ac | 2021-01-06 18:10:26 | [diff] [blame] | 75 | EXCLUDE_INVALID_PREFIX = 16, |
Lily Chen | c42ac75 | 2020-11-03 19:16:36 | [diff] [blame] | 76 | // Cookie was set with an invalid SameParty attribute in combination with |
| 77 | // other attributes. (SameParty is invalid if Secure is not present, or if |
| 78 | // SameSite=Strict is present.) |
cfredric | 05c78ac | 2021-01-06 18:10:26 | [diff] [blame] | 79 | EXCLUDE_INVALID_SAMEPARTY = 17, |
Jihwan Marc Kim | 3e132f1 | 2020-05-20 17:33:19 | [diff] [blame] | 80 | |
| 81 | // This should be kept last. |
| 82 | NUM_EXCLUSION_REASONS |
| 83 | }; |
| 84 | |
Ayu Ishii | b9f3e6e | 2020-06-15 20:53:13 | [diff] [blame] | 85 | // Reason to warn about a cookie. Any information contained in WarningReason |
| 86 | // of an included cookie may be passed to an untrusted renderer. |
| 87 | // If you add one, please update GetDebugString(). |
Jihwan Marc Kim | 3e132f1 | 2020-05-20 17:33:19 | [diff] [blame] | 88 | enum WarningReason { |
| 89 | // Of the following 3 SameSite warnings, there will be, at most, a single |
| 90 | // active one. |
| 91 | |
| 92 | // Warn if a cookie with unspecified SameSite attribute is used in a |
| 93 | // cross-site context. |
| 94 | WARN_SAMESITE_UNSPECIFIED_CROSS_SITE_CONTEXT = 0, |
| 95 | // Warn if a cookie with SameSite=None is not Secure. |
| 96 | WARN_SAMESITE_NONE_INSECURE = 1, |
| 97 | // Warn if a cookie with unspecified SameSite attribute is defaulted into |
| 98 | // Lax and is sent on a request with unsafe method, only because it is new |
| 99 | // enough to activate the Lax-allow-unsafe intervention. |
| 100 | WARN_SAMESITE_UNSPECIFIED_LAX_ALLOW_UNSAFE = 2, |
| 101 | |
| 102 | // The following warnings indicate that an included cookie with an effective |
| 103 | // SameSite is experiencing a SameSiteCookieContext::|context| -> |
| 104 | // SameSiteCookieContext::|schemeful_context| downgrade that will prevent |
| 105 | // its access schemefully. |
| 106 | // This situation means that a cookie is accessible when the |
| 107 | // SchemefulSameSite feature is disabled but not when it's enabled, |
| 108 | // indicating changed behavior and potential breakage. |
| 109 | // |
| 110 | // For example, a Strict to Lax downgrade for an effective SameSite=Strict |
| 111 | // cookie: |
| 112 | // This cookie would be accessible in the Strict context as its SameSite |
| 113 | // value is Strict. However its context for schemeful same-site becomes Lax. |
| 114 | // A strict cookie cannot be accessed in a Lax context and therefore the |
| 115 | // behavior has changed. |
| 116 | // As a counterexample, a Strict to Lax downgrade for an effective |
| 117 | // SameSite=Lax cookie: A Lax cookie can be accessed in both Strict and Lax |
| 118 | // contexts so there is no behavior change (and we don't warn about it). |
| 119 | // |
| 120 | // The warnings are in the following format: |
| 121 | // WARN_{context}_{schemeful_context}_DOWNGRADE_{samesite_value}_SAMESITE |
| 122 | // |
| 123 | // Of the following 5 SameSite warnings, there will be, at most, a single |
| 124 | // active one. |
| 125 | |
| 126 | // Strict to Lax downgrade for an effective SameSite=Strict cookie. |
| 127 | // This warning is only applicable for cookies being sent because a Strict |
| 128 | // cookie will be set in both Strict and Lax Contexts so the downgrade will |
| 129 | // not affect it. |
| 130 | WARN_STRICT_LAX_DOWNGRADE_STRICT_SAMESITE = 3, |
| 131 | // Strict to Cross-site downgrade for an effective SameSite=Strict cookie. |
| 132 | // This also applies to Strict to Lax Unsafe downgrades due to Lax Unsafe |
| 133 | // behaving like Cross-site. |
| 134 | WARN_STRICT_CROSS_DOWNGRADE_STRICT_SAMESITE = 4, |
| 135 | // Strict to Cross-site downgrade for an effective SameSite=Lax cookie. |
| 136 | // This also applies to Strict to Lax Unsafe downgrades due to Lax Unsafe |
| 137 | // behaving like Cross-site. |
| 138 | WARN_STRICT_CROSS_DOWNGRADE_LAX_SAMESITE = 5, |
| 139 | // Lax to Cross-site downgrade for an effective SameSite=Strict cookie. |
| 140 | // This warning is only applicable for cookies being set because a Strict |
| 141 | // cookie will not be sent in a Lax context so the downgrade would not |
| 142 | // affect it. |
| 143 | WARN_LAX_CROSS_DOWNGRADE_STRICT_SAMESITE = 6, |
| 144 | // Lax to Cross-site downgrade for an effective SameSite=Lax cookie. |
| 145 | WARN_LAX_CROSS_DOWNGRADE_LAX_SAMESITE = 7, |
| 146 | |
Maks Orlovich | bd04d78 | 2020-11-17 21:23:34 | [diff] [blame] | 147 | // Advisory warning attached when a Secure cookie is accessed from (sent to, |
| 148 | // or set by) a non-cryptographic URL. This can happen if the URL is |
| 149 | // potentially trustworthy (e.g. a localhost URL, or another URL that |
| 150 | // the CookieAccessDelegate is configured to allow). |
| 151 | // TODO(chlily): Add metrics for how often and where this occurs. |
Lily Chen | 1470871 | 2020-11-19 16:17:42 | [diff] [blame] | 152 | WARN_SECURE_ACCESS_GRANTED_NON_CRYPTOGRAPHIC = 8, |
Maks Orlovich | bd04d78 | 2020-11-17 21:23:34 | [diff] [blame] | 153 | |
cfredric | a5fb098 | 2021-01-09 00:18:01 | [diff] [blame] | 154 | // The cookie was treated as SameParty. This is different from looking at |
| 155 | // whether the cookie has the SameParty attribute, since we may choose to |
| 156 | // ignore that attribute for one reason or another. E.g., we ignore the |
| 157 | // SameParty attribute if the site is not a member of a nontrivial |
| 158 | // First-Party Set. |
| 159 | WARN_TREATED_AS_SAMEPARTY = 9, |
| 160 | |
| 161 | // The cookie was excluded solely for SameParty reasons (i.e. it was in |
| 162 | // cross-party context), but would have been included by SameSite. (This can |
| 163 | // only occur in cross-party, cross-site contexts, for cookies that are |
| 164 | // 'SameParty; SameSite=None'.) |
| 165 | WARN_SAMEPARTY_EXCLUSION_OVERRULED_SAMESITE = 10, |
| 166 | |
| 167 | // The cookie was included due to SameParty, even though it would have been |
| 168 | // excluded by SameSite. (This can only occur in same-party, cross-site |
| 169 | // contexts, for cookies that are 'SameParty; SameSite=Lax'.) |
| 170 | WARN_SAMEPARTY_INCLUSION_OVERRULED_SAMESITE = 11, |
| 171 | |
Lily Chen | ef77a17 | 2021-02-22 22:42:26 | [diff] [blame] | 172 | // The cookie was SameSite=Lax (or unspecified-treated-as-lax) and was |
| 173 | // excluded due to the fix for crbug.com/1166211, i.e. it was accessed by an |
| 174 | // HTTP request which was not a main frame navigation, whose initiator was |
| 175 | // cross-site and whose site-for-cookies was same-site with the request URL. |
| 176 | // TODO(crbug.com/1166211): Remove when no longer needed. |
| 177 | WARN_SAMESITE_LAX_EXCLUDED_AFTER_BUGFIX_1166211 = 12, |
| 178 | |
Jihwan Marc Kim | 3e132f1 | 2020-05-20 17:33:19 | [diff] [blame] | 179 | // This should be kept last. |
| 180 | NUM_WARNING_REASONS |
| 181 | }; |
| 182 | |
| 183 | // These enums encode the context downgrade warnings + the secureness of the |
| 184 | // url sending/setting the cookie. They're used for metrics only. The format |
| 185 | // is {context}_{schemeful_context}_{samesite_value}_{securness}. |
| 186 | // NO_DOWNGRADE_{securness} indicates that a cookie didn't have a breaking |
| 187 | // context downgrade and was A) included B) excluded only due to insufficient |
| 188 | // same-site context. I.e. the cookie wasn't excluded due to other reasons |
| 189 | // such as third-party cookie blocking. Keep this in line with |
| 190 | // SameSiteCookieContextBreakingDowngradeWithSecureness in enums.xml. |
| 191 | enum ContextDowngradeMetricValues { |
| 192 | NO_DOWNGRADE_INSECURE = 0, |
| 193 | NO_DOWNGRADE_SECURE = 1, |
| 194 | |
| 195 | STRICT_LAX_STRICT_INSECURE = 2, |
| 196 | STRICT_CROSS_STRICT_INSECURE = 3, |
| 197 | STRICT_CROSS_LAX_INSECURE = 4, |
| 198 | LAX_CROSS_STRICT_INSECURE = 5, |
| 199 | LAX_CROSS_LAX_INSECURE = 6, |
| 200 | |
| 201 | STRICT_LAX_STRICT_SECURE = 7, |
| 202 | STRICT_CROSS_STRICT_SECURE = 8, |
| 203 | STRICT_CROSS_LAX_SECURE = 9, |
| 204 | LAX_CROSS_STRICT_SECURE = 10, |
| 205 | LAX_CROSS_LAX_SECURE = 11, |
| 206 | |
| 207 | // Keep last. |
| 208 | kMaxValue = LAX_CROSS_LAX_SECURE |
| 209 | }; |
| 210 | // Makes a status that says include and should not warn. |
| 211 | CookieInclusionStatus(); |
| 212 | |
| 213 | // Make a status that contains the given exclusion reason. |
| 214 | explicit CookieInclusionStatus(ExclusionReason reason); |
| 215 | // Makes a status that contains the given exclusion reason and warning. |
| 216 | CookieInclusionStatus(ExclusionReason reason, WarningReason warning); |
| 217 | |
| 218 | bool operator==(const CookieInclusionStatus& other) const; |
| 219 | bool operator!=(const CookieInclusionStatus& other) const; |
| 220 | |
| 221 | // Whether the status is to include the cookie, and has no other reasons for |
| 222 | // exclusion. |
| 223 | bool IsInclude() const; |
| 224 | |
| 225 | // Whether the given reason for exclusion is present. |
| 226 | bool HasExclusionReason(ExclusionReason status_type) const; |
| 227 | |
Lily Chen | 70c537a | 2020-07-20 18:02:09 | [diff] [blame] | 228 | // Whether the given reason for exclusion is present, and is the ONLY reason |
| 229 | // for exclusion. |
| 230 | bool HasOnlyExclusionReason(ExclusionReason status_type) const; |
| 231 | |
Jihwan Marc Kim | 3e132f1 | 2020-05-20 17:33:19 | [diff] [blame] | 232 | // Add an exclusion reason. |
| 233 | void AddExclusionReason(ExclusionReason status_type); |
| 234 | |
| 235 | // Remove an exclusion reason. |
| 236 | void RemoveExclusionReason(ExclusionReason reason); |
| 237 | |
cfredric | 05c78ac | 2021-01-06 18:10:26 | [diff] [blame] | 238 | // Remove multiple exclusion reasons. |
| 239 | void RemoveExclusionReasons(const std::vector<ExclusionReason>& reasons); |
| 240 | |
Jihwan Marc Kim | 3e132f1 | 2020-05-20 17:33:19 | [diff] [blame] | 241 | // If the cookie would have been excluded for reasons other than |
| 242 | // SAMESITE_UNSPECIFIED_TREATED_AS_LAX or SAMESITE_NONE_INSECURE, don't bother |
| 243 | // warning about it (clear the warning). |
| 244 | void MaybeClearSameSiteWarning(); |
| 245 | |
| 246 | // Whether to record the breaking downgrade metrics if the cookie is included |
| 247 | // or if it's only excluded because of insufficient same-site context. |
| 248 | bool ShouldRecordDowngradeMetrics() const; |
| 249 | |
| 250 | // Whether the cookie should be warned about. |
| 251 | bool ShouldWarn() const; |
| 252 | |
| 253 | // Whether the given reason for warning is present. |
| 254 | bool HasWarningReason(WarningReason reason) const; |
| 255 | |
| 256 | // Whether a schemeful downgrade warning is present. |
| 257 | // A schemeful downgrade means that an included cookie with an effective |
| 258 | // SameSite is experiencing a SameSiteCookieContext::|context| -> |
| 259 | // SameSiteCookieContext::|schemeful_context| downgrade that will prevent its |
| 260 | // access schemefully. If the function returns true and |reason| is valid then |
| 261 | // |reason| will contain which warning was found. |
| 262 | bool HasDowngradeWarning( |
| 263 | CookieInclusionStatus::WarningReason* reason = nullptr) const; |
| 264 | |
| 265 | // Add an warning reason. |
| 266 | void AddWarningReason(WarningReason reason); |
| 267 | |
| 268 | // Remove an warning reason. |
| 269 | void RemoveWarningReason(WarningReason reason); |
| 270 | |
| 271 | // Used for serialization/deserialization. |
| 272 | uint32_t exclusion_reasons() const { return exclusion_reasons_; } |
| 273 | void set_exclusion_reasons(uint32_t exclusion_reasons) { |
| 274 | exclusion_reasons_ = exclusion_reasons; |
| 275 | } |
| 276 | |
| 277 | uint32_t warning_reasons() const { return warning_reasons_; } |
| 278 | void set_warning_reasons(uint32_t warning_reasons) { |
| 279 | warning_reasons_ = warning_reasons; |
| 280 | } |
| 281 | |
| 282 | ContextDowngradeMetricValues GetBreakingDowngradeMetricsEnumValue( |
| 283 | const GURL& url) const; |
| 284 | |
| 285 | // Get exclusion reason(s) and warning in string format. |
| 286 | std::string GetDebugString() const; |
| 287 | |
| 288 | // Checks that the underlying bit vector representation doesn't contain any |
| 289 | // extraneous bits that are not mapped to any enum values. Does not check |
| 290 | // for reasons which semantically cannot coexist. |
| 291 | bool IsValid() const; |
| 292 | |
| 293 | // Checks whether the exclusion reasons are exactly the set of exclusion |
| 294 | // reasons in the vector. (Ignores warnings.) |
| 295 | bool HasExactlyExclusionReasonsForTesting( |
| 296 | std::vector<ExclusionReason> reasons) const; |
| 297 | |
| 298 | // Checks whether the warning reasons are exactly the set of warning |
| 299 | // reasons in the vector. (Ignores exclusions.) |
| 300 | bool HasExactlyWarningReasonsForTesting( |
| 301 | std::vector<WarningReason> reasons) const; |
| 302 | |
| 303 | // Makes a status that contains the given exclusion reasons and warning. |
| 304 | static CookieInclusionStatus MakeFromReasonsForTesting( |
| 305 | std::vector<ExclusionReason> reasons, |
| 306 | std::vector<WarningReason> warnings = std::vector<WarningReason>()); |
| 307 | |
| 308 | private: |
cfredric | 2c5cfa6 | 2020-11-16 19:59:25 | [diff] [blame] | 309 | // Returns the `exclusion_reasons_` with the given `reasons` unset. |
| 310 | uint32_t ExclusionReasonsWithout( |
| 311 | const std::vector<ExclusionReason>& reasons) const; |
| 312 | |
Jihwan Marc Kim | 3e132f1 | 2020-05-20 17:33:19 | [diff] [blame] | 313 | // A bit vector of the applicable exclusion reasons. |
| 314 | uint32_t exclusion_reasons_ = 0u; |
| 315 | |
| 316 | // A bit vector of the applicable warning reasons. |
| 317 | uint32_t warning_reasons_ = 0u; |
| 318 | }; |
| 319 | |
| 320 | NET_EXPORT inline std::ostream& operator<<(std::ostream& os, |
| 321 | const CookieInclusionStatus status) { |
| 322 | return os << status.GetDebugString(); |
| 323 | } |
| 324 | |
cfredric | 34ed01a8 | 2020-12-08 05:43:56 | [diff] [blame] | 325 | // Provided to allow gtest to create more helpful error messages, instead of |
| 326 | // printing hex. |
| 327 | inline void PrintTo(const CookieInclusionStatus& cis, std::ostream* os) { |
| 328 | *os << cis; |
| 329 | } |
| 330 | |
Jihwan Marc Kim | 3e132f1 | 2020-05-20 17:33:19 | [diff] [blame] | 331 | } // namespace net |
| 332 | |
| 333 | #endif // NET_COOKIES_COOKIE_INCLUSION_STATUS_H_ |