diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2021-03-12 09:13:00 +0100 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2021-03-16 09:58:26 +0000 |
commit | 03561cae90f1d99b5c54b1ef3be69f10e882b25e (patch) | |
tree | cc5f0958e823c044e7ae51cc0117fe51432abe5e /chromium/net/cookies | |
parent | fa98118a45f7e169f8846086dc2c22c49a8ba310 (diff) | |
download | qtwebengine-chromium-03561cae90f1d99b5c54b1ef3be69f10e882b25e.tar.gz |
BASELINE: Update Chromium to 88.0.4324.208
Change-Id: I3ae87d23e4eff4b4a469685658740a213600c667
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/net/cookies')
27 files changed, 1659 insertions, 765 deletions
diff --git a/chromium/net/cookies/DIR_METADATA b/chromium/net/cookies/DIR_METADATA new file mode 100644 index 00000000000..1b6e5edfbf2 --- /dev/null +++ b/chromium/net/cookies/DIR_METADATA @@ -0,0 +1,11 @@ +# Metadata information for this directory. +# +# For more information on DIR_METADATA files, see: +# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md +# +# For the schema of this file, see Metadata message: +# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto + +monorail { + component: "Internals>Network>Cookies" +}
\ No newline at end of file diff --git a/chromium/net/cookies/OWNERS b/chromium/net/cookies/OWNERS index 9b5ffc36540..2ddd82195da 100644 --- a/chromium/net/cookies/OWNERS +++ b/chromium/net/cookies/OWNERS @@ -3,5 +3,3 @@ estark@chromium.org mkwst@chromium.org mmenke@chromium.org morlovich@chromium.org - -# COMPONENT: Internals>Network>Cookies diff --git a/chromium/net/cookies/canonical_cookie.cc b/chromium/net/cookies/canonical_cookie.cc index 1c85b9d7daf..1db40c01ebb 100644 --- a/chromium/net/cookies/canonical_cookie.cc +++ b/chromium/net/cookies/canonical_cookie.cc @@ -50,6 +50,7 @@ #include "base/feature_list.h" #include "base/format_macros.h" #include "base/logging.h" +#include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_macros.h" #include "base/strings/strcat.h" #include "base/strings/string_util.h" @@ -256,13 +257,7 @@ void ApplySameSiteCookieWarningToStatus( } // namespace -// Keep defaults here in sync with content/public/common/cookie_manager.mojom. -CanonicalCookie::CanonicalCookie() - : secure_(false), - httponly_(false), - same_site_(CookieSameSite::NO_RESTRICTION), - priority_(COOKIE_PRIORITY_MEDIUM), - source_scheme_(CookieSourceScheme::kUnset) {} +CanonicalCookie::CanonicalCookie() = default; CanonicalCookie::CanonicalCookie(const CanonicalCookie& other) = default; @@ -277,7 +272,9 @@ CanonicalCookie::CanonicalCookie(const std::string& name, bool httponly, CookieSameSite same_site, CookiePriority priority, - CookieSourceScheme scheme_secure) + bool same_party, + CookieSourceScheme scheme_secure, + int source_port) : name_(name), value_(value), domain_(domain), @@ -289,7 +286,10 @@ CanonicalCookie::CanonicalCookie(const std::string& name, httponly_(httponly), same_site_(same_site), priority_(priority), - source_scheme_(scheme_secure) {} + same_party_(same_party), + source_scheme_(scheme_secure) { + SetSourcePort(source_port); +} CanonicalCookie::~CanonicalCookie() = default; @@ -408,22 +408,35 @@ std::unique_ptr<CanonicalCookie> CanonicalCookie::Create( status->AddExclusionReason(CookieInclusionStatus::EXCLUDE_INVALID_PREFIX); } + bool is_same_party_valid = IsCookieSamePartyValid(parsed_cookie); + if (!is_same_party_valid) { + status->AddExclusionReason( + CookieInclusionStatus::EXCLUDE_INVALID_SAMEPARTY); + } + + // Collect metrics on whether usage of SameParty attribute is correct. + if (parsed_cookie.IsSameParty()) + base::UmaHistogramBoolean("Cookie.IsSamePartyValid", is_same_party_valid); + // TODO(chlily): Log metrics. if (!status->IsInclude()) return nullptr; CookieSameSiteString samesite_string = CookieSameSiteString::kUnspecified; CookieSameSite samesite = parsed_cookie.SameSite(&samesite_string); - RecordCookieSameSiteAttributeValueHistogram(samesite_string); + RecordCookieSameSiteAttributeValueHistogram(samesite_string, + parsed_cookie.IsSameParty()); CookieSourceScheme source_scheme = url.SchemeIsCryptographic() ? CookieSourceScheme::kSecure : CookieSourceScheme::kNonSecure; + // Get the port, this will get a default value if a port isn't provided. + int source_port = url.EffectiveIntPort(); std::unique_ptr<CanonicalCookie> cc(std::make_unique<CanonicalCookie>( parsed_cookie.Name(), parsed_cookie.Value(), cookie_domain, cookie_path, creation_time, cookie_expires, creation_time, parsed_cookie.IsSecure(), parsed_cookie.IsHttpOnly(), samesite, parsed_cookie.Priority(), - source_scheme)); + parsed_cookie.IsSameParty(), source_scheme, source_port)); DCHECK(cc->IsCanonical()); @@ -445,7 +458,8 @@ std::unique_ptr<CanonicalCookie> CanonicalCookie::CreateSanitizedCookie( bool secure, bool http_only, CookieSameSite same_site, - CookiePriority priority) { + CookiePriority priority, + bool same_party) { // Validate consistency of passed arguments. if (ParsedCookie::ParseTokenString(name) != name || ParsedCookie::ParseValueString(value) != value || @@ -475,6 +489,9 @@ std::unique_ptr<CanonicalCookie> CanonicalCookie::CreateSanitizedCookie( if (secure && source_scheme == CookieSourceScheme::kNonSecure) return nullptr; + // Get the port, this will get a default value if a port isn't provided. + int source_port = url.EffectiveIntPort(); + std::string cookie_path = CanonicalCookie::CanonPathWithString(url, path); if (!path.empty() && cookie_path != path) return nullptr; @@ -484,6 +501,9 @@ std::unique_ptr<CanonicalCookie> CanonicalCookie::CreateSanitizedCookie( return nullptr; } + if (!IsCookieSamePartyValid(same_party, secure, same_site)) + return nullptr; + if (!last_access_time.is_null() && creation_time.is_null()) return nullptr; @@ -498,16 +518,51 @@ std::unique_ptr<CanonicalCookie> CanonicalCookie::CreateSanitizedCookie( std::unique_ptr<CanonicalCookie> cc(std::make_unique<CanonicalCookie>( name, value, cookie_domain, cookie_path, creation_time, expiration_time, - last_access_time, secure, http_only, same_site, priority, source_scheme)); + last_access_time, secure, http_only, same_site, priority, same_party, + source_scheme, source_port)); DCHECK(cc->IsCanonical()); return cc; } +// static +std::unique_ptr<CanonicalCookie> CanonicalCookie::FromStorage( + const std::string& name, + const std::string& value, + const std::string& domain, + const std::string& path, + const base::Time& creation, + const base::Time& expiration, + const base::Time& last_access, + bool secure, + bool httponly, + CookieSameSite same_site, + CookiePriority priority, + bool same_party, + CookieSourceScheme source_scheme, + int source_port) { + std::unique_ptr<CanonicalCookie> cc(std::make_unique<CanonicalCookie>( + name, value, domain, path, creation, expiration, last_access, secure, + httponly, same_site, priority, same_party, source_scheme, source_port)); + if (!cc->IsCanonical()) + return nullptr; + return cc; +} + std::string CanonicalCookie::DomainWithoutDot() const { return cookie_util::CookieDomainAsHost(domain_); } +void CanonicalCookie::SetSourcePort(int port) { + if ((port >= 0 && port <= 65535) || port == url::PORT_UNSPECIFIED) { + // 0 would be really weird as it has a special meaning, but it's still + // technically a valid tcp/ip port so we're going to accept it here. + source_port_ = port; + } else { + source_port_ = url::PORT_INVALID; + } +} + bool CanonicalCookie::IsEquivalentForSecureCookieMatching( const CanonicalCookie& secure_cookie) const { // Names must be the same @@ -828,7 +883,7 @@ bool CanonicalCookie::IsCanonical() const { break; } - return true; + return IsCookieSamePartyValid(same_party_, secure_, same_site_); } bool CanonicalCookie::IsEffectivelySameSiteNone( @@ -959,6 +1014,23 @@ bool CanonicalCookie::IsRecentlyCreated(base::TimeDelta age_threshold) const { return (base::Time::Now() - creation_date_) <= age_threshold; } +// static +bool CanonicalCookie::IsCookieSamePartyValid( + const ParsedCookie& parsed_cookie) { + return IsCookieSamePartyValid(parsed_cookie.IsSameParty(), + parsed_cookie.IsSecure(), + parsed_cookie.SameSite()); +} + +// static +bool CanonicalCookie::IsCookieSamePartyValid(bool is_same_party, + bool is_secure, + CookieSameSite same_site) { + if (!is_same_party) + return true; + return is_secure && (same_site != CookieSameSite::STRICT_MODE); +} + CookieAndLineWithAccessResult::CookieAndLineWithAccessResult() = default; CookieAndLineWithAccessResult::CookieAndLineWithAccessResult( diff --git a/chromium/net/cookies/canonical_cookie.h b/chromium/net/cookies/canonical_cookie.h index 4cb63368be1..5ab0c2c9efc 100644 --- a/chromium/net/cookies/canonical_cookie.h +++ b/chromium/net/cookies/canonical_cookie.h @@ -18,6 +18,7 @@ #include "net/cookies/cookie_constants.h" #include "net/cookies/cookie_inclusion_status.h" #include "net/cookies/cookie_options.h" +#include "url/third_party/mozilla/url_parse.h" class GURL; @@ -47,19 +48,20 @@ class NET_EXPORT CanonicalCookie { // themselves. // NOTE: Prefer using CreateSanitizedCookie() over directly using this // constructor. - CanonicalCookie( - const std::string& name, - const std::string& value, - const std::string& domain, - const std::string& path, - const base::Time& creation, - const base::Time& expiration, - const base::Time& last_access, - bool secure, - bool httponly, - CookieSameSite same_site, - CookiePriority priority, - CookieSourceScheme scheme_secure = CookieSourceScheme::kUnset); + CanonicalCookie(const std::string& name, + const std::string& value, + const std::string& domain, + const std::string& path, + const base::Time& creation, + const base::Time& expiration, + const base::Time& last_access, + bool secure, + bool httponly, + CookieSameSite same_site, + CookiePriority priority, + bool same_party, + CookieSourceScheme scheme_secure = CookieSourceScheme::kUnset, + int source_port = url::PORT_UNSPECIFIED); ~CanonicalCookie(); @@ -104,7 +106,31 @@ class NET_EXPORT CanonicalCookie { bool secure, bool http_only, CookieSameSite same_site, - CookiePriority priority); + CookiePriority priority, + bool same_party); + + // FromStorage is a factory method which is meant for creating a new + // CanonicalCookie using properties of a previously existing cookie + // that was already ingested into the cookie store. + // This should NOT be used to create a new CanonicalCookie that was not + // already in the store. + // Returns nullptr if the resulting cookie is not canonical, + // i.e. cc->IsCanonical() returns false. + static std::unique_ptr<CanonicalCookie> FromStorage( + const std::string& name, + const std::string& value, + const std::string& domain, + const std::string& path, + const base::Time& creation, + const base::Time& expiration, + const base::Time& last_access, + bool secure, + bool httponly, + CookieSameSite same_site, + CookiePriority priority, + bool same_party, + CookieSourceScheme source_scheme, + int source_port); const std::string& Name() const { return name_; } const std::string& Value() const { return value_; } @@ -122,10 +148,15 @@ class NET_EXPORT CanonicalCookie { bool IsHttpOnly() const { return httponly_; } CookieSameSite SameSite() const { return same_site_; } CookiePriority Priority() const { return priority_; } + bool IsSameParty() const { return same_party_; } // Returns an enum indicating the source scheme 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. 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(); } @@ -197,6 +228,11 @@ class NET_EXPORT CanonicalCookie { 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 SetLastAccessDate(const base::Time& date) { last_access_date_ = date; } @@ -347,6 +383,16 @@ class NET_EXPORT CanonicalCookie { // Returns whether the cookie was created at most |age_threshold| ago. bool IsRecentlyCreated(base::TimeDelta age_threshold) const; + // Returns true iff the cookie does not violate any rules associated with + // creating a cookie with the SameParty attribute. In particular, if a cookie + // has SameParty, then it must be Secure and must not be SameSite=Strict. + static bool IsCookieSamePartyValid(const ParsedCookie& parsed_cookie); + static bool IsCookieSamePartyValid(bool is_same_party, + bool is_secure, + CookieSameSite same_site); + + // Keep defaults here in sync with + // services/network/public/interfaces/cookie_manager.mojom. std::string name_; std::string value_; std::string domain_; @@ -354,11 +400,17 @@ class NET_EXPORT CanonicalCookie { base::Time creation_date_; base::Time expiry_date_; base::Time last_access_date_; - bool secure_; - bool httponly_; - CookieSameSite same_site_; - CookiePriority priority_; - CookieSourceScheme source_scheme_; + bool secure_{false}; + bool httponly_{false}; + CookieSameSite same_site_{CookieSameSite::NO_RESTRICTION}; + CookiePriority priority_{COOKIE_PRIORITY_MEDIUM}; + bool same_party_{false}; + 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}; }; // Used to pass excluded cookie information when it's possible that the diff --git a/chromium/net/cookies/canonical_cookie_fuzzer.cc b/chromium/net/cookies/canonical_cookie_fuzzer.cc index 9aecd883fb4..68a4d08c134 100644 --- a/chromium/net/cookies/canonical_cookie_fuzzer.cc +++ b/chromium/net/cookies/canonical_cookie_fuzzer.cc @@ -56,8 +56,9 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { const std::unique_ptr<const CanonicalCookie> sanitized_cookie = CanonicalCookie::CreateSanitizedCookie( url, name, value, domain, path, creation, expiration, last_access, - data_provider.ConsumeBool(), data_provider.ConsumeBool(), same_site, - priority); + data_provider.ConsumeBool() /* secure */, + data_provider.ConsumeBool() /* httponly */, same_site, priority, + data_provider.ConsumeBool() /* same_party */); if (sanitized_cookie) { CHECK(sanitized_cookie->IsCanonical()); diff --git a/chromium/net/cookies/canonical_cookie_unittest.cc b/chromium/net/cookies/canonical_cookie_unittest.cc index 04c1b6488f5..6da0dbb0ecd 100644 --- a/chromium/net/cookies/canonical_cookie_unittest.cc +++ b/chromium/net/cookies/canonical_cookie_unittest.cc @@ -13,6 +13,7 @@ #include "net/cookies/cookie_options.h" #include "testing/gtest/include/gtest/gtest.h" #include "url/gurl.h" +#include "url/third_party/mozilla/url_parse.h" namespace net { @@ -31,13 +32,12 @@ void MatchCookieLineToVector( } // namespace TEST(CanonicalCookieTest, Constructor) { - GURL url("http://www.example.com/test"); base::Time current_time = base::Time::Now(); std::unique_ptr<CanonicalCookie> cookie1(std::make_unique<CanonicalCookie>( "A", "2", "www.example.com", "/test", current_time, base::Time(), base::Time(), false, false, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_DEFAULT, CookieSourceScheme::kSecure)); + COOKIE_PRIORITY_DEFAULT, false, CookieSourceScheme::kSecure, 443)); EXPECT_EQ("A", cookie1->Name()); EXPECT_EQ("2", cookie1->Value()); EXPECT_EQ("www.example.com", cookie1->Domain()); @@ -45,12 +45,15 @@ TEST(CanonicalCookieTest, Constructor) { EXPECT_FALSE(cookie1->IsSecure()); EXPECT_FALSE(cookie1->IsHttpOnly()); EXPECT_EQ(CookieSameSite::NO_RESTRICTION, cookie1->SameSite()); + EXPECT_EQ(CookiePriority::COOKIE_PRIORITY_DEFAULT, cookie1->Priority()); + EXPECT_FALSE(cookie1->IsSameParty()); EXPECT_EQ(cookie1->SourceScheme(), CookieSourceScheme::kSecure); + EXPECT_EQ(cookie1->SourcePort(), 443); std::unique_ptr<CanonicalCookie> cookie2(std::make_unique<CanonicalCookie>( "A", "2", ".www.example.com", "/", current_time, base::Time(), base::Time(), false, false, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_DEFAULT, CookieSourceScheme::kNonSecure)); + COOKIE_PRIORITY_DEFAULT, true, CookieSourceScheme::kNonSecure, 65536)); EXPECT_EQ("A", cookie2->Name()); EXPECT_EQ("2", cookie2->Value()); EXPECT_EQ(".www.example.com", cookie2->Domain()); @@ -58,20 +61,27 @@ TEST(CanonicalCookieTest, Constructor) { EXPECT_FALSE(cookie2->IsSecure()); EXPECT_FALSE(cookie2->IsHttpOnly()); EXPECT_EQ(CookieSameSite::NO_RESTRICTION, cookie2->SameSite()); + EXPECT_EQ(CookiePriority::COOKIE_PRIORITY_DEFAULT, cookie2->Priority()); + EXPECT_TRUE(cookie2->IsSameParty()); EXPECT_EQ(cookie2->SourceScheme(), CookieSourceScheme::kNonSecure); + // Because the port can be set explicitly in the constructor its value can be + // independent of the other parameters. In this case, test that an invalid + // port value is interpreted as such. + EXPECT_EQ(cookie2->SourcePort(), url::PORT_INVALID); - // Set Secure to true but don't specify is_source_scheme_secure + // Set Secure to true but don't specify source_scheme or port. auto cookie3 = std::make_unique<CanonicalCookie>( "A", "2", ".www.example.com", "/", current_time, base::Time(), base::Time(), true /* secure */, false, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_DEFAULT); + COOKIE_PRIORITY_DEFAULT, false); EXPECT_TRUE(cookie3->IsSecure()); EXPECT_EQ(cookie3->SourceScheme(), CookieSourceScheme::kUnset); + EXPECT_EQ(cookie3->SourcePort(), url::PORT_UNSPECIFIED); auto cookie4 = std::make_unique<CanonicalCookie>( "A", "2", ".www.example.com", "/test", current_time, base::Time(), base::Time(), false, false, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_DEFAULT); + COOKIE_PRIORITY_DEFAULT, false); EXPECT_EQ("A", cookie4->Name()); EXPECT_EQ("2", cookie4->Value()); EXPECT_EQ(".www.example.com", cookie4->Domain()); @@ -79,6 +89,25 @@ TEST(CanonicalCookieTest, Constructor) { EXPECT_FALSE(cookie4->IsSecure()); EXPECT_FALSE(cookie4->IsHttpOnly()); EXPECT_EQ(CookieSameSite::NO_RESTRICTION, cookie4->SameSite()); + EXPECT_FALSE(cookie4->IsSameParty()); + EXPECT_EQ(cookie4->SourceScheme(), CookieSourceScheme::kUnset); + EXPECT_EQ(cookie4->SourcePort(), url::PORT_UNSPECIFIED); + + // Test some port edge cases: unspecified. + auto cookie5 = std::make_unique<CanonicalCookie>( + "A", "2", ".www.example.com", "/", current_time, base::Time(), + base::Time(), true /* secure */, false, CookieSameSite::NO_RESTRICTION, + COOKIE_PRIORITY_DEFAULT, false, CookieSourceScheme::kUnset, + url::PORT_UNSPECIFIED); + EXPECT_EQ(cookie5->SourcePort(), url::PORT_UNSPECIFIED); + + // Test some port edge cases: invalid. + auto cookie6 = std::make_unique<CanonicalCookie>( + "A", "2", ".www.example.com", "/", current_time, base::Time(), + base::Time(), true /* secure */, false, CookieSameSite::NO_RESTRICTION, + COOKIE_PRIORITY_DEFAULT, false, CookieSourceScheme::kUnset, + url::PORT_INVALID); + EXPECT_EQ(cookie6->SourcePort(), url::PORT_INVALID); } TEST(CanonicalCookie, CreationCornerCases) { @@ -113,6 +142,7 @@ TEST(CanonicalCookieTest, Create) { EXPECT_EQ("/test", cookie->Path()); EXPECT_FALSE(cookie->IsSecure()); EXPECT_EQ(cookie->SourceScheme(), CookieSourceScheme::kNonSecure); + EXPECT_EQ(cookie->SourcePort(), 80); GURL url2("http://www.foo.com"); cookie = CanonicalCookie::Create(url2, "B=1", creation_time, server_time); @@ -122,6 +152,7 @@ TEST(CanonicalCookieTest, Create) { EXPECT_EQ("/", cookie->Path()); EXPECT_FALSE(cookie->IsSecure()); EXPECT_EQ(cookie->SourceScheme(), CookieSourceScheme::kNonSecure); + EXPECT_EQ(cookie->SourcePort(), 80); // Test creating secure cookies. Secure scheme is not checked upon creation, // so a URL of any scheme can create a Secure cookie. @@ -174,6 +205,33 @@ TEST(CanonicalCookieTest, Create) { cookie = CanonicalCookie::Create(url, "A=2", creation_time, server_time); ASSERT_TRUE(cookie.get()); EXPECT_EQ(CookieSameSite::UNSPECIFIED, cookie->SameSite()); + + // Test creating cookies with different ports. + cookie = CanonicalCookie::Create(GURL("http://www.foo.com"), "B=1", + creation_time, server_time); + EXPECT_EQ(cookie->SourcePort(), 80); + + cookie = CanonicalCookie::Create(GURL("http://www.foo.com:81"), "B=1", + creation_time, server_time); + EXPECT_EQ(cookie->SourcePort(), 81); + + cookie = CanonicalCookie::Create(GURL("https://www.foo.com"), "B=1", + creation_time, server_time); + EXPECT_EQ(cookie->SourcePort(), 443); + + cookie = CanonicalCookie::Create(GURL("https://www.foo.com:1234"), "B=1", + creation_time, server_time); + EXPECT_EQ(cookie->SourcePort(), 1234); + + cookie = CanonicalCookie::Create(GURL("http://www.foo.com:443"), "B=1", + creation_time, server_time); + EXPECT_EQ(cookie->SourcePort(), 443); + + // GURL's port parsing will handle any invalid ports, but let's still make + // sure we get the expected result anyway. + cookie = CanonicalCookie::Create(GURL("http://www.foo.com:70000"), "B=1", + creation_time, server_time); + EXPECT_EQ(cookie->SourcePort(), url::PORT_INVALID); } TEST(CanonicalCookieTest, CreateNonStandardSameSite) { @@ -240,6 +298,53 @@ TEST(CanonicalCookieTest, CreateWithInvalidDomain) { {CookieInclusionStatus::EXCLUDE_INVALID_DOMAIN})); } +TEST(CanonicalCookieTest, CreateSameParty) { + GURL url("http://www.example.com/test/foo.html"); + GURL https_url("https://www.example.com/test/foo.html"); + base::Time creation_time = base::Time::Now(); + base::Optional<base::Time> server_time = base::nullopt; + + CookieInclusionStatus status; + std::unique_ptr<CanonicalCookie> cookie = CanonicalCookie::Create( + url, "A=2; SameParty; Secure", creation_time, server_time, &status); + ASSERT_TRUE(cookie.get()); + EXPECT_TRUE(status.IsInclude()); + EXPECT_TRUE(cookie->IsSecure()); + EXPECT_TRUE(cookie->IsSameParty()); + EXPECT_EQ(CookieSameSite::UNSPECIFIED, cookie->SameSite()); + + cookie = CanonicalCookie::Create(url, "A=2; SameParty; SameSite=None; Secure", + creation_time, server_time, &status); + ASSERT_TRUE(cookie.get()); + EXPECT_TRUE(status.IsInclude()); + EXPECT_TRUE(cookie->IsSecure()); + EXPECT_TRUE(cookie->IsSameParty()); + EXPECT_EQ(CookieSameSite::NO_RESTRICTION, cookie->SameSite()); + + cookie = CanonicalCookie::Create(url, "A=2; SameParty; SameSite=Lax; Secure", + creation_time, server_time, &status); + ASSERT_TRUE(cookie.get()); + EXPECT_TRUE(status.IsInclude()); + EXPECT_TRUE(cookie->IsSecure()); + EXPECT_TRUE(cookie->IsSameParty()); + EXPECT_EQ(CookieSameSite::LAX_MODE, cookie->SameSite()); + + // SameParty cookie with SameSite=Strict is invalid. + cookie = + CanonicalCookie::Create(url, "A=2; SameParty; SameSite=Strict; Secure", + creation_time, server_time, &status); + EXPECT_FALSE(cookie.get()); + EXPECT_TRUE(status.HasExactlyExclusionReasonsForTesting( + {CookieInclusionStatus::EXCLUDE_INVALID_SAMEPARTY})); + + // SameParty cookie without Secure is invalid. + cookie = CanonicalCookie::Create(url, "A=2; SameParty; SameSite=Lax", + creation_time, server_time, &status); + EXPECT_FALSE(cookie.get()); + EXPECT_TRUE(status.HasExactlyExclusionReasonsForTesting( + {CookieInclusionStatus::EXCLUDE_INVALID_SAMEPARTY})); +} + TEST(CanonicalCookieTest, EmptyExpiry) { GURL url("http://www7.ipdl.inpit.go.jp/Tokujitu/tjkta.ipdl?N0000=108"); base::Time creation_time = base::Time::Now(); @@ -281,15 +386,16 @@ TEST(CanonicalCookieTest, IsEquivalent) { std::string cookie_path = "/path"; base::Time creation_time = base::Time::Now(); base::Time expiration_time = creation_time + base::TimeDelta::FromDays(2); - bool secure(false); - bool httponly(false); - CookieSameSite same_site(CookieSameSite::NO_RESTRICTION); + bool secure = false; + bool httponly = false; + CookieSameSite same_site = CookieSameSite::NO_RESTRICTION; + bool same_party = false; // Test that a cookie is equivalent to itself. std::unique_ptr<CanonicalCookie> cookie(std::make_unique<CanonicalCookie>( cookie_name, cookie_value, cookie_domain, cookie_path, creation_time, expiration_time, base::Time(), secure, httponly, same_site, - COOKIE_PRIORITY_MEDIUM)); + COOKIE_PRIORITY_MEDIUM, same_party)); EXPECT_TRUE(cookie->IsEquivalent(*cookie)); EXPECT_TRUE(cookie->IsEquivalentForSecureCookieMatching(*cookie)); @@ -298,7 +404,7 @@ TEST(CanonicalCookieTest, IsEquivalent) { std::make_unique<CanonicalCookie>( cookie_name, cookie_value, cookie_domain, cookie_path, creation_time, expiration_time, base::Time(), secure, httponly, same_site, - COOKIE_PRIORITY_MEDIUM)); + COOKIE_PRIORITY_MEDIUM, same_party)); EXPECT_TRUE(cookie->IsEquivalent(*other_cookie)); EXPECT_TRUE(other_cookie->IsEquivalentForSecureCookieMatching(*cookie)); @@ -307,7 +413,7 @@ TEST(CanonicalCookieTest, IsEquivalent) { other_cookie = std::make_unique<CanonicalCookie>( cookie_name, "2", cookie_domain, cookie_path, creation_time, expiration_time, base::Time(), secure, httponly, same_site, - COOKIE_PRIORITY_HIGH); + COOKIE_PRIORITY_HIGH, same_party); EXPECT_TRUE(cookie->IsEquivalent(*other_cookie)); EXPECT_TRUE(cookie->IsEquivalentForSecureCookieMatching(*other_cookie)); EXPECT_TRUE(other_cookie->IsEquivalentForSecureCookieMatching(*cookie)); @@ -317,7 +423,7 @@ TEST(CanonicalCookieTest, IsEquivalent) { other_cookie = std::make_unique<CanonicalCookie>( cookie_name, "2", cookie_domain, cookie_path, other_creation_time, expiration_time, base::Time(), secure, httponly, same_site, - COOKIE_PRIORITY_MEDIUM); + COOKIE_PRIORITY_MEDIUM, same_party); EXPECT_TRUE(cookie->IsEquivalent(*other_cookie)); EXPECT_TRUE(cookie->IsEquivalentForSecureCookieMatching(*other_cookie)); EXPECT_TRUE(other_cookie->IsEquivalentForSecureCookieMatching(*cookie)); @@ -325,7 +431,7 @@ TEST(CanonicalCookieTest, IsEquivalent) { other_cookie = std::make_unique<CanonicalCookie>( cookie_name, cookie_name, cookie_domain, cookie_path, creation_time, expiration_time, base::Time(), true, httponly, same_site, - COOKIE_PRIORITY_LOW); + COOKIE_PRIORITY_LOW, same_party); EXPECT_TRUE(cookie->IsEquivalent(*other_cookie)); EXPECT_TRUE(cookie->IsEquivalentForSecureCookieMatching(*other_cookie)); EXPECT_TRUE(other_cookie->IsEquivalentForSecureCookieMatching(*cookie)); @@ -333,7 +439,7 @@ TEST(CanonicalCookieTest, IsEquivalent) { other_cookie = std::make_unique<CanonicalCookie>( cookie_name, cookie_name, cookie_domain, cookie_path, creation_time, expiration_time, base::Time(), secure, true, same_site, - COOKIE_PRIORITY_LOW); + COOKIE_PRIORITY_LOW, same_party); EXPECT_TRUE(cookie->IsEquivalent(*other_cookie)); EXPECT_TRUE(cookie->IsEquivalentForSecureCookieMatching(*other_cookie)); EXPECT_TRUE(other_cookie->IsEquivalentForSecureCookieMatching(*cookie)); @@ -341,7 +447,7 @@ TEST(CanonicalCookieTest, IsEquivalent) { other_cookie = std::make_unique<CanonicalCookie>( cookie_name, cookie_name, cookie_domain, cookie_path, creation_time, expiration_time, base::Time(), secure, httponly, - CookieSameSite::STRICT_MODE, COOKIE_PRIORITY_LOW); + CookieSameSite::STRICT_MODE, COOKIE_PRIORITY_LOW, same_party); EXPECT_TRUE(cookie->IsEquivalent(*other_cookie)); EXPECT_TRUE(cookie->IsEquivalentForSecureCookieMatching(*other_cookie)); EXPECT_TRUE(other_cookie->IsEquivalentForSecureCookieMatching(*cookie)); @@ -350,7 +456,7 @@ TEST(CanonicalCookieTest, IsEquivalent) { other_cookie = std::make_unique<CanonicalCookie>( "B", cookie_value, cookie_domain, cookie_path, creation_time, expiration_time, base::Time(), secure, httponly, same_site, - COOKIE_PRIORITY_MEDIUM); + COOKIE_PRIORITY_MEDIUM, same_party); EXPECT_FALSE(cookie->IsEquivalent(*other_cookie)); EXPECT_FALSE(cookie->IsEquivalentForSecureCookieMatching(*other_cookie)); EXPECT_FALSE(other_cookie->IsEquivalentForSecureCookieMatching(*cookie)); @@ -361,7 +467,7 @@ TEST(CanonicalCookieTest, IsEquivalent) { other_cookie = std::make_unique<CanonicalCookie>( cookie_name, cookie_value, "www.example.com", cookie_path, creation_time, expiration_time, base::Time(), secure, httponly, same_site, - COOKIE_PRIORITY_MEDIUM); + COOKIE_PRIORITY_MEDIUM, same_party); EXPECT_TRUE(cookie->IsDomainCookie()); EXPECT_FALSE(other_cookie->IsDomainCookie()); EXPECT_FALSE(cookie->IsEquivalent(*other_cookie)); @@ -373,7 +479,7 @@ TEST(CanonicalCookieTest, IsEquivalent) { other_cookie = std::make_unique<CanonicalCookie>( cookie_name, cookie_value, ".example.com", cookie_path, creation_time, expiration_time, base::Time(), secure, httponly, same_site, - COOKIE_PRIORITY_MEDIUM); + COOKIE_PRIORITY_MEDIUM, same_party); EXPECT_FALSE(cookie->IsEquivalent(*other_cookie)); EXPECT_TRUE(cookie->IsEquivalentForSecureCookieMatching(*other_cookie)); EXPECT_TRUE(other_cookie->IsEquivalentForSecureCookieMatching(*cookie)); @@ -386,7 +492,7 @@ TEST(CanonicalCookieTest, IsEquivalent) { other_cookie = std::make_unique<CanonicalCookie>( cookie_name, cookie_value, cookie_domain, "/test", creation_time, expiration_time, base::Time(), secure, httponly, same_site, - COOKIE_PRIORITY_MEDIUM); + COOKIE_PRIORITY_MEDIUM, same_party); EXPECT_FALSE(cookie->IsEquivalent(*other_cookie)); EXPECT_FALSE(cookie->IsEquivalentForSecureCookieMatching(*other_cookie)); EXPECT_FALSE(other_cookie->IsEquivalentForSecureCookieMatching(*cookie)); @@ -394,7 +500,7 @@ TEST(CanonicalCookieTest, IsEquivalent) { other_cookie = std::make_unique<CanonicalCookie>( cookie_name, cookie_value, cookie_domain, cookie_path + "/subpath", creation_time, expiration_time, base::Time(), secure, httponly, same_site, - COOKIE_PRIORITY_MEDIUM); + COOKIE_PRIORITY_MEDIUM, same_party); EXPECT_FALSE(cookie->IsEquivalent(*other_cookie)); // The path comparison is asymmetric EXPECT_FALSE(cookie->IsEquivalentForSecureCookieMatching(*other_cookie)); @@ -403,7 +509,7 @@ TEST(CanonicalCookieTest, IsEquivalent) { other_cookie = std::make_unique<CanonicalCookie>( cookie_name, cookie_value, cookie_domain, "/", creation_time, expiration_time, base::Time(), secure, httponly, same_site, - COOKIE_PRIORITY_MEDIUM); + COOKIE_PRIORITY_MEDIUM, same_party); EXPECT_FALSE(cookie->IsEquivalent(*other_cookie)); EXPECT_TRUE(cookie->IsEquivalentForSecureCookieMatching(*other_cookie)); EXPECT_FALSE(other_cookie->IsEquivalentForSecureCookieMatching(*cookie)); @@ -445,12 +551,12 @@ TEST(CanonicalCookieTest, IsEquivalentForSecureCookieMatching) { auto cookie = std::make_unique<CanonicalCookie>( test.cookie.name, "value1", test.cookie.domain, test.cookie.path, base::Time(), base::Time(), base::Time(), false /* secure */, false, - CookieSameSite::LAX_MODE, COOKIE_PRIORITY_MEDIUM); + CookieSameSite::LAX_MODE, COOKIE_PRIORITY_MEDIUM, false); auto secure_cookie = std::make_unique<CanonicalCookie>( test.secure_cookie.name, "value2", test.secure_cookie.domain, test.secure_cookie.path, base::Time(), base::Time(), base::Time(), true /* secure */, false, CookieSameSite::LAX_MODE, - COOKIE_PRIORITY_MEDIUM); + COOKIE_PRIORITY_MEDIUM, false); EXPECT_EQ(test.equivalent, cookie->IsEquivalentForSecureCookieMatching(*secure_cookie)); @@ -534,7 +640,7 @@ void VerifyEffectiveSameSiteTestCases( CanonicalCookie cookie("A", "2", "example.test", "/", creation_time, expiry_time, base::Time(), true /* secure */, false /* httponly */, test_case.same_site, - COOKIE_PRIORITY_DEFAULT); + COOKIE_PRIORITY_DEFAULT, false); EXPECT_EQ( test_case.effective_same_site, cookie.GetEffectiveSameSiteForTesting(test_case.access_semantics)); @@ -1196,7 +1302,7 @@ TEST(CanonicalCookieTest, MultipleExclusionReasons) { CanonicalCookie cookie1 = CanonicalCookie( "name", "value", "other-domain.com", "/bar", creation_time, base::Time(), base::Time(), true /* secure */, true /* httponly */, - CookieSameSite::STRICT_MODE, COOKIE_PRIORITY_DEFAULT); + CookieSameSite::STRICT_MODE, COOKIE_PRIORITY_DEFAULT, false); EXPECT_TRUE(cookie1.IncludeForRequestURL(url, options) .status.HasExactlyExclusionReasonsForTesting( {CookieInclusionStatus::EXCLUDE_HTTP_ONLY, @@ -1390,42 +1496,42 @@ TEST(CanonicalCookieTest, IsCanonical) { EXPECT_TRUE(CanonicalCookie("A", "B", "x.y", "/path", base::Time(), base::Time(), base::Time(), false, false, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_LOW) + COOKIE_PRIORITY_LOW, false) .IsCanonical()); // Newline in name. EXPECT_FALSE(CanonicalCookie("A\n", "B", "x.y", "/path", base::Time(), base::Time(), base::Time(), false, false, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_LOW) + COOKIE_PRIORITY_LOW, false) .IsCanonical()); // Carriage return in name. EXPECT_FALSE(CanonicalCookie("A\r", "B", "x.y", "/path", base::Time(), base::Time(), base::Time(), false, false, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_LOW) + COOKIE_PRIORITY_LOW, false) .IsCanonical()); // Null character in name. EXPECT_FALSE(CanonicalCookie(std::string("A\0Z", 3), "B", "x.y", "/path", base::Time(), base::Time(), base::Time(), false, false, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_LOW) + COOKIE_PRIORITY_LOW, false) .IsCanonical()); // Name begins with whitespace. EXPECT_FALSE(CanonicalCookie(" A", "B", "x.y", "/path", base::Time(), base::Time(), base::Time(), false, false, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_LOW) + COOKIE_PRIORITY_LOW, false) .IsCanonical()); // Name ends with whitespace. EXPECT_FALSE(CanonicalCookie("A ", "B", "x.y", "/path", base::Time(), base::Time(), base::Time(), false, false, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_LOW) + COOKIE_PRIORITY_LOW, false) .IsCanonical()); // Empty name. (Note this is against the spec but compatible with other @@ -1433,70 +1539,70 @@ TEST(CanonicalCookieTest, IsCanonical) { EXPECT_TRUE(CanonicalCookie("", "B", "x.y", "/path", base::Time(), base::Time(), base::Time(), false, false, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_LOW) + COOKIE_PRIORITY_LOW, false) .IsCanonical()); // Space in name EXPECT_TRUE(CanonicalCookie("A C", "B", "x.y", "/path", base::Time(), base::Time(), base::Time(), false, false, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_LOW) + COOKIE_PRIORITY_LOW, false) .IsCanonical()); // Extra space suffixing name. EXPECT_FALSE(CanonicalCookie("A ", "B", "x.y", "/path", base::Time(), base::Time(), base::Time(), false, false, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_LOW) + COOKIE_PRIORITY_LOW, false) .IsCanonical()); // '=' character in name. EXPECT_FALSE(CanonicalCookie("A=", "B", "x.y", "/path", base::Time(), base::Time(), base::Time(), false, false, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_LOW) + COOKIE_PRIORITY_LOW, false) .IsCanonical()); // Separator in name. EXPECT_FALSE(CanonicalCookie("A;", "B", "x.y", "/path", base::Time(), base::Time(), base::Time(), false, false, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_LOW) + COOKIE_PRIORITY_LOW, false) .IsCanonical()); // '=' character in value. EXPECT_TRUE(CanonicalCookie("A", "B=", "x.y", "/path", base::Time(), base::Time(), base::Time(), false, false, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_LOW) + COOKIE_PRIORITY_LOW, false) .IsCanonical()); // Separator in value. EXPECT_FALSE(CanonicalCookie("A", "B;", "x.y", "/path", base::Time(), base::Time(), base::Time(), false, false, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_LOW) + COOKIE_PRIORITY_LOW, false) .IsCanonical()); // Separator in domain. EXPECT_FALSE(CanonicalCookie("A", "B", ";x.y", "/path", base::Time(), base::Time(), base::Time(), false, false, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_LOW) + COOKIE_PRIORITY_LOW, false) .IsCanonical()); // Garbage in domain. EXPECT_FALSE(CanonicalCookie("A", "B", "@:&", "/path", base::Time(), base::Time(), base::Time(), false, false, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_LOW) + COOKIE_PRIORITY_LOW, false) .IsCanonical()); // Space in domain. EXPECT_FALSE(CanonicalCookie("A", "B", "x.y ", "/path", base::Time(), base::Time(), base::Time(), false, false, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_LOW) + COOKIE_PRIORITY_LOW, false) .IsCanonical()); // Empty domain. (This is against cookie spec, but needed for Chrome's @@ -1504,64 +1610,65 @@ TEST(CanonicalCookieTest, IsCanonical) { EXPECT_TRUE(CanonicalCookie("A", "B", "", "/path", base::Time(), base::Time(), base::Time(), false, false, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_LOW) + COOKIE_PRIORITY_LOW, false) .IsCanonical()); // Path does not start with a "/". EXPECT_FALSE(CanonicalCookie("A", "B", "x.y", "path", base::Time(), base::Time(), base::Time(), false, false, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_LOW) + COOKIE_PRIORITY_LOW, false) .IsCanonical()); // Empty path. EXPECT_FALSE(CanonicalCookie("A", "B", "x.y", "", base::Time(), base::Time(), base::Time(), false, false, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_LOW) + COOKIE_PRIORITY_LOW, false) .IsCanonical()); // Simple IPv4 address as domain. EXPECT_TRUE(CanonicalCookie("A", "B", "1.2.3.4", "/path", base::Time(), base::Time(), base::Time(), false, false, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_LOW) + COOKIE_PRIORITY_LOW, false) .IsCanonical()); // NOn-canonical IPv4 address as domain. EXPECT_FALSE(CanonicalCookie("A", "B", "01.2.03.4", "/path", base::Time(), base::Time(), base::Time(), false, false, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_LOW) + COOKIE_PRIORITY_LOW, false) .IsCanonical()); // Null IPv6 address as domain. EXPECT_TRUE(CanonicalCookie("A", "B", "[::]", "/path", base::Time(), base::Time(), base::Time(), false, false, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_LOW) + COOKIE_PRIORITY_LOW, false) .IsCanonical()); // Localhost IPv6 address as domain. EXPECT_TRUE(CanonicalCookie("A", "B", "[::1]", "/path", base::Time(), base::Time(), base::Time(), false, false, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_LOW) + COOKIE_PRIORITY_LOW, false) .IsCanonical()); // Fully speced IPv6 address as domain. - EXPECT_FALSE(CanonicalCookie( - "A", "B", "[2001:0DB8:AC10:FE01:0000:0000:0000:0000]", - "/path", base::Time(), base::Time(), base::Time(), false, - false, CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_LOW) - .IsCanonical()); + EXPECT_FALSE( + CanonicalCookie("A", "B", "[2001:0DB8:AC10:FE01:0000:0000:0000:0000]", + "/path", base::Time(), base::Time(), base::Time(), false, + false, CookieSameSite::NO_RESTRICTION, + COOKIE_PRIORITY_LOW, false) + .IsCanonical()); // Zero abbreviated IPv6 address as domain. Not canonical because of leading // zeros & uppercase hex letters. EXPECT_FALSE(CanonicalCookie("A", "B", "[2001:0DB8:AC10:FE01::]", "/path", base::Time(), base::Time(), base::Time(), false, false, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_LOW) + COOKIE_PRIORITY_LOW, false) .IsCanonical()); // Zero prefixes removed IPv6 address as domain. Not canoncial because of @@ -1569,70 +1676,101 @@ TEST(CanonicalCookieTest, IsCanonical) { EXPECT_FALSE(CanonicalCookie("A", "B", "[2001:DB8:AC10:FE01::]", "/path", base::Time(), base::Time(), base::Time(), false, false, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_LOW) + COOKIE_PRIORITY_LOW, false) .IsCanonical()); // Lowercased hex IPv6 address as domain. EXPECT_TRUE(CanonicalCookie("A", "B", "[2001:db8:ac10:fe01::]", "/path", base::Time(), base::Time(), base::Time(), false, false, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_LOW) + COOKIE_PRIORITY_LOW, false) .IsCanonical()); // Properly formatted host cookie. EXPECT_TRUE(CanonicalCookie("__Host-A", "B", "x.y", "/", base::Time(), base::Time(), base::Time(), true, false, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_LOW) + COOKIE_PRIORITY_LOW, false) .IsCanonical()); // Insecure host cookie. EXPECT_FALSE(CanonicalCookie("__Host-A", "B", "x.y", "/", base::Time(), base::Time(), base::Time(), false, false, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_LOW) + COOKIE_PRIORITY_LOW, false) .IsCanonical()); // Host cookie with non-null path. EXPECT_FALSE(CanonicalCookie("__Host-A", "B", "x.y", "/path", base::Time(), base::Time(), base::Time(), true, false, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_LOW) + COOKIE_PRIORITY_LOW, false) .IsCanonical()); // Host cookie with empty domain. EXPECT_FALSE(CanonicalCookie("__Host-A", "B", "", "/", base::Time(), base::Time(), base::Time(), true, false, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_LOW) + COOKIE_PRIORITY_LOW, false) .IsCanonical()); // Host cookie with period prefixed domain. EXPECT_FALSE(CanonicalCookie("__Host-A", "B", ".x.y", "/", base::Time(), base::Time(), base::Time(), true, false, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_LOW) + COOKIE_PRIORITY_LOW, false) .IsCanonical()); // Properly formatted secure cookie. EXPECT_TRUE(CanonicalCookie("__Secure-A", "B", "x.y", "/", base::Time(), base::Time(), base::Time(), true, false, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_LOW) + COOKIE_PRIORITY_LOW, false) .IsCanonical()); // Insecure secure cookie. EXPECT_FALSE(CanonicalCookie("__Secure-A", "B", "x.y", "/", base::Time(), base::Time(), base::Time(), false, false, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_LOW) + COOKIE_PRIORITY_LOW, false) + .IsCanonical()); + + // SameParty attribute used correctly (with Secure and non-Strict SameSite). + EXPECT_TRUE(CanonicalCookie("A", "B", "x.y", "/", base::Time(), base::Time(), + base::Time(), true, false, + CookieSameSite::NO_RESTRICTION, + COOKIE_PRIORITY_LOW, true) + .IsCanonical()); + EXPECT_TRUE(CanonicalCookie("A", "B", "x.y", "/", base::Time(), base::Time(), + base::Time(), true, false, + CookieSameSite::UNSPECIFIED, COOKIE_PRIORITY_LOW, + true) + .IsCanonical()); + EXPECT_TRUE(CanonicalCookie("A", "B", "x.y", "/", base::Time(), base::Time(), + base::Time(), true, false, + CookieSameSite::LAX_MODE, COOKIE_PRIORITY_LOW, + true) + .IsCanonical()); + + // SameParty without Secure is not canonical. + EXPECT_FALSE(CanonicalCookie("A", "B", "x.y", "/", base::Time(), base::Time(), + base::Time(), false, false, + CookieSameSite::LAX_MODE, COOKIE_PRIORITY_LOW, + true) + .IsCanonical()); + + // SameParty with SameSite=Strict is not canonical. + EXPECT_FALSE(CanonicalCookie("A", "B", "x.y", "/", base::Time(), base::Time(), + base::Time(), true, false, + CookieSameSite::STRICT_MODE, COOKIE_PRIORITY_LOW, + true) .IsCanonical()); } TEST(CanonicalCookieTest, TestSetCreationDate) { - CanonicalCookie cookie("A", "B", "x.y", "/path", base::Time(), base::Time(), - base::Time(), false, false, - CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_LOW); + CanonicalCookie cookie( + "A", "B", "x.y", "/path", base::Time(), base::Time(), base::Time(), false, + false, CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_LOW, false); EXPECT_TRUE(cookie.CreationDate().is_null()); base::Time now(base::Time::Now()); @@ -1727,7 +1865,7 @@ TEST(CanonicalCookieTest, CreateSanitizedCookie_Inputs) { GURL("https://www.foo.com"), "A", "B", std::string(), "/foo", base::Time(), base::Time(), base::Time(), false /*secure*/, false /*httponly*/, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_DEFAULT); + COOKIE_PRIORITY_DEFAULT, false /*same_party*/); EXPECT_TRUE(cc); EXPECT_EQ("A", cc->Name()); EXPECT_EQ("B", cc->Value()); @@ -1740,6 +1878,7 @@ TEST(CanonicalCookieTest, CreateSanitizedCookie_Inputs) { EXPECT_FALSE(cc->IsHttpOnly()); EXPECT_EQ(CookieSameSite::NO_RESTRICTION, cc->SameSite()); EXPECT_EQ(COOKIE_PRIORITY_MEDIUM, cc->Priority()); + EXPECT_FALSE(cc->IsSameParty()); EXPECT_FALSE(cc->IsDomainCookie()); // Creation date @@ -1747,7 +1886,7 @@ TEST(CanonicalCookieTest, CreateSanitizedCookie_Inputs) { GURL("https://www.foo.com"), "A", "B", std::string(), "/foo", two_hours_ago, base::Time(), base::Time(), false /*secure*/, false /*httponly*/, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_DEFAULT); + COOKIE_PRIORITY_DEFAULT, false /*same_party*/); EXPECT_TRUE(cc); EXPECT_EQ(two_hours_ago, cc->CreationDate()); @@ -1756,7 +1895,7 @@ TEST(CanonicalCookieTest, CreateSanitizedCookie_Inputs) { GURL("https://www.foo.com"), "A", "B", std::string(), "/foo", two_hours_ago, base::Time(), one_hour_ago, false /*secure*/, false /*httponly*/, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_DEFAULT); + COOKIE_PRIORITY_DEFAULT, false /*same_party*/); EXPECT_TRUE(cc); EXPECT_EQ(one_hour_ago, cc->LastAccessDate()); @@ -1765,7 +1904,7 @@ TEST(CanonicalCookieTest, CreateSanitizedCookie_Inputs) { GURL("https://www.foo.com"), "A", "B", std::string(), "/foo", base::Time(), one_hour_from_now, base::Time(), false /*secure*/, false /*httponly*/, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_DEFAULT); + COOKIE_PRIORITY_DEFAULT, false /*same_party*/); EXPECT_TRUE(cc); EXPECT_EQ(one_hour_from_now, cc->ExpiryDate()); @@ -1774,7 +1913,7 @@ TEST(CanonicalCookieTest, CreateSanitizedCookie_Inputs) { GURL("https://www.foo.com"), "A", "B", std::string(), "/foo", base::Time(), base::Time(), base::Time(), true /*secure*/, false /*httponly*/, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_DEFAULT); + COOKIE_PRIORITY_DEFAULT, false /*same_party*/); EXPECT_TRUE(cc); EXPECT_TRUE(cc->IsSecure()); @@ -1783,7 +1922,7 @@ TEST(CanonicalCookieTest, CreateSanitizedCookie_Inputs) { GURL("https://www.foo.com"), "A", "B", std::string(), "/foo", base::Time(), base::Time(), base::Time(), false /*secure*/, true /*httponly*/, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_DEFAULT); + COOKIE_PRIORITY_DEFAULT, false /*same_party*/); EXPECT_TRUE(cc); EXPECT_TRUE(cc->IsHttpOnly()); @@ -1791,7 +1930,8 @@ TEST(CanonicalCookieTest, CreateSanitizedCookie_Inputs) { cc = CanonicalCookie::CreateSanitizedCookie( GURL("https://www.foo.com"), "A", "B", std::string(), "/foo", base::Time(), base::Time(), base::Time(), false /*secure*/, - false /*httponly*/, CookieSameSite::LAX_MODE, COOKIE_PRIORITY_DEFAULT); + false /*httponly*/, CookieSameSite::LAX_MODE, COOKIE_PRIORITY_DEFAULT, + false /*same_party*/); EXPECT_TRUE(cc); EXPECT_EQ(CookieSameSite::LAX_MODE, cc->SameSite()); @@ -1799,7 +1939,8 @@ TEST(CanonicalCookieTest, CreateSanitizedCookie_Inputs) { cc = CanonicalCookie::CreateSanitizedCookie( GURL("https://www.foo.com"), "A", "B", std::string(), "/foo", base::Time(), base::Time(), base::Time(), false /*secure*/, - false /*httponly*/, CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_LOW); + false /*httponly*/, CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_LOW, + false /*same_party*/); EXPECT_TRUE(cc); EXPECT_EQ(COOKIE_PRIORITY_LOW, cc->Priority()); @@ -1808,9 +1949,18 @@ TEST(CanonicalCookieTest, CreateSanitizedCookie_Inputs) { GURL("https://www.foo.com"), "A", "B", "www.foo.com", "/foo", base::Time(), base::Time(), base::Time(), false /*secure*/, false /*httponly*/, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_DEFAULT); + COOKIE_PRIORITY_DEFAULT, false /*same_party*/); EXPECT_TRUE(cc); EXPECT_TRUE(cc->IsDomainCookie()); + + // SameParty + cc = CanonicalCookie::CreateSanitizedCookie( + GURL("https://www.foo.com"), "A", "B", std::string(), "/foo", + base::Time(), base::Time(), base::Time(), true /*secure*/, + false /*httponly*/, CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_LOW, + true /*same_party*/); + EXPECT_TRUE(cc); + EXPECT_TRUE(cc->IsSameParty()); } // Make sure sanitization and blocking of cookies works correctly. @@ -1825,94 +1975,97 @@ TEST(CanonicalCookieTest, CreateSanitizedCookie_Logic) { GURL("http://www.foo.com/foo"), "A", "B", std::string(), "/foo", one_hour_ago, one_hour_from_now, base::Time(), false /*secure*/, false /*httponly*/, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_DEFAULT)); + COOKIE_PRIORITY_DEFAULT, false /*same_party*/)); EXPECT_TRUE(CanonicalCookie::CreateSanitizedCookie( GURL("http://www.foo.com/bar"), "C", "D", "www.foo.com", "/", two_hours_ago, base::Time(), one_hour_ago, false /*secure*/, true /*httponly*/, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_DEFAULT)); + COOKIE_PRIORITY_DEFAULT, false /*same_party*/)); EXPECT_TRUE(CanonicalCookie::CreateSanitizedCookie( GURL("https://www.foo.com"), "E", "F", std::string(), std::string(), base::Time(), base::Time(), base::Time(), true /*secure*/, false /*httponly*/, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_DEFAULT)); + COOKIE_PRIORITY_DEFAULT, false /*same_party*/)); // Test the file:// protocol. EXPECT_TRUE(CanonicalCookie::CreateSanitizedCookie( GURL("file:///"), "A", "B", std::string(), "/foo", one_hour_ago, one_hour_from_now, base::Time(), false /*secure*/, false /*httponly*/, - CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT)); + CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT, + false /*same_party*/)); EXPECT_TRUE(CanonicalCookie::CreateSanitizedCookie( GURL("file:///home/user/foo.txt"), "A", "B", std::string(), "/foo", one_hour_ago, one_hour_from_now, base::Time(), false /*secure*/, false /*httponly*/, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_DEFAULT)); + COOKIE_PRIORITY_DEFAULT, false /*same_party*/)); EXPECT_FALSE(CanonicalCookie::CreateSanitizedCookie( GURL("file:///home/user/foo.txt"), "A", "B", "home", "/foo", one_hour_ago, one_hour_from_now, base::Time(), false /*secure*/, false /*httponly*/, - CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT)); + CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT, + false /*same_party*/)); // Test that malformed attributes fail to set the cookie. EXPECT_FALSE(CanonicalCookie::CreateSanitizedCookie( GURL("http://www.foo.com/foo"), " A", "B", std::string(), "/foo", base::Time(), base::Time(), base::Time(), false /*secure*/, false /*httponly*/, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_DEFAULT)); + COOKIE_PRIORITY_DEFAULT, false /*same_party*/)); EXPECT_FALSE(CanonicalCookie::CreateSanitizedCookie( GURL("http://www.foo.com/foo"), "A;", "B", std::string(), "/foo", base::Time(), base::Time(), base::Time(), false /*secure*/, false /*httponly*/, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_DEFAULT)); + COOKIE_PRIORITY_DEFAULT, false /*same_party*/)); EXPECT_FALSE(CanonicalCookie::CreateSanitizedCookie( GURL("http://www.foo.com/foo"), "A=", "B", std::string(), "/foo", base::Time(), base::Time(), base::Time(), false /*secure*/, false /*httponly*/, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_DEFAULT)); + COOKIE_PRIORITY_DEFAULT, false /*same_party*/)); EXPECT_FALSE(CanonicalCookie::CreateSanitizedCookie( GURL("http://www.foo.com/foo"), "A\x07", "B", std::string(), "/foo", one_hour_ago, one_hour_from_now, base::Time(), false /*secure*/, false /*httponly*/, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_DEFAULT)); + COOKIE_PRIORITY_DEFAULT, false /*same_party*/)); EXPECT_FALSE(CanonicalCookie::CreateSanitizedCookie( GURL("http://www.foo.com"), "A", " B", std::string(), "/foo", base::Time(), base::Time(), base::Time(), false /*secure*/, false /*httponly*/, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_DEFAULT)); + COOKIE_PRIORITY_DEFAULT, false /*same_party*/)); EXPECT_FALSE(CanonicalCookie::CreateSanitizedCookie( GURL("http://www.foo.com"), "A", "\x0fZ", std::string(), "/foo", base::Time(), base::Time(), base::Time(), false /*secure*/, false /*httponly*/, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_DEFAULT)); + COOKIE_PRIORITY_DEFAULT, false /*same_party*/)); EXPECT_FALSE(CanonicalCookie::CreateSanitizedCookie( GURL("http://www.foo.com"), "A", "B", "www.foo.com ", "/foo", base::Time(), base::Time(), base::Time(), false /*secure*/, false /*httponly*/, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_DEFAULT)); + COOKIE_PRIORITY_DEFAULT, false /*same_party*/)); EXPECT_FALSE(CanonicalCookie::CreateSanitizedCookie( GURL("http://www.foo.com/foo"), "A", "B", "foo.ozzzzzzle", "/foo", base::Time(), base::Time(), base::Time(), false /*secure*/, false /*httponly*/, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_DEFAULT)); + COOKIE_PRIORITY_DEFAULT, false /*same_party*/)); EXPECT_FALSE(CanonicalCookie::CreateSanitizedCookie( GURL("http://www.foo.com/foo"), "A", "B", std::string(), "foo", base::Time(), base::Time(), base::Time(), false /*secure*/, false /*httponly*/, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_DEFAULT)); + COOKIE_PRIORITY_DEFAULT, false /*same_party*/)); EXPECT_FALSE(CanonicalCookie::CreateSanitizedCookie( GURL("http://www.foo.com"), "A", "B", std::string(), "/foo ", base::Time(), base::Time(), base::Time(), false /*secure*/, false /*httponly*/, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_DEFAULT)); + COOKIE_PRIORITY_DEFAULT, false /*same_party*/)); EXPECT_FALSE(CanonicalCookie::CreateSanitizedCookie( GURL("http://www.foo.com/foo"), "A", "B", "%2Efoo.com", "/foo", one_hour_ago, one_hour_from_now, base::Time(), false /*secure*/, false /*httponly*/, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_DEFAULT)); + COOKIE_PRIORITY_DEFAULT, false /*same_party*/)); EXPECT_FALSE(CanonicalCookie::CreateSanitizedCookie( GURL("http://domaintest.%E3%81%BF%E3%82%93%E3%81%AA"), "A", "B", "domaintest.%E3%81%BF%E3%82%93%E3%81%AA", "/foo", base::Time(), base::Time(), base::Time(), false /*secure*/, false /*httponly*/, - CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT)); + CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT, + false /*same_party*/)); std::unique_ptr<CanonicalCookie> cc; @@ -1922,7 +2075,7 @@ TEST(CanonicalCookieTest, CreateSanitizedCookie_Logic) { GURL("http://www.foo.com/foo"), "A", "B", "www.foo.com", "/foo", one_hour_ago, one_hour_from_now, base::Time(), false /*secure*/, false /*httponly*/, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_DEFAULT); + COOKIE_PRIORITY_DEFAULT, false /*same_party*/); ASSERT_TRUE(cc); EXPECT_TRUE(cc->IsDomainCookie()); EXPECT_EQ(".www.foo.com", cc->Domain()); @@ -1931,7 +2084,7 @@ TEST(CanonicalCookieTest, CreateSanitizedCookie_Logic) { GURL("http://www.foo.com/foo"), "A", "B", ".www.foo.com", "/foo", one_hour_ago, one_hour_from_now, base::Time(), false /*secure*/, false /*httponly*/, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_DEFAULT); + COOKIE_PRIORITY_DEFAULT, false /*same_party*/); ASSERT_TRUE(cc); EXPECT_TRUE(cc->IsDomainCookie()); EXPECT_EQ(".www.foo.com", cc->Domain()); @@ -1940,7 +2093,7 @@ TEST(CanonicalCookieTest, CreateSanitizedCookie_Logic) { GURL("http://www.foo.com/foo"), "A", "B", ".foo.com", "/foo", one_hour_ago, one_hour_from_now, base::Time(), false /*secure*/, false /*httponly*/, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_DEFAULT); + COOKIE_PRIORITY_DEFAULT, false /*same_party*/); ASSERT_TRUE(cc); EXPECT_TRUE(cc->IsDomainCookie()); EXPECT_EQ(".foo.com", cc->Domain()); @@ -1949,7 +2102,7 @@ TEST(CanonicalCookieTest, CreateSanitizedCookie_Logic) { GURL("http://www.foo.com/foo"), "A", "B", ".www2.www.foo.com", "/foo", one_hour_ago, one_hour_from_now, base::Time(), false /*secure*/, false /*httponly*/, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_DEFAULT); + COOKIE_PRIORITY_DEFAULT, false /*same_party*/); EXPECT_FALSE(cc); // Secure/URL Scheme mismatch. @@ -1957,26 +2110,28 @@ TEST(CanonicalCookieTest, CreateSanitizedCookie_Logic) { GURL("http://www.foo.com"), "A", "B", std::string(), "/foo ", base::Time(), base::Time(), base::Time(), true /*secure*/, false /*httponly*/, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_DEFAULT)); + COOKIE_PRIORITY_DEFAULT, false /*same_party*/)); // Null creation date/non-null last access date conflict. EXPECT_FALSE(CanonicalCookie::CreateSanitizedCookie( GURL("http://www.foo.com"), "A", "B", std::string(), "/foo", base::Time(), base::Time(), base::Time::Now(), false /*secure*/, false /*httponly*/, - CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT)); + CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT, + false /*same_party*/)); // Domain doesn't match URL EXPECT_FALSE(CanonicalCookie::CreateSanitizedCookie( GURL("http://www.foo.com"), "A", "B", "www.bar.com", "/", base::Time(), base::Time(), base::Time(), false /*secure*/, false /*httponly*/, - CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT)); + CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT, + false /*same_party*/)); // Path with unusual characters escaped. cc = CanonicalCookie::CreateSanitizedCookie( GURL("http://www.foo.com"), "A", "B", std::string(), "/foo", base::Time(), base::Time(), base::Time(), false /*secure*/, false /*httponly*/, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_DEFAULT); + COOKIE_PRIORITY_DEFAULT, false /*same_party*/); ASSERT_TRUE(cc); EXPECT_EQ("/foo%7F", cc->Path()); @@ -1984,79 +2139,116 @@ TEST(CanonicalCookieTest, CreateSanitizedCookie_Logic) { EXPECT_FALSE(CanonicalCookie::CreateSanitizedCookie( GURL("http://www.foo.com"), "", "", std::string(), "/", base::Time(), base::Time(), base::Time(), false /*secure*/, false /*httponly*/, - CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT)); + CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT, + false /*same_party*/)); // A __Secure- cookie must be Secure. EXPECT_TRUE(CanonicalCookie::CreateSanitizedCookie( GURL("https://www.foo.com"), "__Secure-A", "B", ".www.foo.com", "/", two_hours_ago, one_hour_from_now, one_hour_ago, true, false, - CookieSameSite::NO_RESTRICTION, CookiePriority::COOKIE_PRIORITY_DEFAULT)); + CookieSameSite::NO_RESTRICTION, CookiePriority::COOKIE_PRIORITY_DEFAULT, + false)); EXPECT_FALSE(CanonicalCookie::CreateSanitizedCookie( GURL("https://www.foo.com"), "__Secure-A", "B", ".www.foo.com", "/", two_hours_ago, one_hour_from_now, one_hour_ago, false, false, - CookieSameSite::NO_RESTRICTION, CookiePriority::COOKIE_PRIORITY_DEFAULT)); + CookieSameSite::NO_RESTRICTION, CookiePriority::COOKIE_PRIORITY_DEFAULT, + false)); // A __Host- cookie must be Secure. EXPECT_TRUE(CanonicalCookie::CreateSanitizedCookie( GURL("https://www.foo.com"), "__Host-A", "B", std::string(), "/", two_hours_ago, one_hour_from_now, one_hour_ago, true, false, - CookieSameSite::NO_RESTRICTION, CookiePriority::COOKIE_PRIORITY_DEFAULT)); + CookieSameSite::NO_RESTRICTION, CookiePriority::COOKIE_PRIORITY_DEFAULT, + false)); EXPECT_FALSE(CanonicalCookie::CreateSanitizedCookie( GURL("https://www.foo.com"), "__Host-A", "B", std::string(), "/", two_hours_ago, one_hour_from_now, one_hour_ago, false, false, - CookieSameSite::NO_RESTRICTION, CookiePriority::COOKIE_PRIORITY_DEFAULT)); + CookieSameSite::NO_RESTRICTION, CookiePriority::COOKIE_PRIORITY_DEFAULT, + false)); // A __Host- cookie must have path "/". EXPECT_TRUE(CanonicalCookie::CreateSanitizedCookie( GURL("https://www.foo.com"), "__Host-A", "B", std::string(), "/", two_hours_ago, one_hour_from_now, one_hour_ago, true, false, - CookieSameSite::NO_RESTRICTION, CookiePriority::COOKIE_PRIORITY_DEFAULT)); + CookieSameSite::NO_RESTRICTION, CookiePriority::COOKIE_PRIORITY_DEFAULT, + false)); EXPECT_FALSE(CanonicalCookie::CreateSanitizedCookie( GURL("https://www.foo.com"), "__Host-A", "B", std::string(), "/foo", two_hours_ago, one_hour_from_now, one_hour_ago, true, false, - CookieSameSite::NO_RESTRICTION, CookiePriority::COOKIE_PRIORITY_DEFAULT)); + CookieSameSite::NO_RESTRICTION, CookiePriority::COOKIE_PRIORITY_DEFAULT, + false)); // A __Host- cookie must not specify a domain. EXPECT_TRUE(CanonicalCookie::CreateSanitizedCookie( GURL("https://www.foo.com"), "__Host-A", "B", std::string(), "/", two_hours_ago, one_hour_from_now, one_hour_ago, true, false, - CookieSameSite::NO_RESTRICTION, CookiePriority::COOKIE_PRIORITY_DEFAULT)); + CookieSameSite::NO_RESTRICTION, CookiePriority::COOKIE_PRIORITY_DEFAULT, + false)); EXPECT_FALSE(CanonicalCookie::CreateSanitizedCookie( GURL("https://www.foo.com"), "__Host-A", "B", ".www.foo.com", "/", two_hours_ago, one_hour_from_now, one_hour_ago, true, false, - CookieSameSite::NO_RESTRICTION, CookiePriority::COOKIE_PRIORITY_DEFAULT)); + CookieSameSite::NO_RESTRICTION, CookiePriority::COOKIE_PRIORITY_DEFAULT, + false)); // Without __Host- prefix, this is a valid host cookie because it does not // specify a domain. EXPECT_TRUE(CanonicalCookie::CreateSanitizedCookie( GURL("https://www.foo.com"), "A", "B", std::string(), "/", two_hours_ago, one_hour_from_now, one_hour_ago, true, false, - CookieSameSite::NO_RESTRICTION, CookiePriority::COOKIE_PRIORITY_DEFAULT)); + CookieSameSite::NO_RESTRICTION, CookiePriority::COOKIE_PRIORITY_DEFAULT, + false)); // Without __Host- prefix, this is a valid domain (not host) cookie. EXPECT_TRUE(CanonicalCookie::CreateSanitizedCookie( GURL("https://www.foo.com"), "A", "B", ".www.foo.com", "/", two_hours_ago, one_hour_from_now, one_hour_ago, true, false, - CookieSameSite::NO_RESTRICTION, CookiePriority::COOKIE_PRIORITY_DEFAULT)); + CookieSameSite::NO_RESTRICTION, CookiePriority::COOKIE_PRIORITY_DEFAULT, + false)); // The __Host- prefix should not prevent otherwise-valid host cookies from // being accepted. EXPECT_TRUE(CanonicalCookie::CreateSanitizedCookie( GURL("https://127.0.0.1"), "A", "B", std::string(), "/", two_hours_ago, one_hour_from_now, one_hour_ago, true, false, - CookieSameSite::NO_RESTRICTION, CookiePriority::COOKIE_PRIORITY_DEFAULT)); + CookieSameSite::NO_RESTRICTION, CookiePriority::COOKIE_PRIORITY_DEFAULT, + false)); EXPECT_TRUE(CanonicalCookie::CreateSanitizedCookie( GURL("https://127.0.0.1"), "__Host-A", "B", std::string(), "/", two_hours_ago, one_hour_from_now, one_hour_ago, true, false, - CookieSameSite::NO_RESTRICTION, CookiePriority::COOKIE_PRIORITY_DEFAULT)); + CookieSameSite::NO_RESTRICTION, CookiePriority::COOKIE_PRIORITY_DEFAULT, + false)); // Host cookies should not specify domain unless it is an IP address that // matches the URL. EXPECT_TRUE(CanonicalCookie::CreateSanitizedCookie( GURL("https://127.0.0.1"), "A", "B", "127.0.0.1", "/", two_hours_ago, one_hour_from_now, one_hour_ago, true, false, - CookieSameSite::NO_RESTRICTION, CookiePriority::COOKIE_PRIORITY_DEFAULT)); + CookieSameSite::NO_RESTRICTION, CookiePriority::COOKIE_PRIORITY_DEFAULT, + false)); EXPECT_TRUE(CanonicalCookie::CreateSanitizedCookie( GURL("https://127.0.0.1"), "__Host-A", "B", "127.0.0.1", "/", two_hours_ago, one_hour_from_now, one_hour_ago, true, false, - CookieSameSite::NO_RESTRICTION, CookiePriority::COOKIE_PRIORITY_DEFAULT)); + CookieSameSite::NO_RESTRICTION, CookiePriority::COOKIE_PRIORITY_DEFAULT, + false)); + + // SameParty attribute requires Secure and forbids SameSite=Strict. + EXPECT_TRUE(CanonicalCookie::CreateSanitizedCookie( + GURL("https://www.foo.com"), "A", "B", ".www.foo.com", "/", two_hours_ago, + one_hour_from_now, one_hour_ago, true /*secure*/, false, + CookieSameSite::NO_RESTRICTION, CookiePriority::COOKIE_PRIORITY_DEFAULT, + true /*same_party*/)); + EXPECT_FALSE(CanonicalCookie::CreateSanitizedCookie( + GURL("https://www.foo.com"), "A", "B", ".www.foo.com", "/", two_hours_ago, + one_hour_from_now, one_hour_ago, false /*secure*/, false, + CookieSameSite::LAX_MODE, CookiePriority::COOKIE_PRIORITY_DEFAULT, + true /*same_party*/)); + EXPECT_FALSE(CanonicalCookie::CreateSanitizedCookie( + GURL("https://www.foo.com"), "A", "B", ".www.foo.com", "/", two_hours_ago, + one_hour_from_now, one_hour_ago, true /*secure*/, false, + CookieSameSite::STRICT_MODE, CookiePriority::COOKIE_PRIORITY_DEFAULT, + true /*same_party*/)); + EXPECT_FALSE(CanonicalCookie::CreateSanitizedCookie( + GURL("https://www.foo.com"), "A", "B", ".www.foo.com", "/", two_hours_ago, + one_hour_from_now, one_hour_ago, false /*secure*/, false, + CookieSameSite::STRICT_MODE, CookiePriority::COOKIE_PRIORITY_DEFAULT, + true /*same_party*/)); // Check that CreateSanitizedCookie can gracefully fail on inputs that would // crash cookie_util::GetCookieDomainWithString due to failing @@ -2066,30 +2258,30 @@ TEST(CanonicalCookieTest, CreateSanitizedCookie_Logic) { EXPECT_FALSE(CanonicalCookie::CreateSanitizedCookie( GURL("http://..."), "A", "B", "...", "/", base::Time(), base::Time(), base::Time(), false /*secure*/, false /*httponly*/, - CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT)); + CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT, false)); EXPECT_FALSE(CanonicalCookie::CreateSanitizedCookie( GURL("http://."), "A", "B", std::string(), "/", base::Time(), base::Time(), base::Time(), false /*secure*/, false /*httponly*/, - CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT)); + CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT, false)); EXPECT_FALSE(CanonicalCookie::CreateSanitizedCookie( GURL("http://.chromium.org"), "A", "B", ".chromium.org", "/", base::Time(), base::Time(), base::Time(), false /*secure*/, false /*httponly*/, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_DEFAULT)); + COOKIE_PRIORITY_DEFAULT, false)); // Check that a file URL with an IPv6 host, and matching IPv6 domain, are // valid. EXPECT_TRUE(CanonicalCookie::CreateSanitizedCookie( GURL("file://[A::]"), "A", "B", "[A::]", "", base::Time(), base::Time(), base::Time(), false /*secure*/, false /*httponly*/, - CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT)); + CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT, false)); // On Windows, URLs beginning with two backslashes are considered file // URLs. On other platforms, they are invalid. auto double_backslash_ipv6_cookie = CanonicalCookie::CreateSanitizedCookie( GURL("\\\\[A::]"), "A", "B", "[A::]", "", base::Time(), base::Time(), base::Time(), false /*secure*/, false /*httponly*/, - CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT); + CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT, false); #if defined(OS_WIN) EXPECT_TRUE(double_backslash_ipv6_cookie); EXPECT_TRUE(double_backslash_ipv6_cookie->IsCanonical()); @@ -2098,6 +2290,69 @@ TEST(CanonicalCookieTest, CreateSanitizedCookie_Logic) { #endif } +TEST(CanonicalCookieTest, FromStorage) { + base::Time two_hours_ago = base::Time::Now() - base::TimeDelta::FromHours(2); + base::Time one_hour_ago = base::Time::Now() - base::TimeDelta::FromHours(1); + base::Time one_hour_from_now = + base::Time::Now() + base::TimeDelta::FromHours(1); + + std::unique_ptr<CanonicalCookie> cc = CanonicalCookie::FromStorage( + "A", "B", "www.foo.com", "/bar", two_hours_ago, one_hour_from_now, + one_hour_ago, false /*secure*/, false /*httponly*/, + CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT, + false /*same_party*/, CookieSourceScheme::kSecure, 87); + EXPECT_TRUE(cc); + EXPECT_EQ("A", cc->Name()); + EXPECT_EQ("B", cc->Value()); + EXPECT_EQ("www.foo.com", cc->Domain()); + EXPECT_EQ("/bar", cc->Path()); + EXPECT_EQ(two_hours_ago, cc->CreationDate()); + EXPECT_EQ(one_hour_ago, cc->LastAccessDate()); + EXPECT_EQ(one_hour_from_now, cc->ExpiryDate()); + EXPECT_FALSE(cc->IsSecure()); + EXPECT_FALSE(cc->IsHttpOnly()); + EXPECT_EQ(CookieSameSite::NO_RESTRICTION, cc->SameSite()); + EXPECT_EQ(COOKIE_PRIORITY_MEDIUM, cc->Priority()); + EXPECT_EQ(CookieSourceScheme::kSecure, cc->SourceScheme()); + EXPECT_FALSE(cc->IsDomainCookie()); + EXPECT_EQ(cc->SourcePort(), 87); + + // Should return nullptr when the cookie is not canonical. + // In this case the cookie is not canonical because its name attribute + // contains a newline character. + EXPECT_FALSE(CanonicalCookie::FromStorage( + "A\n", "B", "www.foo.com", "/bar", two_hours_ago, one_hour_from_now, + one_hour_ago, false /*secure*/, false /*httponly*/, + CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT, + false /*same_party*/, CookieSourceScheme::kSecure, 80)); + + // If the port information gets corrupted out of the valid range + // FromStorage() should result in a PORT_INVALID. + std::unique_ptr<CanonicalCookie> cc2 = CanonicalCookie::FromStorage( + "A", "B", "www.foo.com", "/bar", two_hours_ago, one_hour_from_now, + one_hour_ago, false /*secure*/, false /*httponly*/, + CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT, + false /*same_party*/, CookieSourceScheme::kSecure, 80000); + + EXPECT_EQ(cc2->SourcePort(), url::PORT_INVALID); + + // Test port edge cases: unspecified. + std::unique_ptr<CanonicalCookie> cc3 = CanonicalCookie::FromStorage( + "A", "B", "www.foo.com", "/bar", two_hours_ago, one_hour_from_now, + one_hour_ago, false /*secure*/, false /*httponly*/, + CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT, + false /*same_party*/, CookieSourceScheme::kSecure, url::PORT_UNSPECIFIED); + EXPECT_EQ(cc3->SourcePort(), url::PORT_UNSPECIFIED); + + // Test port edge cases: invalid. + std::unique_ptr<CanonicalCookie> cc4 = CanonicalCookie::FromStorage( + "A", "B", "www.foo.com", "/bar", two_hours_ago, one_hour_from_now, + one_hour_ago, false /*secure*/, false /*httponly*/, + CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT, + false /*same_party*/, CookieSourceScheme::kSecure, url::PORT_INVALID); + EXPECT_EQ(cc4->SourcePort(), url::PORT_INVALID); +} + TEST(CanonicalCookieTest, IsSetPermittedInContext) { GURL url("http://www.example.com/test"); base::Time current_time = base::Time::Now(); @@ -2105,11 +2360,11 @@ TEST(CanonicalCookieTest, IsSetPermittedInContext) { CanonicalCookie cookie_scriptable( "A", "2", "www.example.com", "/test", current_time, base::Time(), base::Time(), true /*secure*/, false /*httponly*/, - CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT); + CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT, false); CanonicalCookie cookie_httponly( "A", "2", "www.example.com", "/test", current_time, base::Time(), base::Time(), true /*secure*/, true /*httponly*/, - CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT); + CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT, false); CookieOptions context_script; CookieOptions context_network; @@ -2158,7 +2413,7 @@ TEST(CanonicalCookieTest, IsSetPermittedInContext) { CanonicalCookie cookie_same_site_unrestricted( "A", "2", "www.example.com", "/test", current_time, base::Time(), base::Time(), true /*secure*/, false /*httponly*/, - CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT); + CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT, false); EXPECT_TRUE(cookie_same_site_unrestricted .IsSetPermittedInContext(context_cross_site) @@ -2224,7 +2479,7 @@ TEST(CanonicalCookieTest, IsSetPermittedInContext) { CanonicalCookie cookie_same_site_lax( "A", "2", "www.example.com", "/test", current_time, base::Time(), base::Time(), true /*secure*/, false /*httponly*/, - CookieSameSite::LAX_MODE, COOKIE_PRIORITY_DEFAULT); + CookieSameSite::LAX_MODE, COOKIE_PRIORITY_DEFAULT, false); EXPECT_TRUE(cookie_same_site_lax.IsSetPermittedInContext(context_cross_site) .status.HasExactlyExclusionReasonsForTesting( @@ -2298,7 +2553,7 @@ TEST(CanonicalCookieTest, IsSetPermittedInContext) { CanonicalCookie cookie_same_site_strict( "A", "2", "www.example.com", "/test", current_time, base::Time(), base::Time(), true /*secure*/, false /*httponly*/, - CookieSameSite::STRICT_MODE, COOKIE_PRIORITY_DEFAULT); + CookieSameSite::STRICT_MODE, COOKIE_PRIORITY_DEFAULT, false); // TODO(morlovich): Do compatibility testing on whether set of strict in lax // context really should be accepted. @@ -2400,7 +2655,7 @@ TEST(CanonicalCookieTest, IsSetPermittedInContext) { CanonicalCookie cookie_same_site_unspecified( "A", "2", "www.example.com", "/test", current_time, base::Time(), base::Time(), true /*secure*/, false /*httponly*/, - CookieSameSite::UNSPECIFIED, COOKIE_PRIORITY_DEFAULT); + CookieSameSite::UNSPECIFIED, COOKIE_PRIORITY_DEFAULT, false); { base::test::ScopedFeatureList feature_list; @@ -2503,7 +2758,7 @@ TEST(CanonicalCookieTest, IsSetPermittedEffectiveSameSite) { CanonicalCookie cookie_no_restriction( "A", "2", "www.example.com", "/test", current_time, base::Time(), base::Time(), true /*secure*/, false /*httponly*/, - CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT); + CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT, false); EXPECT_EQ( cookie_no_restriction @@ -2516,7 +2771,7 @@ TEST(CanonicalCookieTest, IsSetPermittedEffectiveSameSite) { CanonicalCookie cookie_lax("A", "2", "www.example.com", "/test", current_time, base::Time(), base::Time(), true /*secure*/, false /*httponly*/, CookieSameSite::LAX_MODE, - COOKIE_PRIORITY_DEFAULT); + COOKIE_PRIORITY_DEFAULT, false); EXPECT_EQ( cookie_lax @@ -2529,7 +2784,7 @@ TEST(CanonicalCookieTest, IsSetPermittedEffectiveSameSite) { CanonicalCookie cookie_strict( "A", "2", "www.example.com", "/test", current_time, base::Time(), base::Time(), true /*secure*/, false /*httponly*/, - CookieSameSite::STRICT_MODE, COOKIE_PRIORITY_DEFAULT); + CookieSameSite::STRICT_MODE, COOKIE_PRIORITY_DEFAULT, false); EXPECT_EQ( cookie_strict @@ -2543,11 +2798,11 @@ TEST(CanonicalCookieTest, IsSetPermittedEffectiveSameSite) { CanonicalCookie cookie_old_unspecified( "A", "2", "www.example.com", "/test", creation_time, base::Time(), base::Time(), true /*secure*/, false /*httponly*/, - CookieSameSite::UNSPECIFIED, COOKIE_PRIORITY_DEFAULT); + CookieSameSite::UNSPECIFIED, COOKIE_PRIORITY_DEFAULT, false); CanonicalCookie cookie_unspecified( "A", "2", "www.example.com", "/test", current_time, base::Time(), base::Time(), true /*secure*/, false /*httponly*/, - CookieSameSite::UNSPECIFIED, COOKIE_PRIORITY_DEFAULT); + CookieSameSite::UNSPECIFIED, COOKIE_PRIORITY_DEFAULT, false); EXPECT_EQ( cookie_old_unspecified diff --git a/chromium/net/cookies/cookie_constants.cc b/chromium/net/cookies/cookie_constants.cc index bf8dd08c555..117fe0c90f5 100644 --- a/chromium/net/cookies/cookie_constants.cc +++ b/chromium/net/cookies/cookie_constants.cc @@ -4,6 +4,7 @@ #include "net/cookies/cookie_constants.h" +#include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_macros.h" #include "base/notreached.h" #include "base/strings/string_util.h" @@ -97,8 +98,200 @@ CookieSameSite StringToCookieSameSite(const std::string& same_site, return samesite; } -void RecordCookieSameSiteAttributeValueHistogram(CookieSameSiteString value) { +void RecordCookieSameSiteAttributeValueHistogram(CookieSameSiteString value, + bool is_cookie_same_party) { UMA_HISTOGRAM_ENUMERATION("Cookie.SameSiteAttributeValue", value); + if (is_cookie_same_party) { + base::UmaHistogramEnumeration( + "Cookie.SamePartyCookieSameSiteAttributeValue", value); + } +} + +CookiePort ReducePortRangeForCookieHistogram(const int port) { + switch (port) { + case 80: + return CookiePort::k80; + case 81: + return CookiePort::k81; + case 82: + return CookiePort::k82; + case 83: + return CookiePort::k83; + case 84: + return CookiePort::k84; + case 85: + return CookiePort::k85; + case 443: + return CookiePort::k443; + case 444: + return CookiePort::k444; + case 445: + return CookiePort::k445; + case 446: + return CookiePort::k446; + case 447: + return CookiePort::k447; + case 448: + return CookiePort::k448; + case 3000: + return CookiePort::k3000; + case 3001: + return CookiePort::k3001; + case 3002: + return CookiePort::k3002; + case 3003: + return CookiePort::k3003; + case 3004: + return CookiePort::k3004; + case 3005: + return CookiePort::k3005; + case 4200: + return CookiePort::k4200; + case 4201: + return CookiePort::k4201; + case 4202: + return CookiePort::k4202; + case 4203: + return CookiePort::k4203; + case 4204: + return CookiePort::k4204; + case 4205: + return CookiePort::k4205; + case 5000: + return CookiePort::k5000; + case 5001: + return CookiePort::k5001; + case 5002: + return CookiePort::k5002; + case 5003: + return CookiePort::k5003; + case 5004: + return CookiePort::k5004; + case 5005: + return CookiePort::k5005; + case 7000: + return CookiePort::k7000; + case 7001: + return CookiePort::k7001; + case 7002: + return CookiePort::k7002; + case 7003: + return CookiePort::k7003; + case 7004: + return CookiePort::k7004; + case 7005: + return CookiePort::k7005; + case 8000: + return CookiePort::k8000; + case 8001: + return CookiePort::k8001; + case 8002: + return CookiePort::k8002; + case 8003: + return CookiePort::k8003; + case 8004: + return CookiePort::k8004; + case 8005: + return CookiePort::k8005; + case 8080: + return CookiePort::k8080; + case 8081: + return CookiePort::k8081; + case 8082: + return CookiePort::k8082; + case 8083: + return CookiePort::k8083; + case 8084: + return CookiePort::k8084; + case 8085: + return CookiePort::k8085; + case 8090: + return CookiePort::k8090; + case 8091: + return CookiePort::k8091; + case 8092: + return CookiePort::k8092; + case 8093: + return CookiePort::k8093; + case 8094: + return CookiePort::k8094; + case 8095: + return CookiePort::k8095; + case 8100: + return CookiePort::k8100; + case 8101: + return CookiePort::k8101; + case 8102: + return CookiePort::k8102; + case 8103: + return CookiePort::k8103; + case 8104: + return CookiePort::k8104; + case 8105: + return CookiePort::k8105; + case 8200: + return CookiePort::k8200; + case 8201: + return CookiePort::k8201; + case 8202: + return CookiePort::k8202; + case 8203: + return CookiePort::k8203; + case 8204: + return CookiePort::k8204; + case 8205: + return CookiePort::k8205; + case 8443: + return CookiePort::k8443; + case 8444: + return CookiePort::k8444; + case 8445: + return CookiePort::k8445; + case 8446: + return CookiePort::k8446; + case 8447: + return CookiePort::k8447; + case 8448: + return CookiePort::k8448; + case 8888: + return CookiePort::k8888; + case 8889: + return CookiePort::k8889; + case 8890: + return CookiePort::k8890; + case 8891: + return CookiePort::k8891; + case 8892: + return CookiePort::k8892; + case 8893: + return CookiePort::k8893; + case 9000: + return CookiePort::k9000; + case 9001: + return CookiePort::k9001; + case 9002: + return CookiePort::k9002; + case 9003: + return CookiePort::k9003; + case 9004: + return CookiePort::k9004; + case 9005: + return CookiePort::k9005; + case 9090: + return CookiePort::k9090; + case 9091: + return CookiePort::k9091; + case 9092: + return CookiePort::k9092; + case 9093: + return CookiePort::k9093; + case 9094: + return CookiePort::k9094; + case 9095: + return CookiePort::k9095; + default: + return CookiePort::kOther; + } } } // namespace net diff --git a/chromium/net/cookies/cookie_constants.h b/chromium/net/cookies/cookie_constants.h index 8397a471a8b..8fb0df7e5ff 100644 --- a/chromium/net/cookies/cookie_constants.h +++ b/chromium/net/cookies/cookie_constants.h @@ -95,6 +95,125 @@ enum class CookieSourceScheme { kMaxValue = kSecure // Keep as the last value. }; +enum class CookiePort { + // DO NOT REORDER OR RENUMBER. These are used for histograms. + + // Potentially interesting port values for cookies for use with histograms. + + // Not a port explicitly listed below, including invalid ports (-1, 65536, + // etc). + kOther = 0, + // HTTP + k80 = 1, + k81 = 2, + k82 = 3, + k83 = 4, + k84 = 5, + k85 = 6, + // HTTPS + k443 = 7, + k444 = 8, + k445 = 9, + k446 = 10, + k447 = 11, + k448 = 12, + // JS Framework + k3000 = 13, + k3001 = 14, + k3002 = 15, + k3003 = 16, + k3004 = 17, + k3005 = 18, + // JS Framework + k4200 = 19, + k4201 = 20, + k4202 = 21, + k4203 = 22, + k4204 = 23, + k4205 = 24, + // JS Framework + k5000 = 25, + k5001 = 26, + k5002 = 27, + k5003 = 28, + k5004 = 29, + k5005 = 30, + // Common Dev Ports + k7000 = 31, + k7001 = 32, + k7002 = 33, + k7003 = 34, + k7004 = 35, + k7005 = 36, + // HTTP + k8000 = 37, + k8001 = 38, + k8002 = 39, + k8003 = 40, + k8004 = 41, + k8005 = 42, + // HTTP + k8080 = 43, + k8081 = 44, + k8082 = 45, + k8083 = 46, + k8084 = 47, + k8085 = 48, + // HTTP + k8090 = 49, + k8091 = 50, + k8092 = 51, + k8093 = 52, + k8094 = 53, + k8095 = 54, + // JS Framework + k8100 = 55, + k8101 = 56, + k8102 = 57, + k8103 = 58, + k8104 = 59, + k8105 = 60, + // JS Framework + k8200 = 61, + k8201 = 62, + k8202 = 63, + k8203 = 64, + k8204 = 65, + k8205 = 66, + // HTTP(S) + k8443 = 67, + k8444 = 68, + k8445 = 69, + k8446 = 70, + k8447 = 71, + k8448 = 72, + // HTTP + k8888 = 73, + k8889 = 74, + k8890 = 75, + k8891 = 76, + k8892 = 77, + k8893 = 78, + // Common Dev Ports + k9000 = 79, + k9001 = 80, + k9002 = 81, + k9003 = 82, + k9004 = 83, + k9005 = 84, + // HTTP + k9090 = 85, + k9091 = 86, + k9092 = 87, + k9093 = 88, + k9094 = 89, + k9095 = 90, + + // Keep as last value. + kMaxValue = k9095 + +}; + // Returns the Set-Cookie header priority token corresponding to |priority|. NET_EXPORT std::string CookiePriorityToString(CookiePriority priority); @@ -116,7 +235,13 @@ StringToCookieSameSite(const std::string& same_site, CookieSameSiteString* samesite_string = nullptr); NET_EXPORT void RecordCookieSameSiteAttributeValueHistogram( - CookieSameSiteString value); + CookieSameSiteString value, + bool is_cookie_same_party = false); + +// This function reduces the 65535 available TCP port values down to a <100 +// potentially interesting values that cookies could be set by or sent to. This +// is because UMA cannot handle the full range. +NET_EXPORT CookiePort ReducePortRangeForCookieHistogram(const int port); } // namespace net diff --git a/chromium/net/cookies/cookie_constants_unittest.cc b/chromium/net/cookies/cookie_constants_unittest.cc index 8fab6e292c9..a66a3bbbe7b 100644 --- a/chromium/net/cookies/cookie_constants_unittest.cc +++ b/chromium/net/cookies/cookie_constants_unittest.cc @@ -62,4 +62,41 @@ TEST(CookieConstantsTest, TestCookieSameSite) { } } +TEST(CookieConstantsTest, TestReducePortRangeForCookieHistogram) { + struct TestData { + int input_port; + CookiePort expected_enum; + }; + + const TestData kTestValues[] = { + {-1234 /* Invalid port. */, CookiePort::kOther}, + {0 /* Invalid port. */, CookiePort::kOther}, + {1 /* Valid but outside range. */, CookiePort::kOther}, + {79 /* Valid but outside range. */, CookiePort::kOther}, + {80, CookiePort::k80}, + {445, CookiePort::k445}, + {3001, CookiePort::k3001}, + {4200, CookiePort::k4200}, + {5002, CookiePort::k5002}, + {7003, CookiePort::k7003}, + {8001, CookiePort::k8001}, + {8080, CookiePort::k8080}, + {8086 /* Valid but outside range. */, CookiePort::kOther}, + {8095, CookiePort::k8095}, + {8100, CookiePort::k8100}, + {8201, CookiePort::k8201}, + {8445, CookiePort::k8445}, + {8888, CookiePort::k8888}, + {9004, CookiePort::k9004}, + {9091, CookiePort::k9091}, + {65535 /* Valid but outside range. */, CookiePort::kOther}, + {655356 /* Invalid port. */, CookiePort::kOther}, + }; + + for (const auto& value : kTestValues) { + EXPECT_EQ(value.expected_enum, + ReducePortRangeForCookieHistogram(value.input_port)); + } +} + } // namespace net diff --git a/chromium/net/cookies/cookie_deletion_info_unittest.cc b/chromium/net/cookies/cookie_deletion_info_unittest.cc index 39131b50175..ff629f91223 100644 --- a/chromium/net/cookies/cookie_deletion_info_unittest.cc +++ b/chromium/net/cookies/cookie_deletion_info_unittest.cc @@ -90,7 +90,8 @@ TEST(CookieDeletionInfoTest, CookieDeletionInfoMatchSessionControl) { /*secure=*/true, /*httponly=*/false, CookieSameSite::NO_RESTRICTION, - CookiePriority::COOKIE_PRIORITY_DEFAULT); + CookiePriority::COOKIE_PRIORITY_DEFAULT, + /*sameparty=*/false); CanonicalCookie session_cookie( "session-cookie", "session-value", "session-domain", "session-path", @@ -99,7 +100,8 @@ TEST(CookieDeletionInfoTest, CookieDeletionInfoMatchSessionControl) { /*last_access=*/base::Time::Now(), /*secure=*/true, /*httponly=*/false, CookieSameSite::NO_RESTRICTION, - CookiePriority::COOKIE_PRIORITY_DEFAULT); + CookiePriority::COOKIE_PRIORITY_DEFAULT, + /*sameparty=*/false); CookieDeletionInfo delete_info; EXPECT_TRUE(delete_info.Matches(persistent_cookie)); @@ -125,7 +127,8 @@ TEST(CookieDeletionInfoTest, CookieDeletionInfoMatchHost) { /*secure=*/true, /*httponly=*/false, CookieSameSite::NO_RESTRICTION, - CookiePriority::COOKIE_PRIORITY_DEFAULT); + CookiePriority::COOKIE_PRIORITY_DEFAULT, + /*sameparty=*/false); CanonicalCookie host_cookie("host-cookie", "host-cookie-value", /*domain=*/"thehost.hosting.com", "/path", @@ -135,7 +138,8 @@ TEST(CookieDeletionInfoTest, CookieDeletionInfoMatchHost) { /*secure=*/true, /*httponly=*/false, CookieSameSite::NO_RESTRICTION, - CookiePriority::COOKIE_PRIORITY_DEFAULT); + CookiePriority::COOKIE_PRIORITY_DEFAULT, + /*sameparty=*/false); EXPECT_TRUE(domain_cookie.IsDomainCookie()); EXPECT_TRUE(host_cookie.IsHostCookie()); @@ -165,7 +169,8 @@ TEST(CookieDeletionInfoTest, CookieDeletionInfoMatchName) { /*last_access=*/base::Time::Now(), /*secure=*/true, /*httponly=*/false, CookieSameSite::NO_RESTRICTION, - CookiePriority::COOKIE_PRIORITY_DEFAULT); + CookiePriority::COOKIE_PRIORITY_DEFAULT, + /*sameparty=*/false); CanonicalCookie cookie2("cookie2-name", "cookie2-value", /*domain=*/".example.com", "/path", /*creation=*/base::Time::Now(), @@ -173,7 +178,8 @@ TEST(CookieDeletionInfoTest, CookieDeletionInfoMatchName) { /*last_access=*/base::Time::Now(), /*secure=*/true, /*httponly=*/false, CookieSameSite::NO_RESTRICTION, - CookiePriority::COOKIE_PRIORITY_DEFAULT); + CookiePriority::COOKIE_PRIORITY_DEFAULT, + /*sameparty=*/false); CookieDeletionInfo delete_info; delete_info.name = "cookie1-name"; @@ -189,7 +195,8 @@ TEST(CookieDeletionInfoTest, CookieDeletionInfoMatchValue) { /*last_access=*/base::Time::Now(), /*secure=*/true, /*httponly=*/false, CookieSameSite::NO_RESTRICTION, - CookiePriority::COOKIE_PRIORITY_DEFAULT); + CookiePriority::COOKIE_PRIORITY_DEFAULT, + /*sameparty=*/false); CanonicalCookie cookie2("cookie2-name", "cookie2-value", /*domain=*/".example.com", "/path", /*creation=*/base::Time::Now(), @@ -197,7 +204,8 @@ TEST(CookieDeletionInfoTest, CookieDeletionInfoMatchValue) { /*last_access=*/base::Time::Now(), /*secure=*/true, /*httponly=*/false, CookieSameSite::NO_RESTRICTION, - CookiePriority::COOKIE_PRIORITY_DEFAULT); + CookiePriority::COOKIE_PRIORITY_DEFAULT, + /*sameparty=*/false); CookieDeletionInfo delete_info; delete_info.value_for_testing = "cookie2-value"; @@ -213,7 +221,8 @@ TEST(CookieDeletionInfoTest, CookieDeletionInfoMatchUrl) { /*last_access=*/base::Time::Now(), /*secure=*/true, /*httponly=*/false, CookieSameSite::NO_RESTRICTION, - CookiePriority::COOKIE_PRIORITY_DEFAULT); + CookiePriority::COOKIE_PRIORITY_DEFAULT, + /*sameparty=*/false); CookieDeletionInfo delete_info; delete_info.url = GURL("https://www.example.com/path"); @@ -242,7 +251,8 @@ TEST(CookieDeletionInfoTest, CookieDeletionInfoDomainMatchesDomain) { /*secure=*/true, /*httponly=*/false, /*same_site=*/CookieSameSite::NO_RESTRICTION, - /*priority=*/CookiePriority::COOKIE_PRIORITY_DEFAULT); + /*priority=*/CookiePriority::COOKIE_PRIORITY_DEFAULT, + /*sameparty=*/false); return cookie; }; @@ -278,7 +288,8 @@ TEST(CookieDeletionInfoTest, CookieDeletionInfoMatchesDomainList) { /*secure=*/false, /*httponly=*/false, /*same_site=*/CookieSameSite::NO_RESTRICTION, - /*priority=*/CookiePriority::COOKIE_PRIORITY_DEFAULT); + /*priority=*/CookiePriority::COOKIE_PRIORITY_DEFAULT, + /*sameparty=*/false); return cookie; }; diff --git a/chromium/net/cookies/cookie_inclusion_status.cc b/chromium/net/cookies/cookie_inclusion_status.cc index 240b17cd1b0..468526f3142 100644 --- a/chromium/net/cookies/cookie_inclusion_status.cc +++ b/chromium/net/cookies/cookie_inclusion_status.cc @@ -226,6 +226,8 @@ std::string CookieInclusionStatus::GetDebugString() const { base::StrAppend(&out, {"EXCLUDE_INVALID_DOMAIN, "}); if (HasExclusionReason(EXCLUDE_INVALID_PREFIX)) base::StrAppend(&out, {"EXCLUDE_INVALID_PREFIX, "}); + if (HasExclusionReason(EXCLUDE_INVALID_SAMEPARTY)) + base::StrAppend(&out, {"EXCLUDE_INVALID_SAMEPARTY, "}); // Add warning if (!ShouldWarn()) { diff --git a/chromium/net/cookies/cookie_inclusion_status.h b/chromium/net/cookies/cookie_inclusion_status.h index 9e506b55501..4a875067ee6 100644 --- a/chromium/net/cookies/cookie_inclusion_status.h +++ b/chromium/net/cookies/cookie_inclusion_status.h @@ -27,29 +27,54 @@ class NET_EXPORT CookieInclusionStatus { enum ExclusionReason { EXCLUDE_UNKNOWN_ERROR = 0, + // Statuses applied when accessing a cookie (either sending or setting): + + // Cookie was HttpOnly, but the attempted access was through a non-HTTP API. EXCLUDE_HTTP_ONLY = 1, + // Cookie was Secure, but the URL was not allowed to access Secure cookies. EXCLUDE_SECURE_ONLY = 2, + // The cookie's domain attribute did not match the domain of the URL + // attempting access. EXCLUDE_DOMAIN_MISMATCH = 3, + // The cookie's path attribute did not match the path of the URL attempting + // access. EXCLUDE_NOT_ON_PATH = 4, + // The cookie had SameSite=Strict, and the attempted access did not have an + // appropriate SameSiteCookieContext. EXCLUDE_SAMESITE_STRICT = 5, + // The cookie had SameSite=Lax, and the attempted access did not have an + // appropriate SameSiteCookieContext. EXCLUDE_SAMESITE_LAX = 6, - - // The following two are used for the SameSiteByDefaultCookies experiment, - // where if the SameSite attribute is not specified, it will be treated as - // SameSite=Lax by default. + // The cookie did not specify a SameSite attribute, and therefore was + // treated as if it were SameSite=Lax, and the attempted access did not have + // an appropriate SameSiteCookieContext. EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX = 7, - // This is used if SameSite=None is specified, but the cookie is not - // Secure. + // The cookie specified SameSite=None, but it was not Secure. EXCLUDE_SAMESITE_NONE_INSECURE = 8, + // Caller did not allow access to the cookie. EXCLUDE_USER_PREFERENCES = 9, - // Statuses specific to setting cookies + // Statuses only applied when creating/setting cookies: + + // Cookie was malformed and could not be stored. EXCLUDE_FAILURE_TO_STORE = 10, + // Attempted to set a cookie from a scheme that does not support cookies. EXCLUDE_NONCOOKIEABLE_SCHEME = 11, + // Cookie would have overwritten a Secure cookie, and was not allowed to do + // so. (See "Leave Secure Cookies Alone": + // https://tools.ietf.org/html/draft-west-leave-secure-cookies-alone-05 ) EXCLUDE_OVERWRITE_SECURE = 12, + // Cookie would have overwritten an HttpOnly cookie, and was not allowed to + // do so. EXCLUDE_OVERWRITE_HTTP_ONLY = 13, + // Cookie was set with an invalid Domain attribute. EXCLUDE_INVALID_DOMAIN = 14, + // Cookie was set with an invalid __Host- or __Secure- prefix. EXCLUDE_INVALID_PREFIX = 15, + // Cookie was set with an invalid SameParty attribute in combination with + // other attributes. (SameParty is invalid if Secure is not present, or if + // SameSite=Strict is present.) + EXCLUDE_INVALID_SAMEPARTY = 16, // This should be kept last. NUM_EXCLUSION_REASONS diff --git a/chromium/net/cookies/cookie_monster.cc b/chromium/net/cookies/cookie_monster.cc index 140b61a81dc..77b0d86b38c 100644 --- a/chromium/net/cookies/cookie_monster.cc +++ b/chromium/net/cookies/cookie_monster.cc @@ -63,6 +63,7 @@ #include "base/threading/thread_task_runner_handle.h" #include "base/trace_event/process_memory_dump.h" #include "net/base/registry_controlled_domains/registry_controlled_domain.h" +#include "net/base/url_util.h" #include "net/cookies/canonical_cookie.h" #include "net/cookies/cookie_monster_change_dispatcher.h" #include "net/cookies/cookie_monster_netlog_params.h" @@ -71,6 +72,8 @@ #include "net/log/net_log.h" #include "net/log/net_log_values.h" #include "url/origin.h" +#include "url/third_party/mozilla/url_parse.h" +#include "url/url_canon.h" using base::Time; using base::TimeDelta; @@ -299,16 +302,6 @@ size_t CountCookiesForPossibleDeletion( return cookies_count; } -// Returns whether the CookieOptions has at least as same-site of a context as -// |same_site_requirement|, and the options permit HttpOnly access. -bool IsHttpSameSiteContextAtLeast( - const CookieOptions& options, - CookieOptions::SameSiteCookieContext::ContextType same_site_requirement) { - return !options.exclude_httponly() && - options.same_site_cookie_context().GetContextForCookieInclusion() >= - same_site_requirement; -} - } // namespace CookieMonster::CookieMonster(scoped_refptr<PersistentCookieStore> store, @@ -570,7 +563,7 @@ void CookieMonster::AttachAccessSemanticsListForCookieList( const CookieList& cookie_list) { std::vector<CookieAccessSemantics> access_semantics_list; for (const CanonicalCookie& cookie : cookie_list) { - access_semantics_list.push_back(GetAccessSemanticsForCookieGet(cookie)); + access_semantics_list.push_back(GetAccessSemanticsForCookie(cookie)); } MaybeRunCookieCallback(std::move(callback), cookie_list, access_semantics_list); @@ -629,8 +622,7 @@ void CookieMonster::DeleteAllMatchingInfo(CookieDeletionInfo delete_info, CanonicalCookie* cc = curit->second.get(); ++it; - if (delete_info.Matches(*cc, GetAccessSemanticsForCookie( - *cc, false /* legacy_access_granted */))) { + if (delete_info.Matches(*cc, GetAccessSemanticsForCookie(*cc))) { InternalDeleteCookie(curit, true, /*sync_to_store*/ DELETE_COOKIE_EXPLICIT); ++num_deleted; @@ -971,7 +963,7 @@ void CookieMonster::FilterCookiesWithOptions( // given |url|. HTTP only cookies are filtered depending on the passed // cookie |options|. CookieAccessResult access_result = (*it)->IncludeForRequestURL( - url, options, GetAccessSemanticsForCookieGet(**it)); + url, options, GetAccessSemanticsForCookie(**it)); if (!access_result.status.IsInclude()) { if (options.return_excluded_cookies()) @@ -982,7 +974,32 @@ void CookieMonster::FilterCookiesWithOptions( if (options.update_access_time()) InternalUpdateCookieAccessTime(*it, current_time); - MaybeRecordCookieAccessWithOptions(**it, options, false); + int destination_port = url.EffectiveIntPort(); + + if (IsLocalhost(url)) { + UMA_HISTOGRAM_ENUMERATION( + "Cookie.Port.Read.Localhost", + ReducePortRangeForCookieHistogram(destination_port)); + UMA_HISTOGRAM_ENUMERATION( + "Cookie.Port.ReadDiffersFromSet.Localhost", + IsCookieSentToSamePortThatSetIt(url, (*it)->SourcePort(), + (*it)->SourceScheme())); + } else { + UMA_HISTOGRAM_ENUMERATION( + "Cookie.Port.Read.RemoteHost", + ReducePortRangeForCookieHistogram(destination_port)); + UMA_HISTOGRAM_ENUMERATION( + "Cookie.Port.ReadDiffersFromSet.RemoteHost", + IsCookieSentToSamePortThatSetIt(url, (*it)->SourcePort(), + (*it)->SourceScheme())); + } + + if ((*it)->IsDomainCookie()) { + UMA_HISTOGRAM_ENUMERATION( + "Cookie.Port.ReadDiffersFromSet.DomainSet", + IsCookieSentToSamePortThatSetIt(url, (*it)->SourcePort(), + (*it)->SourceScheme())); + } included_cookies->push_back({**it, access_result}); } @@ -1089,31 +1106,6 @@ void CookieMonster::MaybeDeleteEquivalentCookieAndUpdateStatus( } } -// Find the creation time of an equivalent cookie with the same value -// ("identical", well, modulo other attributes that don't get compared) -// if any. This iterates through the matching range of the |cookies_| map an -// extra time, but this is ok because it is only used if -// RecentCreationTimeGrantsLegacyCookieSemantics is enabled. -base::Time CookieMonster::EffectiveCreationTimeForMaybePreexistingCookie( - const std::string& key, - const CanonicalCookie& cookie) const { - DCHECK(cookie_util::IsRecentCreationTimeGrantsLegacyCookieSemanticsEnabled()); - base::Time effective_creation_time = cookie.CreationDate(); - const auto range_its = cookies_.equal_range(key); - for (auto cur_it = range_its.first; cur_it != range_its.second; ++cur_it) { - CanonicalCookie* preexisting_maybe_identical_cookie = cur_it->second.get(); - if (cookie.IsEquivalent(*preexisting_maybe_identical_cookie)) { - if (preexisting_maybe_identical_cookie->Value() == cookie.Value()) { - effective_creation_time = - preexisting_maybe_identical_cookie->CreationDate(); - } - // There should only ever be at most one equivalent cookie in the store. - break; - } - } - return effective_creation_time; -} - CookieMonster::CookieMap::iterator CookieMonster::InternalInsertCookie( const std::string& key, std::unique_ptr<CanonicalCookie> cc, @@ -1187,14 +1179,8 @@ void CookieMonster::SetCanonicalCookie(std::unique_ptr<CanonicalCookie> cc, const std::string key(GetKey(cc->Domain())); - cc->IsSetPermittedInContext( - options, - GetAccessSemanticsForCookieSet( - *cc, options, - cookie_util::IsRecentCreationTimeGrantsLegacyCookieSemanticsEnabled() - ? EffectiveCreationTimeForMaybePreexistingCookie(key, *cc) - : base::Time()), - &access_result); + cc->IsSetPermittedInContext(options, GetAccessSemanticsForCookie(*cc), + &access_result); base::Time creation_date = cc->CreationDate(); if (creation_date.is_null()) { @@ -1236,8 +1222,13 @@ void CookieMonster::SetCanonicalCookie(std::unique_ptr<CanonicalCookie> cc, if (!already_expired) { // See InitializeHistograms() for details. if (cc->IsPersistent()) { - histogram_expiration_duration_minutes_->Add( - (cc->ExpiryDate() - creation_date).InMinutes()); + if (cc->IsSecure()) { + histogram_expiration_duration_minutes_secure_->Add( + (cc->ExpiryDate() - creation_date).InMinutes()); + } else { + histogram_expiration_duration_minutes_non_secure_->Add( + (cc->ExpiryDate() - creation_date).InMinutes()); + } } // Histogram the type of scheme used on URLs that set cookies. This @@ -1255,12 +1246,12 @@ void CookieMonster::SetCanonicalCookie(std::unique_ptr<CanonicalCookie> cc, : COOKIE_SOURCE_NONSECURE_COOKIE_NONCRYPTOGRAPHIC_SCHEME)); histogram_cookie_source_scheme_->Add(cookie_source_sample); + UMA_HISTOGRAM_BOOLEAN("Cookie.DomainSet", cc->IsDomainCookie()); + if (!creation_date_to_inherit.is_null()) { cc->SetCreationDate(creation_date_to_inherit); } - MaybeRecordCookieAccessWithOptions(*cc, options, true); - InternalInsertCookie(key, std::move(cc), true, access_result); } else { DVLOG(net::cookie_util::kVlogSetCookies) @@ -1273,6 +1264,16 @@ void CookieMonster::SetCanonicalCookie(std::unique_ptr<CanonicalCookie> cc, // that if a cookie was set, in the common case it will be used soon after, // and we will purge the expired cookies in GetCookies(). GarbageCollect(creation_date, key); + + if (IsLocalhost(source_url)) { + UMA_HISTOGRAM_ENUMERATION( + "Cookie.Port.Set.Localhost", + ReducePortRangeForCookieHistogram(source_url.EffectiveIntPort())); + } else { + UMA_HISTOGRAM_ENUMERATION( + "Cookie.Port.Set.RemoteHost", + ReducePortRangeForCookieHistogram(source_url.EffectiveIntPort())); + } } // TODO(chlily): Log metrics. @@ -1297,13 +1298,17 @@ void CookieMonster::SetAllCookies(CookieList list, continue; if (cookie.IsPersistent()) { - histogram_expiration_duration_minutes_->Add( - (cookie.ExpiryDate() - creation_time).InMinutes()); + if (cookie.IsSecure()) { + histogram_expiration_duration_minutes_secure_->Add( + (cookie.ExpiryDate() - creation_time).InMinutes()); + } else { + histogram_expiration_duration_minutes_non_secure_->Add( + (cookie.ExpiryDate() - creation_time).InMinutes()); + } } CookieAccessResult access_result; - access_result.access_semantics = GetAccessSemanticsForCookie( - cookie, false /* legacy_semantics_granted */); + access_result.access_semantics = GetAccessSemanticsForCookie(cookie); InternalInsertCookie(key, std::make_unique<CanonicalCookie>(cookie), true, access_result); GarbageCollect(creation_time, key); @@ -1359,26 +1364,16 @@ void CookieMonster::InternalDeleteCookie(CookieMap::iterator it, }); } - // Skip this if the map is empty, to avoid unnecessarily constructing the - // UniqueCookieKey. - if (!last_http_same_site_accesses_.empty()) { - DCHECK(cookie_util:: - IsRecentHttpSameSiteAccessGrantsLegacyCookieSemanticsEnabled()); - last_http_same_site_accesses_.erase(it->second->UniqueKey()); - } - if ((cc->IsPersistent() || persist_session_cookies_) && store_.get() && sync_to_store) { store_->DeleteCookie(*cc); } change_dispatcher_.DispatchChange( - CookieChangeInfo( - *cc, - CookieAccessResult(CookieEffectiveSameSite::UNDEFINED, - CookieInclusionStatus(), - GetAccessSemanticsForCookie( - *cc, false /* legacy_access_granted */)), - mapping.cause), + CookieChangeInfo(*cc, + CookieAccessResult(CookieEffectiveSameSite::UNDEFINED, + CookieInclusionStatus(), + GetAccessSemanticsForCookie(*cc)), + mapping.cause), mapping.notify); // If this is the last cookie in |cookies_| with this key, decrement the @@ -1728,95 +1723,12 @@ bool CookieMonster::HasCookieableScheme(const GURL& url) { } CookieAccessSemantics CookieMonster::GetAccessSemanticsForCookie( - const CanonicalCookie& cookie, - bool legacy_semantics_granted) const { - if (legacy_semantics_granted) - return CookieAccessSemantics::LEGACY; + const CanonicalCookie& cookie) const { if (cookie_access_delegate()) return cookie_access_delegate()->GetAccessSemantics(cookie); return CookieAccessSemantics::UNKNOWN; } -CookieAccessSemantics CookieMonster::GetAccessSemanticsForCookieGet( - const CanonicalCookie& cookie) const { - bool legacy_semantics_granted = - cookie_util::DoesLastHttpSameSiteAccessGrantLegacySemantics( - LastAccessFromHttpSameSiteContext(cookie)) || - cookie_util::DoesCreationTimeGrantLegacySemantics(cookie.CreationDate()); - return GetAccessSemanticsForCookie(cookie, legacy_semantics_granted); -} - -CookieAccessSemantics CookieMonster::GetAccessSemanticsForCookieSet( - const CanonicalCookie& cookie, - const CookieOptions& options, - base::Time effective_creation_time) const { - // If the current cookie access is a set, directly treat the cookie as LEGACY - // if the |options| qualify, because there may not be a time entry in - // |last_http_same_site_accesses_| since it may be a new cookie without a - // previous access. It will still only be added to the map as a qualifying - // cookie access if the final inclusion status is include. - bool legacy_semantics_granted = - (cookie_util:: - IsRecentHttpSameSiteAccessGrantsLegacyCookieSemanticsEnabled() && - IsHttpSameSiteContextAtLeast( - options, - CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX)); - - // If the current cookie access is not itself http-and-same-site, but the last - // one that was, was recent enough, (and the corresponding feature is enabled) - // grant legacy semantics. - legacy_semantics_granted = - legacy_semantics_granted || - cookie_util::DoesLastHttpSameSiteAccessGrantLegacySemantics( - LastAccessFromHttpSameSiteContext(cookie)); - - // If the cookie's creation time (or that of an identical preexisting cookie) - // was recent enough (and the corresponding feature is enabled), grant legacy - // semantics. - legacy_semantics_granted = legacy_semantics_granted || - cookie_util::DoesCreationTimeGrantLegacySemantics( - effective_creation_time); - - return GetAccessSemanticsForCookie(cookie, legacy_semantics_granted); -} - -base::TimeTicks CookieMonster::LastAccessFromHttpSameSiteContext( - const CanonicalCookie& cookie) const { - // Return early to avoid unnecessarily constructing the UniqueCookieKey - if (last_http_same_site_accesses_.empty()) { - return base::TimeTicks(); - } - - const auto it = last_http_same_site_accesses_.find(cookie.UniqueKey()); - if (it != last_http_same_site_accesses_.end()) - return it->second; - return base::TimeTicks(); -} - -void CookieMonster::MaybeRecordCookieAccessWithOptions( - const CanonicalCookie& cookie, - const CookieOptions& options, - bool is_set) { - // Don't populate |last_http_same_site_accesses_| if the relevant feature is - // not enabled. - if (!cookie_util:: - IsRecentHttpSameSiteAccessGrantsLegacyCookieSemanticsEnabled()) { - return; - } - - // Don't update time for accesses that don't update access time. (E.g. the - // time should not be updated when the cookie is accessed to populate the UI.) - if (!options.update_access_time()) - return; - CookieOptions::SameSiteCookieContext::ContextType same_site_requirement; - same_site_requirement = - is_set - ? CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX - : CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_STRICT; - if (IsHttpSameSiteContextAtLeast(options, same_site_requirement)) - last_http_same_site_accesses_[cookie.UniqueKey()] = base::TimeTicks::Now(); -} - // Test to see if stats should be recorded, and record them if so. // The goal here is to get sampling for the average browser-hour of // activity. We won't take samples when the web isn't being surfed, @@ -1885,9 +1797,13 @@ void CookieMonster::InitializeHistograms() { DCHECK(thread_checker_.CalledOnValidThread()); // From UMA_HISTOGRAM_CUSTOM_COUNTS - histogram_expiration_duration_minutes_ = base::Histogram::FactoryGet( - "Cookie.ExpirationDurationMinutes", 1, kMinutesInTenYears, 50, + histogram_expiration_duration_minutes_secure_ = base::Histogram::FactoryGet( + "Cookie.ExpirationDurationMinutesSecure", 1, kMinutesInTenYears, 50, base::Histogram::kUmaTargetedHistogramFlag); + histogram_expiration_duration_minutes_non_secure_ = + base::Histogram::FactoryGet("Cookie.ExpirationDurationMinutesNonSecure", + 1, kMinutesInTenYears, 50, + base::Histogram::kUmaTargetedHistogramFlag); histogram_count_ = base::Histogram::FactoryGet( "Cookie.Count", 1, 4000, 50, base::Histogram::kUmaTargetedHistogramFlag); @@ -1965,4 +1881,44 @@ void CookieMonster::DoCookieCallbackForHostOrDomain( std::move(callback).Run(); } +CookieMonster::CookieSentToSamePort +CookieMonster::IsCookieSentToSamePortThatSetIt( + const GURL& destination, + int source_port, + CookieSourceScheme source_scheme) { + if (source_port == url::PORT_UNSPECIFIED) + return CookieSentToSamePort::kSourcePortUnspecified; + + if (source_port == url::PORT_INVALID) + return CookieSentToSamePort::kInvalid; + + int destination_port = destination.EffectiveIntPort(); + if (source_port == destination_port) + return CookieSentToSamePort::kYes; + + const std::string& destination_scheme = destination.scheme(); + bool destination_port_is_default = + url::DefaultPortForScheme(destination_scheme.c_str(), + destination_scheme.length()) == + destination_port; + + // Since the source port has to be specified if we got to this point, that + // means this is a newer cookie that therefore has its scheme set as well. + DCHECK(source_scheme != CookieSourceScheme::kUnset); + std::string source_scheme_string = + source_scheme == CookieSourceScheme::kSecure + ? url::kHttpsScheme + : url::kHttpScheme; // wss/ws have the same default port values as + // https/http, so it's ok that we use these. + + bool source_port_is_default = + url::DefaultPortForScheme(source_scheme_string.c_str(), + source_scheme_string.length()) == source_port; + + if (destination_port_is_default && source_port_is_default) + return CookieSentToSamePort::kNoButDefault; + + return CookieSentToSamePort::kNo; +} + } // namespace net diff --git a/chromium/net/cookies/cookie_monster.h b/chromium/net/cookies/cookie_monster.h index 57ce9d640ac..1c798ba1c78 100644 --- a/chromium/net/cookies/cookie_monster.h +++ b/chromium/net/cookies/cookie_monster.h @@ -237,6 +237,11 @@ class NET_EXPORT CookieMonster : public CookieStore { FRIEND_TEST_ALL_PREFIXES(CookieMonsterTest, CookieDeleteEquivalentHistogramTest); + // For CookieSentToSamePort enum. + FRIEND_TEST_ALL_PREFIXES(CookieMonsterTest, + CookiePortReadDiffersFromSetHistogram); + FRIEND_TEST_ALL_PREFIXES(CookieMonsterTest, IsCookieSentToSamePortThatSetIt); + // Internal reasons for deletion, used to populate informative histograms // and to provide a public cause for onCookieChange notifications. // @@ -310,6 +315,28 @@ class NET_EXPORT CookieMonster : public CookieStore { COOKIE_SOURCE_LAST_ENTRY }; + // Enum for collecting metrics on how frequently a cookie is sent to the same + // port it was set by. + // + // kNoButDefault exists because we expect for cookies being sent between + // schemes to have a port mismatch and want to separate those out from other, + // more interesting, cases. + // + // Do not reorder or renumber. Used for metrics. + enum class CookieSentToSamePort { + kSourcePortUnspecified = 0, // Cookie's source port is unspecified, we + // can't know if this is the same port or not. + kInvalid = 1, // The source port was corrupted to be PORT_INVALID, we + // can't know if this is the same port or not. + kNo = 2, // Source port and destination port are different. + kNoButDefault = + 3, // Source and destination ports are different but they're + // the defaults for their scheme. This can mean that an http + // cookie was sent to a https origin or vice-versa. + kYes = 4, // They're the same. + kMaxValue = kYes + }; + // Record statistics every kRecordStatisticsIntervalSeconds of uptime. static const int kRecordStatisticsIntervalSeconds = 10 * 60; @@ -438,14 +465,6 @@ class NET_EXPORT CookieMonster : public CookieStore { base::Time* creation_date_to_inherit, CookieInclusionStatus* status); - // This is only used if the RecentCreationTimeGrantsLegacyCookieSemantics - // feature is enabled. It finds an equivalent cookie (based on name, domain, - // path) with the same value, if there is any, and returns its creation time, - // or the creation time of the |cookie| itself, if there is none. - base::Time EffectiveCreationTimeForMaybePreexistingCookie( - const std::string& key, - const CanonicalCookie& cookie) const; - // Inserts |cc| into cookies_. Returns an iterator that points to the inserted // cookie in cookies_. Guarantee: all iterators to cookies_ remain valid. CookieMap::iterator InternalInsertCookie( @@ -522,50 +541,12 @@ class NET_EXPORT CookieMonster : public CookieStore { bool HasCookieableScheme(const GURL& url); - // Get the cookie's access semantics (LEGACY or NONLEGACY), considering any - // features granting legacy semantics for special conditions (if any are - // active and meet the conditions for granting legacy access, pass true for - // |legacy_semantics_granted|). If none are active, this then checks for a + // Get the cookie's access semantics (LEGACY or NONLEGACY), by checking for a // value from the cookie access delegate, if it is non-null. Otherwise returns // UNKNOWN. CookieAccessSemantics GetAccessSemanticsForCookie( - const CanonicalCookie& cookie, - bool legacy_semantics_granted) const; - - // This is called for getting a cookie. - CookieAccessSemantics GetAccessSemanticsForCookieGet( - const CanonicalCookie& cookie) const; - - // This is called for setting a cookie with the options specified by - // |options|. For setting a cookie, a same-site access is lax or better (since - // CookieOptions for setting a cookie will never be strict). - // |effective_creation_time| is the time that should be used for deciding - // whether the RecentCreationTimeGrantsLegacyCookieSemantics feature should - // grant legacy semantics. This may differ from the CreationDate() field of - // the cookie, if there was a preexisting equivalent cookie (in which case it - // is the creation time of that equivalent cookie). - CookieAccessSemantics GetAccessSemanticsForCookieSet( - const CanonicalCookie& cookie, - const CookieOptions& options, - base::Time effective_creation_time) const; - - // Looks up the last time a cookie matching the (name, domain, path) of - // |cookie| was accessed in a same-site context permitting HttpOnly - // cookie access. If there was none, this returns a null base::Time. - // Returns null value if RecentHttpSameSiteAccessGrantsLegacyCookieSemantics - // is not enabled. - base::TimeTicks LastAccessFromHttpSameSiteContext( const CanonicalCookie& cookie) const; - // Updates |last_http_same_site_accesses_| with the current time if the - // |options| are appropriate (same-site and permits HttpOnly access). - // |is_set| is true if the access is setting the cookie, false otherwise (e.g. - // if getting the cookie). Does nothing if - // RecentHttpSameSiteAccessGrantsLegacyCookieSemantics is not enabled. - void MaybeRecordCookieAccessWithOptions(const CanonicalCookie& cookie, - const CookieOptions& options, - bool is_set); - // Statistics support // This function should be called repeatedly, and will record @@ -594,9 +575,20 @@ class NET_EXPORT CookieMonster : public CookieStore { void DoCookieCallbackForHostOrDomain(base::OnceClosure callback, base::StringPiece host_or_domain); + // Checks to see if a cookie is being sent to the same port it was set by. For + // metrics. + // + // This is in CookieMonster because only CookieMonster uses it. It's otherwise + // a standalone utility function. + static CookieSentToSamePort IsCookieSentToSamePortThatSetIt( + const GURL& destination, + int source_port, + CookieSourceScheme source_scheme); + // Histogram variables; see CookieMonster::InitializeHistograms() in // cookie_monster.cc for details. - base::HistogramBase* histogram_expiration_duration_minutes_; + base::HistogramBase* histogram_expiration_duration_minutes_secure_; + base::HistogramBase* histogram_expiration_duration_minutes_non_secure_; base::HistogramBase* histogram_count_; base::HistogramBase* histogram_cookie_type_; base::HistogramBase* histogram_cookie_source_scheme_; @@ -662,17 +654,6 @@ class NET_EXPORT CookieMonster : public CookieStore { // wanted. Thus this value is not initialized. base::Time earliest_access_time_; - // Records the last access to a cookie (either getting or setting) from a - // context that is both same-site and permits HttpOnly access. - // The access is considered same-site if it is at least laxly same-site for - // set, or strictly same-site for get. - // This information is used to determine if the feature - // kRecentSameSiteAccessGrantsLegacyCookieSemantics should grant legacy - // access semantics to a cookie for subsequent accesses. - // This map is not used if that feature is not enabled. - std::map<CanonicalCookie::UniqueCookieKey, base::TimeTicks> - last_http_same_site_accesses_; - std::vector<std::string> cookieable_schemes_; base::Time last_statistic_record_time_; diff --git a/chromium/net/cookies/cookie_monster_netlog_params.cc b/chromium/net/cookies/cookie_monster_netlog_params.cc index 89b38d4170d..fad5f472ee6 100644 --- a/chromium/net/cookies/cookie_monster_netlog_params.cc +++ b/chromium/net/cookies/cookie_monster_netlog_params.cc @@ -32,6 +32,7 @@ base::Value NetLogCookieMonsterCookieAdded(const CanonicalCookie* cookie, dict.SetStringKey("same_site", CookieSameSiteToString(cookie->SameSite())); dict.SetBoolKey("is_persistent", cookie->IsPersistent()); dict.SetBoolKey("sync_requested", sync_requested); + dict.SetBoolKey("same_party", cookie->IsSameParty()); return dict; } diff --git a/chromium/net/cookies/cookie_monster_store_test.cc b/chromium/net/cookies/cookie_monster_store_test.cc index b50d1b074f1..f37b1a2cd8f 100644 --- a/chromium/net/cookies/cookie_monster_store_test.cc +++ b/chromium/net/cookies/cookie_monster_store_test.cc @@ -122,7 +122,7 @@ std::unique_ptr<CanonicalCookie> BuildCanonicalCookie( return std::make_unique<CanonicalCookie>( pc.Name(), pc.Value(), "." + url.host(), cookie_path, creation_time, cookie_expires, base::Time(), pc.IsSecure(), pc.IsHttpOnly(), - pc.SameSite(), pc.Priority()); + pc.SameSite(), pc.Priority(), pc.IsSameParty()); } void AddCookieToList(const GURL& url, @@ -236,7 +236,7 @@ std::unique_ptr<CookieMonster> CreateMonsterFromStoreForGC( std::unique_ptr<CanonicalCookie> cc(std::make_unique<CanonicalCookie>( "a", "1", base::StringPrintf("h%05d.izzle", i), "/path", creation_time, expiration_time, base::Time(), secure, false, - CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT)); + CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT, false)); cc->SetLastAccessDate(last_access_time); store->AddCookie(*cc); } diff --git a/chromium/net/cookies/cookie_monster_unittest.cc b/chromium/net/cookies/cookie_monster_unittest.cc index a4b72d59e64..01ee039244e 100644 --- a/chromium/net/cookies/cookie_monster_unittest.cc +++ b/chromium/net/cookies/cookie_monster_unittest.cc @@ -11,7 +11,7 @@ #include <vector> #include "base/bind.h" -#include "base/bind_helpers.h" +#include "base/callback_helpers.h" #include "base/containers/queue.h" #include "base/location.h" #include "base/memory/ref_counted.h" @@ -26,7 +26,7 @@ #include "base/strings/string_split.h" #include "base/strings/string_tokenizer.h" #include "base/strings/stringprintf.h" -#include "base/test/bind_test_util.h" +#include "base/test/bind.h" #include "base/test/metrics/histogram_tester.h" #include "base/test/mock_callback.h" #include "base/test/scoped_feature_list.h" @@ -51,6 +51,7 @@ #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "url/gurl.h" +#include "url/third_party/mozilla/url_parse.h" #include "url/url_constants.h" namespace net { @@ -59,11 +60,6 @@ using base::Time; using base::TimeDelta; using CookieDeletionInfo = net::CookieDeletionInfo; using features::kCookiesWithoutSameSiteMustBeSecure; -using features::kRecentCreationTimeGrantsLegacyCookieSemantics; -using features::kRecentCreationTimeGrantsLegacyCookieSemanticsMilliseconds; -using features::kRecentHttpSameSiteAccessGrantsLegacyCookieSemantics; -using features:: - kRecentHttpSameSiteAccessGrantsLegacyCookieSemanticsMilliseconds; using features::kSameSiteByDefaultCookies; namespace { @@ -214,71 +210,71 @@ class CookieMonsterTestBase : public CookieStoreTest<T> { cookies.push_back(std::make_unique<CanonicalCookie>( "dom_1", "A", ".harvard.edu", "/", base::Time(), base::Time(), base::Time(), false, false, CookieSameSite::LAX_MODE, - COOKIE_PRIORITY_DEFAULT)); + COOKIE_PRIORITY_DEFAULT, false)); cookies.push_back(std::make_unique<CanonicalCookie>( "dom_2", "B", ".math.harvard.edu", "/", base::Time(), base::Time(), base::Time(), false, false, CookieSameSite::LAX_MODE, - COOKIE_PRIORITY_DEFAULT)); + COOKIE_PRIORITY_DEFAULT, false)); cookies.push_back(std::make_unique<CanonicalCookie>( "dom_3", "C", ".bourbaki.math.harvard.edu", "/", base::Time(), base::Time(), base::Time(), false, false, CookieSameSite::LAX_MODE, - COOKIE_PRIORITY_DEFAULT)); + COOKIE_PRIORITY_DEFAULT, false)); // Host cookies cookies.push_back(std::make_unique<CanonicalCookie>( "host_1", "A", url_top_level_domain_plus_1, "/", base::Time(), base::Time(), base::Time(), false, false, CookieSameSite::LAX_MODE, - COOKIE_PRIORITY_DEFAULT)); + COOKIE_PRIORITY_DEFAULT, false)); cookies.push_back(std::make_unique<CanonicalCookie>( "host_2", "B", url_top_level_domain_plus_2, "/", base::Time(), base::Time(), base::Time(), false, false, CookieSameSite::LAX_MODE, - COOKIE_PRIORITY_DEFAULT)); + COOKIE_PRIORITY_DEFAULT, false)); cookies.push_back(std::make_unique<CanonicalCookie>( "host_3", "C", url_top_level_domain_plus_3, "/", base::Time(), base::Time(), base::Time(), false, false, CookieSameSite::LAX_MODE, - COOKIE_PRIORITY_DEFAULT)); + COOKIE_PRIORITY_DEFAULT, false)); // http_only cookie cookies.push_back(std::make_unique<CanonicalCookie>( "httpo_check", "A", url_top_level_domain_plus_2, "/", base::Time(), base::Time(), base::Time(), false, true, CookieSameSite::LAX_MODE, - COOKIE_PRIORITY_DEFAULT)); + COOKIE_PRIORITY_DEFAULT, false)); // same-site cookie cookies.push_back(std::make_unique<CanonicalCookie>( "firstp_check", "A", url_top_level_domain_plus_2, "/", base::Time(), base::Time(), base::Time(), false, false, CookieSameSite::STRICT_MODE, - COOKIE_PRIORITY_DEFAULT)); + COOKIE_PRIORITY_DEFAULT, false)); // Secure cookies cookies.push_back(std::make_unique<CanonicalCookie>( "sec_dom", "A", ".math.harvard.edu", "/", base::Time(), base::Time(), base::Time(), true, false, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_DEFAULT)); + COOKIE_PRIORITY_DEFAULT, false)); cookies.push_back(std::make_unique<CanonicalCookie>( "sec_host", "B", url_top_level_domain_plus_2, "/", base::Time(), base::Time(), base::Time(), true, false, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_DEFAULT)); + COOKIE_PRIORITY_DEFAULT, false)); // Domain path cookies cookies.push_back(std::make_unique<CanonicalCookie>( "dom_path_1", "A", ".math.harvard.edu", "/dir1", base::Time(), base::Time(), base::Time(), false, false, CookieSameSite::LAX_MODE, - COOKIE_PRIORITY_DEFAULT)); + COOKIE_PRIORITY_DEFAULT, false)); cookies.push_back(std::make_unique<CanonicalCookie>( "dom_path_2", "B", ".math.harvard.edu", "/dir1/dir2", base::Time(), base::Time(), base::Time(), false, false, CookieSameSite::LAX_MODE, - COOKIE_PRIORITY_DEFAULT)); + COOKIE_PRIORITY_DEFAULT, false)); // Host path cookies cookies.push_back(std::make_unique<CanonicalCookie>( "host_path_1", "A", url_top_level_domain_plus_2, "/dir1", base::Time(), base::Time(), base::Time(), false, false, CookieSameSite::LAX_MODE, - COOKIE_PRIORITY_DEFAULT)); + COOKIE_PRIORITY_DEFAULT, false)); cookies.push_back(std::make_unique<CanonicalCookie>( "host_path_2", "B", url_top_level_domain_plus_2, "/dir1/dir2", base::Time(), base::Time(), base::Time(), false, false, - CookieSameSite::LAX_MODE, COOKIE_PRIORITY_DEFAULT)); + CookieSameSite::LAX_MODE, COOKIE_PRIORITY_DEFAULT, false)); for (auto& cookie : cookies) { GURL source_url = cookie_util::SimulatedCookieSource( @@ -838,7 +834,7 @@ class CookieMonsterTestBase : public CookieStoreTest<T> { creation_time, base::Time() /* expiration_time */, creation_time /* last_access */, true /* secure */, false /* http_only */, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_DEFAULT)); + COOKIE_PRIORITY_DEFAULT, false /* same_party */)); GURL source_url = cookie_util::SimulatedCookieSource(*cc, "https"); cm->SetCanonicalCookieAsync(std::move(cc), source_url, CookieOptions::MakeAllInclusive(), @@ -879,6 +875,7 @@ struct CookiesInputInfo { bool http_only; CookieSameSite same_site; CookiePriority priority; + bool same_party; }; } // namespace @@ -1014,11 +1011,11 @@ TEST_F(DeferredCookieTaskTest, DeferredSetAllCookies) { list.push_back(CanonicalCookie("A", "B", "." + http_www_foo_.domain(), "/", base::Time::Now(), base::Time(), base::Time(), false, true, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_DEFAULT)); + COOKIE_PRIORITY_DEFAULT, false)); list.push_back(CanonicalCookie("C", "D", "." + http_www_foo_.domain(), "/", base::Time::Now(), base::Time(), base::Time(), false, true, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_DEFAULT)); + COOKIE_PRIORITY_DEFAULT, false)); ResultSavingCookieCallback<CookieAccessResult> call1; cookie_monster_->SetAllCookiesAsync(list, call1.MakeCallback()); @@ -1996,13 +1993,13 @@ TEST_F(CookieMonsterTest, BackingStoreCommunication) { const CookiesInputInfo input_info[] = { {GURL("https://a.b.foo.com"), "a", "1", "a.b.foo.com", "/path/to/cookie", expires, true /* secure */, false, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_DEFAULT}, + COOKIE_PRIORITY_DEFAULT, false}, {GURL("https://www.foo.com"), "b", "2", ".foo.com", "/path/from/cookie", expires + TimeDelta::FromSeconds(10), true, true, - CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT}, + CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT, true}, {GURL("https://foo.com"), "c", "3", "foo.com", "/another/path/to/cookie", base::Time::Now() + base::TimeDelta::FromSeconds(100), false, false, - CookieSameSite::STRICT_MODE, COOKIE_PRIORITY_DEFAULT}}; + CookieSameSite::STRICT_MODE, COOKIE_PRIORITY_DEFAULT, false}}; const int INPUT_DELETE = 1; // Create new cookies and flush them to the store. @@ -2015,7 +2012,8 @@ TEST_F(CookieMonsterTest, BackingStoreCommunication) { std::make_unique<CanonicalCookie>( cookie.name, cookie.value, cookie.domain, cookie.path, base::Time(), cookie.expiration_time, base::Time(), cookie.secure, - cookie.http_only, cookie.same_site, cookie.priority), + cookie.http_only, cookie.same_site, cookie.priority, + cookie.same_party), cookie.url, true /*modify_httponly*/)); } @@ -2468,15 +2466,15 @@ TEST_F(CookieMonsterTest, SetAllCookies) { list.push_back(CanonicalCookie( "A", "B", "." + http_www_foo_.url().host(), "/", base::Time::Now(), base::Time(), base::Time(), false, false, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_DEFAULT)); + COOKIE_PRIORITY_DEFAULT, false)); list.push_back(CanonicalCookie( "W", "X", "." + http_www_foo_.url().host(), "/bar", base::Time::Now(), base::Time(), base::Time(), false, false, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_DEFAULT)); + COOKIE_PRIORITY_DEFAULT, false)); list.push_back(CanonicalCookie( "Y", "Z", "." + http_www_foo_.url().host(), "/", base::Time::Now(), base::Time(), base::Time(), false, false, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_DEFAULT)); + COOKIE_PRIORITY_DEFAULT, false)); // SetAllCookies must not flush. ASSERT_EQ(0, store->flush_count()); @@ -2551,7 +2549,7 @@ TEST_F(CookieMonsterTest, HistogramCheck) { // since the histogram should have been initialized by the CM construction // above. base::HistogramBase* expired_histogram = base::Histogram::FactoryGet( - "Cookie.ExpirationDurationMinutes", 1, 10 * 365 * 24 * 60, 50, + "Cookie.ExpirationDurationMinutesSecure", 1, 10 * 365 * 24 * 60, 50, base::Histogram::kUmaTargetedHistogramFlag); std::unique_ptr<base::HistogramSamples> samples1( @@ -2559,7 +2557,7 @@ TEST_F(CookieMonsterTest, HistogramCheck) { auto cookie = std::make_unique<CanonicalCookie>( "a", "b", "a.url", "/", base::Time(), base::Time::Now() + base::TimeDelta::FromMinutes(59), base::Time(), true, - false, CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT); + false, CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT, false); GURL source_url = cookie_util::SimulatedCookieSource(*cookie, "https"); ASSERT_TRUE(SetCanonicalCookie(cm.get(), std::move(cookie), source_url, true /*modify_httponly*/)); @@ -2687,7 +2685,7 @@ TEST_F(CookieMonsterTest, ControlCharacterPurge) { "\x05" "boo", "." + domain, path, now2, later, base::Time(), false, false, - CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT); + CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT, false); initial_cookies.push_back(std::move(cc)); AddCookieToList(url, "hello=world; path=" + path, now3, &initial_cookies); @@ -4023,59 +4021,26 @@ class CookieMonsterLegacyCookieAccessTest : public CookieMonsterTest { ~CookieMonsterLegacyCookieAccessTest() override {} - // The third parameter is nullopt if - // kRecentHttpSameSiteAccessGrantsLegacyCookieSemantics is not enabled. - // Otherwise it gives the value of the corresponding parameter. - // Similarly for the fourth parameter, which is for - // kRecentCreationTimeGrantsLegacyCookieSemantics. - void SetFeatures( - bool is_same_site_by_default_cookies_enabled, - bool is_cookies_without_samesite_must_be_secure_enabled, - base::Optional<int> - milliseconds_for_http_same_site_access_grants_legacy_semantics, - base::Optional<int> - milliseconds_for_creation_time_grants_legacy_semantics) { + void SetFeatures(bool is_same_site_by_default_cookies_enabled, + bool is_cookies_without_samesite_must_be_secure_enabled) { feature_list_ = std::make_unique<base::test::ScopedFeatureList>(); - std::vector<base::test::ScopedFeatureList::FeatureAndParams> enabled; + std::vector<base::Feature> enabled; std::vector<base::Feature> disabled; if (is_same_site_by_default_cookies_enabled) { - enabled.push_back({kSameSiteByDefaultCookies, {}}); + enabled.push_back(kSameSiteByDefaultCookies); } else { disabled.push_back(kSameSiteByDefaultCookies); } if (is_cookies_without_samesite_must_be_secure_enabled) { - enabled.push_back({kCookiesWithoutSameSiteMustBeSecure, {}}); + enabled.push_back(kCookiesWithoutSameSiteMustBeSecure); } else { disabled.push_back(kCookiesWithoutSameSiteMustBeSecure); } - if (milliseconds_for_http_same_site_access_grants_legacy_semantics) { - enabled.push_back( - {kRecentHttpSameSiteAccessGrantsLegacyCookieSemantics, - {{kRecentHttpSameSiteAccessGrantsLegacyCookieSemanticsMilliseconds - .name, - base::NumberToString( - milliseconds_for_http_same_site_access_grants_legacy_semantics - .value())}}}); - } else { - disabled.push_back(kRecentHttpSameSiteAccessGrantsLegacyCookieSemantics); - } - - if (milliseconds_for_creation_time_grants_legacy_semantics) { - enabled.push_back( - {kRecentCreationTimeGrantsLegacyCookieSemantics, - {{kRecentCreationTimeGrantsLegacyCookieSemanticsMilliseconds.name, - base::NumberToString( - milliseconds_for_creation_time_grants_legacy_semantics - .value())}}}); - } else { - disabled.push_back(kRecentCreationTimeGrantsLegacyCookieSemantics); - } - - feature_list_->InitWithFeaturesAndParameters(enabled, disabled); + feature_list_->InitWithFeatures(enabled, disabled); } protected: @@ -4091,7 +4056,7 @@ class CookieMonsterLegacyCookieAccessTest : public CookieMonsterTest { }; TEST_F(CookieMonsterLegacyCookieAccessTest, SetLegacyNoSameSiteCookie) { - SetFeatures(true, true, base::nullopt, base::nullopt); + SetFeatures(true, true); // Check that setting unspecified-SameSite cookie from cross-site context // fails if not set to Legacy semantics, but succeeds if set to legacy. EXPECT_FALSE(CreateAndSetCookie(cm_.get(), kHttpUrl, "cookie=chocolate_chip", @@ -4113,13 +4078,13 @@ TEST_F(CookieMonsterLegacyCookieAccessTest, SetLegacyNoSameSiteCookie) { TEST_F(CookieMonsterLegacyCookieAccessTest, GetLegacyNoSameSiteCookie) { // Set an unspecified-SameSite cookie with SameSite features turned off. // Getting the cookie will succeed. - SetFeatures(false, false, base::nullopt, base::nullopt); + SetFeatures(false, false); ASSERT_TRUE(CreateAndSetCookie(cm_.get(), kHttpUrl, "cookie=chocolate_chip", CookieOptions())); EXPECT_EQ("cookie=chocolate_chip", GetCookiesWithOptions(cm_.get(), kHttpUrl, CookieOptions())); // Turn on the features. Now getting the cookie fails. - SetFeatures(true, true, base::nullopt, base::nullopt); + SetFeatures(true, true); access_delegate_->SetExpectationForCookieDomain( kDomain, CookieAccessSemantics::UNKNOWN); EXPECT_EQ("", GetCookiesWithOptions(cm_.get(), kHttpUrl, CookieOptions())); @@ -4135,7 +4100,7 @@ TEST_F(CookieMonsterLegacyCookieAccessTest, GetLegacyNoSameSiteCookie) { TEST_F(CookieMonsterLegacyCookieAccessTest, SetLegacySameSiteNoneInsecureCookie) { - SetFeatures(true, true, base::nullopt, base::nullopt); + SetFeatures(true, true); access_delegate_->SetExpectationForCookieDomain( kDomain, CookieAccessSemantics::UNKNOWN); EXPECT_FALSE(CreateAndSetCookie(cm_.get(), kHttpsUrl, @@ -4160,14 +4125,14 @@ TEST_F(CookieMonsterLegacyCookieAccessTest, GetLegacySameSiteNoneInsecureCookie) { // Set an SameSite=None insecure cookie with SameSite features turned off. // Getting the cookie will succeed. - SetFeatures(false, false, base::nullopt, base::nullopt); + SetFeatures(false, false); ASSERT_TRUE(CreateAndSetCookie(cm_.get(), kHttpUrl, "cookie=oatmeal_raisin; SameSite=None", CookieOptions())); EXPECT_EQ("cookie=oatmeal_raisin", GetCookiesWithOptions(cm_.get(), kHttpUrl, CookieOptions())); // Turn on the features. Now getting the cookie fails. - SetFeatures(true, true, base::nullopt, base::nullopt); + SetFeatures(true, true); access_delegate_->SetExpectationForCookieDomain( kDomain, CookieAccessSemantics::UNKNOWN); EXPECT_EQ("", GetCookiesWithOptions(cm_.get(), kHttpUrl, CookieOptions())); @@ -4183,7 +4148,7 @@ TEST_F(CookieMonsterLegacyCookieAccessTest, TEST_F(CookieMonsterLegacyCookieAccessTest, NonlegacyCookie) { // Nonlegacy cookie will have default as Lax. - SetFeatures(false, false, base::nullopt, base::nullopt); + SetFeatures(false, false); access_delegate_->SetExpectationForCookieDomain( kDomain, CookieAccessSemantics::NONLEGACY); EXPECT_FALSE(CreateAndSetCookie(cm_.get(), kHttpUrl, "cookie=chocolate_chip", @@ -4198,164 +4163,311 @@ TEST_F(CookieMonsterLegacyCookieAccessTest, NonlegacyCookie) { GetCookiesWithOptions(cm_.get(), kHttpUrl, CookieOptions())); } -// Test the RecentHttpSameSiteAccessGrantsLegacyCookieSemantics feature. -TEST_F(CookieMonsterLegacyCookieAccessTest, RecentHttpSameSiteAccess) { - SetFeatures(true, true, 100, base::nullopt); - // This feature overrides the CookieAccessDelegate setting. - access_delegate_->SetExpectationForCookieDomain( - kDomain, CookieAccessSemantics::NONLEGACY); +TEST_F(CookieMonsterTest, IsCookieSentToSamePortThatSetIt) { + // Note: `IsCookieSentToSamePortThatSetIt()` only uses the source_scheme if + // the port is valid, specified, and doesn't match the url's port. So for test + // cases where the above aren't true the value of source_scheme is irreleant. + + // Test unspecified. + ASSERT_EQ(CookieMonster::IsCookieSentToSamePortThatSetIt( + GURL("https://foo.com"), url::PORT_UNSPECIFIED, + CookieSourceScheme::kSecure), + CookieMonster::CookieSentToSamePort::kSourcePortUnspecified); + + // Test invalid. + ASSERT_EQ(CookieMonster::IsCookieSentToSamePortThatSetIt( + GURL("https://foo.com"), url::PORT_INVALID, + CookieSourceScheme::kSecure), + CookieMonster::CookieSentToSamePort::kInvalid); + + // Test same. + ASSERT_EQ(CookieMonster::IsCookieSentToSamePortThatSetIt( + GURL("https://foo.com"), 443, CookieSourceScheme::kSecure), + CookieMonster::CookieSentToSamePort::kYes); + + ASSERT_EQ( + CookieMonster::IsCookieSentToSamePortThatSetIt( + GURL("https://foo.com:1234"), 1234, CookieSourceScheme::kSecure), + CookieMonster::CookieSentToSamePort::kYes); + + // Test different but default. + ASSERT_EQ(CookieMonster::IsCookieSentToSamePortThatSetIt( + GURL("https://foo.com"), 80, CookieSourceScheme::kNonSecure), + CookieMonster::CookieSentToSamePort::kNoButDefault); + + ASSERT_EQ( + CookieMonster::IsCookieSentToSamePortThatSetIt( + GURL("https://foo.com:443"), 80, CookieSourceScheme::kNonSecure), + CookieMonster::CookieSentToSamePort::kNoButDefault); + + ASSERT_EQ(CookieMonster::IsCookieSentToSamePortThatSetIt( + GURL("wss://foo.com"), 80, CookieSourceScheme::kNonSecure), + CookieMonster::CookieSentToSamePort::kNoButDefault); + + ASSERT_EQ(CookieMonster::IsCookieSentToSamePortThatSetIt( + GURL("http://foo.com"), 443, CookieSourceScheme::kSecure), + CookieMonster::CookieSentToSamePort::kNoButDefault); + + ASSERT_EQ(CookieMonster::IsCookieSentToSamePortThatSetIt( + GURL("ws://foo.com"), 443, CookieSourceScheme::kSecure), + CookieMonster::CookieSentToSamePort::kNoButDefault); + + // Test different. + ASSERT_EQ(CookieMonster::IsCookieSentToSamePortThatSetIt( + GURL("http://foo.com:9000"), 85, CookieSourceScheme::kSecure), + CookieMonster::CookieSentToSamePort::kNo); + + ASSERT_EQ(CookieMonster::IsCookieSentToSamePortThatSetIt( + GURL("https://foo.com"), 80, CookieSourceScheme::kSecure), + CookieMonster::CookieSentToSamePort::kNo); + + ASSERT_EQ(CookieMonster::IsCookieSentToSamePortThatSetIt( + GURL("wss://foo.com"), 80, CookieSourceScheme::kSecure), + CookieMonster::CookieSentToSamePort::kNo); + + ASSERT_EQ(CookieMonster::IsCookieSentToSamePortThatSetIt( + GURL("http://foo.com"), 443, CookieSourceScheme::kNonSecure), + CookieMonster::CookieSentToSamePort::kNo); + + ASSERT_EQ(CookieMonster::IsCookieSentToSamePortThatSetIt( + GURL("ws://foo.com"), 443, CookieSourceScheme::kNonSecure), + CookieMonster::CookieSentToSamePort::kNo); + + ASSERT_EQ(CookieMonster::IsCookieSentToSamePortThatSetIt( + GURL("http://foo.com:444"), 443, CookieSourceScheme::kSecure), + CookieMonster::CookieSentToSamePort::kNo); +} - // Set a cookie from a qualifying (HTTP and same-site) context. - CookieOptions http_lax_options; - http_lax_options.set_include_httponly(); - http_lax_options.set_same_site_cookie_context( - CookieOptions::SameSiteCookieContext( - CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX)); - // This one only works because it's treated as Legacy, otherwise it would be - // rejected for being SameSite=None without secure. - EXPECT_TRUE(CreateAndSetCookie(cm_.get(), kHttpUrl, "cookie=1;SameSite=None", - http_lax_options)); - // Subsequently getting the cookie from a cross-site context also works - // because we just accessed it in an eligible context. - EXPECT_EQ("cookie=1", - GetCookiesWithOptions(cm_.get(), kHttpUrl, CookieOptions())); - // This one should work regardless. - EXPECT_TRUE( - CreateAndSetCookie(cm_.get(), kHttpUrl, "cookie=2", http_lax_options)); - // Subsequently getting the cookie from a cross-site context works even though - // it defaults to Lax, because we just accessed it in an eligible context. - EXPECT_EQ("cookie=2", - GetCookiesWithOptions(cm_.get(), kHttpUrl, CookieOptions())); - // After some delay less than the recency threshold, we can still get the - // cookie from a cross-site context because the last eligible access was - // recent enough. - task_environment_->FastForwardBy(TimeDelta::FromMilliseconds(90)); - EXPECT_EQ("cookie=2", - GetCookiesWithOptions(cm_.get(), kHttpUrl, CookieOptions())); - // After a further delay that passes the recency threshold, we can no longer - // get the cookie from a cross-site context. - // Notably, the last access didn't reset the timer because it wasn't a - // same-site access. - task_environment_->FastForwardBy(TimeDelta::FromMilliseconds(20)); - EXPECT_EQ("", GetCookiesWithOptions(cm_.get(), kHttpUrl, CookieOptions())); +TEST_F(CookieMonsterTest, CookieDomainSetHistogram) { + base::HistogramTester histograms; + const char kHistogramName[] = "Cookie.DomainSet"; - // Deleting the cookie clears the last access time. - DeleteAll(cm_.get()); + scoped_refptr<MockPersistentCookieStore> store(new MockPersistentCookieStore); + std::unique_ptr<CookieMonster> cm(new CookieMonster(store.get(), &net_log_)); - // Set a cookie from a same-site but not Http context. This should work - // because it's same-site, but does not activate the feature because it isn't - // http. - CookieOptions exclude_http_lax_options; - exclude_http_lax_options.set_exclude_httponly(); - exclude_http_lax_options.set_same_site_cookie_context( - CookieOptions::SameSiteCookieContext( - CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_LAX)); - EXPECT_TRUE(CreateAndSetCookie(cm_.get(), kHttpUrl, "cookie=1", - exclude_http_lax_options)); - // There is no recent eligible last access time, because we deleted the - // cookie and subsequently re-set it from a non-eligible context. - EXPECT_EQ("", GetCookiesWithOptions(cm_.get(), kHttpUrl, CookieOptions())); - // Accessing it from a laxly same-site context works (because the cookie - // defaults to lax). - EXPECT_EQ("cookie=1", - GetCookiesWithOptions(cm_.get(), kHttpUrl, http_lax_options)); - // However that doesn't count as a recent http same-site access because it was - // only laxly (not strictly) same-site, so getting the cookie from a - // cross-site context does not currently work. - EXPECT_EQ("", GetCookiesWithOptions(cm_.get(), kHttpUrl, CookieOptions())); - // Attempting to set a cookie (unsuccessfully) from an eligible context does - // not count. - CookieOptions http_strict_options; - http_strict_options.set_include_httponly(); - http_strict_options.set_same_site_cookie_context( - CookieOptions::SameSiteCookieContext( - CookieOptions::SameSiteCookieContext::ContextType::SAME_SITE_STRICT)); - EXPECT_FALSE(CreateAndSetCookie(cm_.get(), kHttpUrl, "cookie=2;Secure", - http_strict_options)); - EXPECT_EQ("", GetCookiesWithOptions(cm_.get(), kHttpUrl, CookieOptions())); - // Now get the cookie from an eligible, Http and strictly same-site context. - EXPECT_EQ("cookie=1", - GetCookiesWithOptions(cm_.get(), kHttpUrl, http_strict_options)); - // Subsequently getting the cookie from a cross-site context also works - // because we just accessed it in an eligible context. - EXPECT_EQ("cookie=1", - GetCookiesWithOptions(cm_.get(), kHttpUrl, CookieOptions())); - // After some delay less than the recency threshold, we can still get the - // cookie from a cross-site context because the last eligible access was - // recent enough. - task_environment_->FastForwardBy(TimeDelta::FromMilliseconds(90)); - EXPECT_EQ("cookie=1", - GetCookiesWithOptions(cm_.get(), kHttpUrl, CookieOptions())); - // After a further delay that passes the recency threshold, we can no longer - // get the cookie from a cross-site context. - // Notably, the last access didn't reset the timer because it wasn't a - // same-site access. - task_environment_->FastForwardBy(TimeDelta::FromMilliseconds(20)); - EXPECT_EQ("", GetCookiesWithOptions(cm_.get(), kHttpUrl, CookieOptions())); + histograms.ExpectTotalCount(kHistogramName, 0); + + // Set a host only cookie (non-Domain). + EXPECT_TRUE(SetCookie(cm.get(), https_www_foo_.url(), "A=B")); + histograms.ExpectTotalCount(kHistogramName, 1); + histograms.ExpectBucketCount(kHistogramName, false, 1); + + // Set a domain cookie. + EXPECT_TRUE(SetCookie(cm.get(), https_www_foo_.url(), + "A=B; Domain=" + https_www_foo_.host())); + histograms.ExpectTotalCount(kHistogramName, 2); + histograms.ExpectBucketCount(kHistogramName, true, 1); + + // Invalid cookies don't count toward the histogram. + EXPECT_FALSE( + SetCookie(cm.get(), https_www_foo_.url(), "A=B; Domain=other.com")); + histograms.ExpectTotalCount(kHistogramName, 2); + histograms.ExpectBucketCount(kHistogramName, false, 1); } -// Test the RecentCreationTimeGrantsLegacyCookieSemantics feature. -TEST_F(CookieMonsterLegacyCookieAccessTest, RecentCreationTime) { - SetFeatures(true, true, base::nullopt, 100); - // This feature overrides the CookieAccessDelegate setting. - access_delegate_->SetExpectationForCookieDomain( - kDomain, CookieAccessSemantics::NONLEGACY); +TEST_F(CookieMonsterTest, CookiePortReadHistogram) { + base::HistogramTester histograms; + const char kHistogramName[] = "Cookie.Port.Read.RemoteHost"; + const char kHistogramNameLocal[] = "Cookie.Port.Read.Localhost"; - // While the grace period is active, even if the delegate returns NONLEGACY - // semantics, we are able to set unspecified-SameSite cookies from a - // cross-site context, and we are allowed to set SameSite=None cookies without - // Secure. We are also allowed to get such cookies. - EXPECT_TRUE(CreateAndSetCookie(cm_.get(), kHttpUrl, "cookie1=chocolate_chip", - CookieOptions())); - EXPECT_TRUE(CreateAndSetCookie(cm_.get(), kHttpUrl, - "cookie2=oatmeal_raisin; SameSite=None", - CookieOptions())); - EXPECT_EQ("cookie1=chocolate_chip; cookie2=oatmeal_raisin", - GetCookiesWithOptions(cm_.get(), kHttpUrl, CookieOptions())); + scoped_refptr<MockPersistentCookieStore> store(new MockPersistentCookieStore); + std::unique_ptr<CookieMonster> cm(new CookieMonster(store.get(), &net_log_)); - // After some time passes, but we are still under the time threshold, - // the cookie is still accessible in a cross-site context. - task_environment_->FastForwardBy(TimeDelta::FromMilliseconds(90)); - EXPECT_EQ("cookie1=chocolate_chip; cookie2=oatmeal_raisin", - GetCookiesWithOptions(cm_.get(), kHttpUrl, CookieOptions())); - // After the grace period expires, these cookies are now blocked. - task_environment_->FastForwardBy(TimeDelta::FromMilliseconds(20)); - EXPECT_EQ("", GetCookiesWithOptions(cm_.get(), kHttpUrl, CookieOptions())); + histograms.ExpectTotalCount(kHistogramName, 0); - // Also, now that there is a preexisting cookie in the store that's older than - // the grace period, the same cookie will not be granted legacy semantics - // again because the creation date of the preexisting identical cookie is - // inherited. (This disallows refreshing the grace period by repeatedly - // setting an identical cookie.) - EXPECT_FALSE(CreateAndSetCookie(cm_.get(), kHttpUrl, "cookie1=chocolate_chip", - CookieOptions())); - EXPECT_FALSE(CreateAndSetCookie(cm_.get(), kHttpUrl, - "cookie2=oatmeal_raisin; SameSite=None", - CookieOptions())); - // However, an equivalent (but not identical) cookie can still be set with - // legacy semantics, because now the creation date isn't inherited from the - // preexisting cookie. - // TODO(chlily): It might not actually make sense to allow this... This could - // in effect allow repeatedly refreshing the grace period by setting a cookie - // with a different value and then immediately setting it back to the original - // value. - EXPECT_TRUE(CreateAndSetCookie(cm_.get(), kHttpUrl, "cookie1=snickerdoodle", - CookieOptions())); - EXPECT_TRUE(CreateAndSetCookie(cm_.get(), kHttpUrl, - "cookie2=gingerbread; SameSite=None", - CookieOptions())); + EXPECT_TRUE(SetCookie(cm.get(), GURL("https://www.foo.com"), "A=B")); - // Test the behavior when the time threshold is 0 (the default value). - SetFeatures(true, true, base::nullopt, 0); - // No legacy behavior is used if there is no active, non-zero grace period. - // In particular, if there is a zero grace period, we don't allow setting the - // cookie even if it was created at the very instant it was attempted to be - // set. - EXPECT_FALSE(CreateAndSetCookie(cm_.get(), kHttpUrl, "cookie1=chocolate_chip", - CookieOptions())); - EXPECT_FALSE(CreateAndSetCookie(cm_.get(), kHttpUrl, - "cookie2=oatmeal_raisin; SameSite=None", - CookieOptions())); + // May as well check that it didn't change the histogram... + histograms.ExpectTotalCount(kHistogramName, 0); + + // Now read it from some different ports. This requires some knowledge of how + // `ReducePortRangeForCookieHistogram` maps ports, but that's probably fine. + EXPECT_EQ(GetCookies(cm.get(), GURL("https://www.foo.com")), "A=B"); + // https default is 443, so check that. + histograms.ExpectTotalCount(kHistogramName, 1); + histograms.ExpectBucketCount(kHistogramName, + ReducePortRangeForCookieHistogram(443), 1); + + EXPECT_EQ(GetCookies(cm.get(), GURL("https://www.foo.com:82")), "A=B"); + histograms.ExpectTotalCount(kHistogramName, 2); + histograms.ExpectBucketCount(kHistogramName, + ReducePortRangeForCookieHistogram(82), 1); + + EXPECT_EQ(GetCookies(cm.get(), GURL("https://www.foo.com:8080")), "A=B"); + histograms.ExpectTotalCount(kHistogramName, 3); + histograms.ExpectBucketCount(kHistogramName, + ReducePortRangeForCookieHistogram(8080), 1); + + EXPECT_EQ(GetCookies(cm.get(), GURL("https://www.foo.com:1234")), "A=B"); + histograms.ExpectTotalCount(kHistogramName, 4); + histograms.ExpectBucketCount(kHistogramName, + ReducePortRangeForCookieHistogram(1234), 1); + + // Histogram should not increment if nothing is read. + EXPECT_EQ(GetCookies(cm.get(), GURL("https://www.other.com")), ""); + histograms.ExpectTotalCount(kHistogramName, 4); + + // Make sure the correct histogram is chosen for localhost. + EXPECT_TRUE(SetCookie(cm.get(), GURL("https://localhost"), "local=host")); + + histograms.ExpectTotalCount(kHistogramNameLocal, 0); + + EXPECT_EQ(GetCookies(cm.get(), GURL("https://localhost:82")), "local=host"); + histograms.ExpectTotalCount(kHistogramNameLocal, 1); + histograms.ExpectBucketCount(kHistogramNameLocal, + ReducePortRangeForCookieHistogram(82), 1); +} + +TEST_F(CookieMonsterTest, CookiePortSetHistogram) { + base::HistogramTester histograms; + const char kHistogramName[] = "Cookie.Port.Set.RemoteHost"; + const char kHistogramNameLocal[] = "Cookie.Port.Set.Localhost"; + + scoped_refptr<MockPersistentCookieStore> store(new MockPersistentCookieStore); + std::unique_ptr<CookieMonster> cm(new CookieMonster(store.get(), &net_log_)); + + histograms.ExpectTotalCount(kHistogramName, 0); + + // Set some cookies. This requires some knowledge of how + // ReducePortRangeForCookieHistogram maps ports, but that's probably fine. + + EXPECT_TRUE(SetCookie(cm.get(), GURL("https://www.foo.com"), "A=B")); + histograms.ExpectTotalCount(kHistogramName, 1); + histograms.ExpectBucketCount(kHistogramName, + ReducePortRangeForCookieHistogram(443), 1); + + EXPECT_TRUE(SetCookie(cm.get(), GURL("https://www.foo.com:80"), "A=B")); + histograms.ExpectTotalCount(kHistogramName, 2); + histograms.ExpectBucketCount(kHistogramName, + ReducePortRangeForCookieHistogram(80), 1); + + EXPECT_TRUE(SetCookie(cm.get(), GURL("https://www.foo.com:9000"), "A=B")); + histograms.ExpectTotalCount(kHistogramName, 3); + histograms.ExpectBucketCount(kHistogramName, + ReducePortRangeForCookieHistogram(9000), 1); + + EXPECT_TRUE(SetCookie(cm.get(), GURL("https://www.foo.com:1234"), "A=B")); + histograms.ExpectTotalCount(kHistogramName, 4); + histograms.ExpectBucketCount(kHistogramName, + ReducePortRangeForCookieHistogram(1234), 1); + + // Histogram should not increment for invalid cookie. + EXPECT_FALSE(SetCookie(cm.get(), GURL("https://www.foo.com"), + "A=B; Domain=malformedcookie.com")); + histograms.ExpectTotalCount(kHistogramName, 4); + + // Nor should it increment for a read operation + EXPECT_NE(GetCookies(cm.get(), GURL("https://www.foo.com")), ""); + histograms.ExpectTotalCount(kHistogramName, 4); + + // Make sure the correct histogram is chosen for localhost. + histograms.ExpectTotalCount(kHistogramNameLocal, 0); + + EXPECT_TRUE( + SetCookie(cm.get(), GURL("https://localhost:1234"), "local=host")); + histograms.ExpectTotalCount(kHistogramNameLocal, 1); + histograms.ExpectBucketCount(kHistogramNameLocal, + ReducePortRangeForCookieHistogram(1234), 1); +} + +TEST_F(CookieMonsterTest, CookiePortReadDiffersFromSetHistogram) { + base::HistogramTester histograms; + const char kHistogramName[] = "Cookie.Port.ReadDiffersFromSet.RemoteHost"; + const char kHistogramNameLocal[] = "Cookie.Port.ReadDiffersFromSet.Localhost"; + const char kHistogramNameDomainSet[] = + "Cookie.Port.ReadDiffersFromSet.DomainSet"; + + scoped_refptr<MockPersistentCookieStore> store(new MockPersistentCookieStore); + std::unique_ptr<CookieMonster> cm(new CookieMonster(store.get(), &net_log_)); + + histograms.ExpectTotalCount(kHistogramName, 0); + + // Set some cookies. One with a port, one without, and one with an invalid + // port. + EXPECT_TRUE(SetCookie(cm.get(), GURL("https://www.foo.com/withport"), + "A=B; Path=/withport")); // Port 443 + + auto unspecified_cookie = CanonicalCookie::Create( + GURL("https://www.foo.com/withoutport"), "C=D; Path=/withoutport", + base::Time::Now(), base::nullopt); + // Force to be unspecified. + unspecified_cookie->SetSourcePort(url::PORT_UNSPECIFIED); + EXPECT_TRUE(SetCanonicalCookieReturnAccessResult( + cm.get(), std::move(unspecified_cookie), + GURL("https://www.foo.com/withoutport"), + false /*can_modify_httponly*/) + .status.IsInclude()); + + auto invalid_cookie = CanonicalCookie::Create( + GURL("https://www.foo.com/invalidport"), "E=F; Path=/invalidport", + base::Time::Now(), base::nullopt); + // Force to be invalid. + invalid_cookie->SetSourcePort(99999); + EXPECT_TRUE(SetCanonicalCookieReturnAccessResult( + cm.get(), std::move(invalid_cookie), + GURL("https://www.foo.com/invalidport"), + false /*can_modify_httponly*/) + .status.IsInclude()); + + // Try same port. + EXPECT_EQ(GetCookies(cm.get(), GURL("https://www.foo.com/withport")), "A=B"); + histograms.ExpectTotalCount(kHistogramName, 1); + histograms.ExpectBucketCount(kHistogramName, + CookieMonster::CookieSentToSamePort::kYes, 1); + + // Try different port. + EXPECT_EQ(GetCookies(cm.get(), GURL("https://www.foo.com:8080/withport")), + "A=B"); + histograms.ExpectTotalCount(kHistogramName, 2); + histograms.ExpectBucketCount(kHistogramName, + CookieMonster::CookieSentToSamePort::kNo, 1); + + // Try different port, but it's the default for a different scheme. + EXPECT_EQ(GetCookies(cm.get(), GURL("http://www.foo.com/withport")), "A=B"); + histograms.ExpectTotalCount(kHistogramName, 3); + histograms.ExpectBucketCount( + kHistogramName, CookieMonster::CookieSentToSamePort::kNoButDefault, 1); + + // Now try it with an unspecified port cookie. + EXPECT_EQ(GetCookies(cm.get(), GURL("http://www.foo.com/withoutport")), + "C=D"); + histograms.ExpectTotalCount(kHistogramName, 4); + histograms.ExpectBucketCount( + kHistogramName, + CookieMonster::CookieSentToSamePort::kSourcePortUnspecified, 1); + + // Finally try it with an invalid port cookie. + EXPECT_EQ(GetCookies(cm.get(), GURL("http://www.foo.com/invalidport")), + "E=F"); + histograms.ExpectTotalCount(kHistogramName, 5); + histograms.ExpectBucketCount( + kHistogramName, CookieMonster::CookieSentToSamePort::kInvalid, 1); + + // Make sure the correct histogram is chosen for localhost. + histograms.ExpectTotalCount(kHistogramNameLocal, 0); + EXPECT_TRUE(SetCookie(cm.get(), GURL("https://localhost"), "local=host")); + + EXPECT_EQ(GetCookies(cm.get(), GURL("https://localhost")), "local=host"); + histograms.ExpectTotalCount(kHistogramNameLocal, 1); + histograms.ExpectBucketCount(kHistogramNameLocal, + CookieMonster::CookieSentToSamePort::kYes, 1); + + // Make sure the Domain set version works. + EXPECT_TRUE(SetCookie(cm.get(), GURL("https://www.foo.com/withDomain"), + "W=D; Domain=foo.com; Path=/withDomain")); + + histograms.ExpectTotalCount(kHistogramNameDomainSet, 0); + + EXPECT_EQ(GetCookies(cm.get(), GURL("https://www.foo.com/withDomain")), + "W=D"); + histograms.ExpectTotalCount(kHistogramNameDomainSet, 1); + histograms.ExpectBucketCount(kHistogramNameDomainSet, + CookieMonster::CookieSentToSamePort::kYes, 1); + // The RemoteHost histogram should also increase with this cookie. Domain + // cookies aren't special insofar as this metric is concerned. + histograms.ExpectTotalCount(kHistogramName, 6); + histograms.ExpectBucketCount(kHistogramName, + CookieMonster::CookieSentToSamePort::kYes, 2); } } // namespace net diff --git a/chromium/net/cookies/cookie_options.cc b/chromium/net/cookies/cookie_options.cc index 3ab86e67f0c..c05d0c00c23 100644 --- a/chromium/net/cookies/cookie_options.cc +++ b/chromium/net/cookies/cookie_options.cc @@ -51,12 +51,20 @@ CookieOptions::CookieOptions() update_access_time_(true), return_excluded_cookies_(false) {} +CookieOptions::CookieOptions(const CookieOptions& other) = default; +CookieOptions::CookieOptions(CookieOptions&& other) = default; +CookieOptions::~CookieOptions() = default; + +CookieOptions& CookieOptions::operator=(const CookieOptions&) = default; +CookieOptions& CookieOptions::operator=(CookieOptions&&) = default; + // static CookieOptions CookieOptions::MakeAllInclusive() { CookieOptions options; options.set_include_httponly(); options.set_same_site_cookie_context(SameSiteCookieContext::MakeInclusive()); options.set_do_not_update_access_time(); + options.set_full_party_context(std::set<net::SchemefulSite>()); return options; } diff --git a/chromium/net/cookies/cookie_options.h b/chromium/net/cookies/cookie_options.h index abf074b6aab..821ac334199 100644 --- a/chromium/net/cookies/cookie_options.h +++ b/chromium/net/cookies/cookie_options.h @@ -7,8 +7,12 @@ #ifndef NET_COOKIES_COOKIE_OPTIONS_H_ #define NET_COOKIES_COOKIE_OPTIONS_H_ +#include <set> + +#include "base/optional.h" #include "base/time/time.h" #include "net/base/net_export.h" +#include "net/base/schemeful_site.h" #include "net/cookies/cookie_constants.h" #include "url/gurl.h" @@ -88,13 +92,21 @@ class NET_EXPORT CookieOptions { // * 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_full_party_context()| 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; } @@ -118,6 +130,15 @@ class NET_EXPORT CookieOptions { void unset_return_excluded_cookies() { return_excluded_cookies_ = false; } bool return_excluded_cookies() const { return return_excluded_cookies_; } + void set_full_party_context( + const base::Optional<std::set<net::SchemefulSite>>& full_party_context) { + full_party_context_ = full_party_context; + } + const base::Optional<std::set<net::SchemefulSite>>& full_party_context() + const { + return full_party_context_; + } + // 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 @@ -130,7 +151,8 @@ class NET_EXPORT CookieOptions { bool exclude_httponly_; SameSiteCookieContext same_site_cookie_context_; bool update_access_time_; - bool return_excluded_cookies_; + bool return_excluded_cookies_ = false; + base::Optional<std::set<net::SchemefulSite>> full_party_context_; }; } // namespace net diff --git a/chromium/net/cookies/cookie_store_unittest.h b/chromium/net/cookies/cookie_store_unittest.h index c2f495bedce..29a520956b6 100644 --- a/chromium/net/cookies/cookie_store_unittest.h +++ b/chromium/net/cookies/cookie_store_unittest.h @@ -423,7 +423,7 @@ TYPED_TEST_P(CookieStoreTest, FilterTest) { std::unique_ptr<CanonicalCookie> cc(CanonicalCookie::CreateSanitizedCookie( this->www_foo_foo_.url(), "A", "B", std::string(), "/foo", one_hour_ago, one_hour_from_now, base::Time(), false, false, - CookieSameSite::STRICT_MODE, COOKIE_PRIORITY_DEFAULT)); + CookieSameSite::STRICT_MODE, COOKIE_PRIORITY_DEFAULT, false)); ASSERT_TRUE(cc); EXPECT_TRUE(this->SetCanonicalCookie( cs, std::move(cc), this->www_foo_foo_.url(), true /*modify_httponly*/)); @@ -433,7 +433,7 @@ TYPED_TEST_P(CookieStoreTest, FilterTest) { cc = CanonicalCookie::CreateSanitizedCookie( this->www_foo_bar_.url(), "C", "D", this->www_foo_bar_.domain(), "/bar", two_hours_ago, base::Time(), one_hour_ago, false, true, - CookieSameSite::STRICT_MODE, COOKIE_PRIORITY_DEFAULT); + CookieSameSite::STRICT_MODE, COOKIE_PRIORITY_DEFAULT, false); ASSERT_TRUE(cc); EXPECT_TRUE(this->SetCanonicalCookie( cs, std::move(cc), this->www_foo_bar_.url(), true /*modify_httponly*/)); @@ -444,13 +444,13 @@ TYPED_TEST_P(CookieStoreTest, FilterTest) { cc = CanonicalCookie::CreateSanitizedCookie( this->http_www_foo_.url(), "E", "F", std::string(), std::string(), base::Time(), base::Time(), base::Time(), true, false, - CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT); + CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT, false); ASSERT_FALSE(cc); cc = CanonicalCookie::CreateSanitizedCookie( this->https_www_foo_.url(), "E", "F", std::string(), std::string(), base::Time(), base::Time(), base::Time(), true, false, - CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT); + CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT, false); ASSERT_TRUE(cc); EXPECT_TRUE(this->SetCanonicalCookie( cs, std::move(cc), this->https_www_foo_.url(), true /*modify_httponly*/)); @@ -550,14 +550,14 @@ TYPED_TEST_P(CookieStoreTest, SetCanonicalCookieTest) { std::make_unique<CanonicalCookie>( "A", "B", foo_foo_host, "/foo", one_hour_ago, one_hour_from_now, base::Time(), false /* secure */, false /* httponly */, - CookieSameSite::LAX_MODE, COOKIE_PRIORITY_DEFAULT), + CookieSameSite::LAX_MODE, COOKIE_PRIORITY_DEFAULT, false), this->www_foo_foo_.url(), true)); EXPECT_TRUE(this->SetCanonicalCookie( cs, std::make_unique<CanonicalCookie>( "C", "D", "." + foo_bar_domain, "/bar", two_hours_ago, base::Time(), one_hour_ago, false, true, CookieSameSite::LAX_MODE, - COOKIE_PRIORITY_DEFAULT), + COOKIE_PRIORITY_DEFAULT, false), this->www_foo_bar_.url(), true)); // A secure source is required for setting secure cookies. @@ -567,7 +567,7 @@ TYPED_TEST_P(CookieStoreTest, SetCanonicalCookieTest) { std::make_unique<CanonicalCookie>( "E", "F", http_foo_host, "/", base::Time(), base::Time(), base::Time(), true, false, CookieSameSite::NO_RESTRICTION, - COOKIE_PRIORITY_DEFAULT), + COOKIE_PRIORITY_DEFAULT, false), this->http_www_foo_.url(), true) .status.HasExclusionReason( CookieInclusionStatus::EXCLUDE_SECURE_ONLY)); @@ -592,19 +592,20 @@ TYPED_TEST_P(CookieStoreTest, SetCanonicalCookieTest) { std::make_unique<CanonicalCookie>( "E", "F", https_foo_host, "/", base::Time(), base::Time(), base::Time(), true /* secure */, false /* httponly */, - CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT), + CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT, + false /* same_party */), this->https_www_foo_.url(), true /* modify_http_only */)); - EXPECT_TRUE( - this->SetCanonicalCookieReturnAccessResult( - cs, - std::make_unique<CanonicalCookie>( - "E", "F", http_foo_host, "/", base::Time(), base::Time(), - base::Time(), true /* secure */, false /* httponly */, - CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT), - this->http_www_foo_.url(), true /* modify_http_only */) - .status.HasExclusionReason( - CookieInclusionStatus::EXCLUDE_SECURE_ONLY)); + EXPECT_TRUE(this->SetCanonicalCookieReturnAccessResult( + cs, + std::make_unique<CanonicalCookie>( + "E", "F", http_foo_host, "/", base::Time(), + base::Time(), base::Time(), true /* secure */, + false /* httponly */, CookieSameSite::NO_RESTRICTION, + COOKIE_PRIORITY_DEFAULT, false /* same_party */), + this->http_www_foo_.url(), true /* modify_http_only */) + .status.HasExclusionReason( + CookieInclusionStatus::EXCLUDE_SECURE_ONLY)); if (TypeParam::supports_http_only) { // Permission to modify http only cookies is required to set an @@ -615,7 +616,7 @@ TYPED_TEST_P(CookieStoreTest, SetCanonicalCookieTest) { "G", "H", http_foo_host, "/unique", base::Time(), base::Time(), base::Time(), false /* secure */, true /* httponly */, CookieSameSite::LAX_MODE, - COOKIE_PRIORITY_DEFAULT), + COOKIE_PRIORITY_DEFAULT, false /* same_party */), this->http_www_foo_.url(), false /* modify_http_only */) .status.HasExclusionReason( CookieInclusionStatus::EXCLUDE_HTTP_ONLY)); @@ -641,7 +642,8 @@ TYPED_TEST_P(CookieStoreTest, SetCanonicalCookieTest) { std::make_unique<CanonicalCookie>( "G", "H", http_foo_host, "/unique", base::Time(), base::Time(), base::Time(), false /* secure */, true /* httponly */, - CookieSameSite::LAX_MODE, COOKIE_PRIORITY_DEFAULT), + CookieSameSite::LAX_MODE, COOKIE_PRIORITY_DEFAULT, + false /* same_party */), this->http_www_foo_.url(), true /* modify_http_only */)); EXPECT_TRUE(this->SetCanonicalCookieReturnAccessResult( @@ -650,7 +652,7 @@ TYPED_TEST_P(CookieStoreTest, SetCanonicalCookieTest) { "G", "H", http_foo_host, "/unique", base::Time(), base::Time(), base::Time(), false /* secure */, true /* httponly */, CookieSameSite::LAX_MODE, - COOKIE_PRIORITY_DEFAULT), + COOKIE_PRIORITY_DEFAULT, false /* same_party */), this->http_www_foo_.url(), false /* modify_http_only */) .status.HasExclusionReason( CookieInclusionStatus::EXCLUDE_HTTP_ONLY)); @@ -661,7 +663,8 @@ TYPED_TEST_P(CookieStoreTest, SetCanonicalCookieTest) { std::make_unique<CanonicalCookie>( "G", "H", http_foo_host, "/unique", base::Time(), base::Time(), base::Time(), false /* secure */, true /* httponly */, - CookieSameSite::LAX_MODE, COOKIE_PRIORITY_DEFAULT), + CookieSameSite::LAX_MODE, COOKIE_PRIORITY_DEFAULT, + false /* same_party */), this->http_www_foo_.url(), true /* modify_http_only */)); } @@ -739,28 +742,28 @@ TYPED_TEST_P(CookieStoreTest, SecureEnforcement) { std::make_unique<CanonicalCookie>( "A", "B", http_domain, "/", base::Time::Now(), base::Time(), base::Time(), true, false, CookieSameSite::STRICT_MODE, - COOKIE_PRIORITY_DEFAULT), + COOKIE_PRIORITY_DEFAULT, false /* same_party */), http_url, true /*modify_httponly*/)); EXPECT_TRUE(this->SetCanonicalCookie( cs, std::make_unique<CanonicalCookie>( "A", "B", http_domain, "/", base::Time::Now(), base::Time(), base::Time(), true, false, CookieSameSite::STRICT_MODE, - COOKIE_PRIORITY_DEFAULT), + COOKIE_PRIORITY_DEFAULT, false /* same_party */), https_url, true /*modify_httponly*/)); EXPECT_TRUE(this->SetCanonicalCookie( cs, std::make_unique<CanonicalCookie>( "A", "B", http_domain, "/", base::Time::Now(), base::Time(), base::Time(), false, false, CookieSameSite::STRICT_MODE, - COOKIE_PRIORITY_DEFAULT), + COOKIE_PRIORITY_DEFAULT, false /* same_party */), https_url, true /*modify_httponly*/)); EXPECT_TRUE(this->SetCanonicalCookie( cs, std::make_unique<CanonicalCookie>( "A", "B", http_domain, "/", base::Time::Now(), base::Time(), base::Time(), false, false, CookieSameSite::STRICT_MODE, - COOKIE_PRIORITY_DEFAULT), + COOKIE_PRIORITY_DEFAULT, false /* same_party */), http_url, true /*modify_httponly*/)); } diff --git a/chromium/net/cookies/cookie_util.cc b/chromium/net/cookies/cookie_util.cc index 86735d1d510..27d2486ba40 100644 --- a/chromium/net/cookies/cookie_util.cc +++ b/chromium/net/cookies/cookie_util.cc @@ -630,51 +630,6 @@ bool IsSchemefulSameSiteEnabled() { return base::FeatureList::IsEnabled(features::kSchemefulSameSite); } -bool IsRecentHttpSameSiteAccessGrantsLegacyCookieSemanticsEnabled() { - return IsSameSiteByDefaultCookiesEnabled() && - base::FeatureList::IsEnabled( - features::kRecentHttpSameSiteAccessGrantsLegacyCookieSemantics) && - features:: - kRecentHttpSameSiteAccessGrantsLegacyCookieSemanticsMilliseconds - .Get() > 0; -} - -bool IsRecentCreationTimeGrantsLegacyCookieSemanticsEnabled() { - return IsSameSiteByDefaultCookiesEnabled() && - base::FeatureList::IsEnabled( - features::kRecentCreationTimeGrantsLegacyCookieSemantics) && - features::kRecentCreationTimeGrantsLegacyCookieSemanticsMilliseconds - .Get() > 0; -} - -bool DoesLastHttpSameSiteAccessGrantLegacySemantics( - base::TimeTicks last_http_same_site_access) { - if (last_http_same_site_access.is_null()) - return false; - if (!IsRecentHttpSameSiteAccessGrantsLegacyCookieSemanticsEnabled()) - return false; - - base::TimeDelta recency_threshold = base::TimeDelta::FromMilliseconds( - features::kRecentHttpSameSiteAccessGrantsLegacyCookieSemanticsMilliseconds - .Get()); - DCHECK(!recency_threshold.is_zero()); - return (base::TimeTicks::Now() - last_http_same_site_access) < - recency_threshold; -} - -bool DoesCreationTimeGrantLegacySemantics(base::Time creation_date) { - if (creation_date.is_null()) - return false; - if (!IsRecentCreationTimeGrantsLegacyCookieSemanticsEnabled()) - return false; - - base::TimeDelta recency_threshold = base::TimeDelta::FromMilliseconds( - features::kRecentCreationTimeGrantsLegacyCookieSemanticsMilliseconds - .Get()); - DCHECK(!recency_threshold.is_zero()); - return (base::Time::Now() - creation_date) < recency_threshold; -} - base::OnceCallback<void(CookieAccessResult)> AdaptCookieAccessResultToBool( base::OnceCallback<void(bool)> callback) { return base::BindOnce( @@ -696,5 +651,20 @@ CookieList StripAccessResults( return cookies; } +NET_EXPORT void RecordCookiePortOmniboxHistograms(const GURL& url) { + int port = url.EffectiveIntPort(); + + if (port == url::PORT_UNSPECIFIED) + return; + + if (IsLocalhost(url)) { + UMA_HISTOGRAM_ENUMERATION("Cookie.Port.OmniboxURLNavigation.Localhost", + ReducePortRangeForCookieHistogram(port)); + } else { + UMA_HISTOGRAM_ENUMERATION("Cookie.Port.OmniboxURLNavigation.RemoteHost", + ReducePortRangeForCookieHistogram(port)); + } +} + } // namespace cookie_util } // namespace net diff --git a/chromium/net/cookies/cookie_util.h b/chromium/net/cookies/cookie_util.h index b2b7992d03b..89209de68f7 100644 --- a/chromium/net/cookies/cookie_util.h +++ b/chromium/net/cookies/cookie_util.h @@ -230,21 +230,6 @@ NET_EXPORT bool IsSameSiteCompatPair(const CanonicalCookie& c1, NET_EXPORT bool IsSameSiteByDefaultCookiesEnabled(); NET_EXPORT bool IsCookiesWithoutSameSiteMustBeSecureEnabled(); NET_EXPORT bool IsSchemefulSameSiteEnabled(); -bool IsRecentHttpSameSiteAccessGrantsLegacyCookieSemanticsEnabled(); -bool IsRecentCreationTimeGrantsLegacyCookieSemanticsEnabled(); - -// Determines whether the last same-site access to a cookie should grant legacy -// access semantics to the current attempted cookies access, based on the state -// of the feature kRecentSameSiteAccessGrantsLegacyCookieSemantics, the value of -// the feature param, and the time since the last eligible same-site access. -bool DoesLastHttpSameSiteAccessGrantLegacySemantics( - base::TimeTicks last_http_same_site_access); - -// Determines whether the creation time of a cookie should grant legacy -// access semantics to the current attempted cookies access, based on the state -// of the feature kRecentCreationTimeGrantsLegacyCookieSemantics, the value of -// the feature param, and the creation time of the cookie. -bool DoesCreationTimeGrantLegacySemantics(base::Time creation_date); // Takes a callback accepting a CookieAccessResult and returns a callback // that accepts a bool, setting the bool to true if the CookieInclusionStatus @@ -260,6 +245,9 @@ AdaptCookieAccessResultToBool(base::OnceCallback<void(bool)> callback); NET_EXPORT CookieList StripAccessResults(const CookieAccessResultList& cookie_access_result_list); +// Records port related metrics from Omnibox navigations. +NET_EXPORT void RecordCookiePortOmniboxHistograms(const GURL& url); + } // namespace cookie_util } // namespace net diff --git a/chromium/net/cookies/cookie_util_unittest.cc b/chromium/net/cookies/cookie_util_unittest.cc index 8093a6f440e..a4a3f625423 100644 --- a/chromium/net/cookies/cookie_util_unittest.cc +++ b/chromium/net/cookies/cookie_util_unittest.cc @@ -7,7 +7,7 @@ #include "base/callback.h" #include "base/strings/string_split.h" -#include "base/test/bind_test_util.h" +#include "base/test/bind.h" #include "net/cookies/cookie_util.h" #include "testing/gtest/include/gtest/gtest.h" diff --git a/chromium/net/cookies/parse_cookie_line_fuzzer.cc b/chromium/net/cookies/parse_cookie_line_fuzzer.cc index 4ea774ab748..037662209c2 100644 --- a/chromium/net/cookies/parse_cookie_line_fuzzer.cc +++ b/chromium/net/cookies/parse_cookie_line_fuzzer.cc @@ -25,7 +25,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { // Call zero or one of ParsedCookie's mutator methods. Should not call // anything other than SetName/SetValue when !IsValid(). - const uint8_t action = data_provider.ConsumeIntegralInRange(0, 10); + const uint8_t action = data_provider.ConsumeIntegralInRange(0, 11); switch (action) { case 1: parsed_cookie.SetName(GetArbitraryString(&data_provider)); @@ -62,6 +62,9 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { case 10: parsed_cookie.SetPriority(GetArbitraryString(&data_provider)); break; + case 11: + parsed_cookie.SetIsSameParty(data_provider.ConsumeBool()); + break; } } diff --git a/chromium/net/cookies/parsed_cookie.cc b/chromium/net/cookies/parsed_cookie.cc index 6da21345f8b..a2626774945 100644 --- a/chromium/net/cookies/parsed_cookie.cc +++ b/chromium/net/cookies/parsed_cookie.cc @@ -60,6 +60,7 @@ const char kSecureTokenName[] = "secure"; const char kHttpOnlyTokenName[] = "httponly"; const char kSameSiteTokenName[] = "samesite"; const char kPriorityTokenName[] = "priority"; +const char kSamePartyTokenName[] = "sameparty"; const char kTerminator[] = "\n\r\0"; const int kTerminatorLen = sizeof(kTerminator) - 1; @@ -129,15 +130,7 @@ bool IsControlCharacter(unsigned char c) { namespace net { -ParsedCookie::ParsedCookie(const std::string& cookie_line) - : path_index_(0), - domain_index_(0), - expires_index_(0), - maxage_index_(0), - secure_index_(0), - httponly_index_(0), - same_site_index_(0), - priority_index_(0) { +ParsedCookie::ParsedCookie(const std::string& cookie_line) { if (cookie_line.size() > kMaxCookieSize) { DVLOG(1) << "Not parsing cookie, too large: " << cookie_line.size(); return; @@ -232,6 +225,10 @@ bool ParsedCookie::SetPriority(const std::string& priority) { return SetString(&priority_index_, kPriorityTokenName, priority); } +bool ParsedCookie::SetIsSameParty(bool is_same_party) { + return SetBool(&same_party_index_, kSamePartyTokenName, is_same_party); +} + std::string ParsedCookie::ToCookieLine() const { std::string out; for (auto it = pairs_.begin(); it != pairs_.end(); ++it) { @@ -242,7 +239,8 @@ std::string ParsedCookie::ToCookieLine() const { // print it for the first pair(see crbug.com/977619). After the first pair, // we need to consider whether the name component is a special token. if (it == pairs_.begin() || - (it->first != kSecureTokenName && it->first != kHttpOnlyTokenName)) { + (it->first != kSecureTokenName && it->first != kHttpOnlyTokenName && + it->first != kSamePartyTokenName)) { out.append("="); out.append(it->second); } @@ -464,6 +462,8 @@ void ParsedCookie::SetupAttributes() { same_site_index_ = i; } else if (pairs_[i].first == kPriorityTokenName) { priority_index_ = i; + } else if (pairs_[i].first == kSamePartyTokenName) { + same_party_index_ = i; } else { /* some attribute we don't know or don't care about. */ } @@ -529,9 +529,9 @@ void ParsedCookie::ClearAttributePair(size_t index) { if (index == 0) return; - size_t* indexes[] = {&path_index_, &domain_index_, &expires_index_, - &maxage_index_, &secure_index_, &httponly_index_, - &same_site_index_, &priority_index_}; + size_t* indexes[] = {&path_index_, &domain_index_, &expires_index_, + &maxage_index_, &secure_index_, &httponly_index_, + &same_site_index_, &priority_index_, &same_party_index_}; for (size_t* attribute_index : indexes) { if (*attribute_index == index) *attribute_index = 0; diff --git a/chromium/net/cookies/parsed_cookie.h b/chromium/net/cookies/parsed_cookie.h index 7917c681c86..f555814bc2f 100644 --- a/chromium/net/cookies/parsed_cookie.h +++ b/chromium/net/cookies/parsed_cookie.h @@ -53,6 +53,7 @@ class NET_EXPORT ParsedCookie { CookieSameSite SameSite( CookieSameSiteString* samesite_string = nullptr) const; CookiePriority Priority() const; + bool IsSameParty() const { return same_party_index_ != 0; } // Returns the number of attributes, for example, returning 2 for: // "BLAH=hah; path=/; domain=.google.com" @@ -76,6 +77,7 @@ class NET_EXPORT ParsedCookie { bool SetIsHttpOnly(bool is_http_only); bool SetSameSite(const std::string& same_site); bool SetPriority(const std::string& priority); + bool SetIsSameParty(bool is_same_party); // Returns the cookie description as it appears in a HTML response header. std::string ToCookieLine() const; @@ -139,17 +141,16 @@ class NET_EXPORT ParsedCookie { PairList pairs_; // These will default to 0, but that should never be valid since the - // 0th index is the user supplied token/value, not an attribute. - // We're really never going to have more than like 8 attributes, so we - // could fit these into 3 bits each if we're worried about size... - size_t path_index_; - size_t domain_index_; - size_t expires_index_; - size_t maxage_index_; - size_t secure_index_; - size_t httponly_index_; - size_t same_site_index_; - size_t priority_index_; + // 0th index is the user supplied cookie name/value, not an attribute. + size_t path_index_ = 0; + size_t domain_index_ = 0; + size_t expires_index_ = 0; + size_t maxage_index_ = 0; + size_t secure_index_ = 0; + size_t httponly_index_ = 0; + size_t same_site_index_ = 0; + size_t priority_index_ = 0; + size_t same_party_index_ = 0; DISALLOW_COPY_AND_ASSIGN(ParsedCookie); }; diff --git a/chromium/net/cookies/parsed_cookie_unittest.cc b/chromium/net/cookies/parsed_cookie_unittest.cc index 27ce53d1249..d4671514d23 100644 --- a/chromium/net/cookies/parsed_cookie_unittest.cc +++ b/chromium/net/cookies/parsed_cookie_unittest.cc @@ -11,11 +11,38 @@ namespace net { TEST(ParsedCookieTest, TestBasic) { - ParsedCookie pc("a=b"); - EXPECT_TRUE(pc.IsValid()); - EXPECT_FALSE(pc.IsSecure()); - EXPECT_EQ("a", pc.Name()); - EXPECT_EQ("b", pc.Value()); + ParsedCookie pc1("a=b"); + EXPECT_TRUE(pc1.IsValid()); + EXPECT_FALSE(pc1.IsSecure()); + EXPECT_FALSE(pc1.IsHttpOnly()); + EXPECT_FALSE(pc1.IsSameParty()); + EXPECT_EQ("a", pc1.Name()); + EXPECT_EQ("b", pc1.Value()); + EXPECT_FALSE(pc1.HasPath()); + EXPECT_FALSE(pc1.HasDomain()); + EXPECT_FALSE(pc1.HasExpires()); + EXPECT_FALSE(pc1.HasMaxAge()); + EXPECT_EQ(CookieSameSite::UNSPECIFIED, pc1.SameSite()); + EXPECT_EQ(CookiePriority::COOKIE_PRIORITY_DEFAULT, pc1.Priority()); + + ParsedCookie pc2( + "c=d; secure; httponly; sameparty; path=/foo; domain=bar.test; " + "max-age=60; samesite=lax; priority=high"); + EXPECT_TRUE(pc2.IsValid()); + EXPECT_TRUE(pc2.IsSecure()); + EXPECT_TRUE(pc2.IsHttpOnly()); + EXPECT_TRUE(pc2.IsSameParty()); + EXPECT_EQ("c", pc2.Name()); + EXPECT_EQ("d", pc2.Value()); + EXPECT_TRUE(pc2.HasPath()); + EXPECT_EQ("/foo", pc2.Path()); + EXPECT_TRUE(pc2.HasDomain()); + EXPECT_EQ("bar.test", pc2.Domain()); + EXPECT_FALSE(pc2.HasExpires()); + EXPECT_TRUE(pc2.HasMaxAge()); + EXPECT_EQ("60", pc2.MaxAge()); + EXPECT_EQ(CookieSameSite::LAX_MODE, pc2.SameSite()); + EXPECT_EQ(CookiePriority::COOKIE_PRIORITY_HIGH, pc2.Priority()); } TEST(ParsedCookieTest, TestEmpty) { @@ -48,7 +75,7 @@ TEST(ParsedCookieTest, TestSetEmptyNameValue) { EXPECT_EQ("value", empty_name.Value()); EXPECT_FALSE(empty_name.SetValue("")); EXPECT_EQ("value", empty_name.Value()); - EXPECT_TRUE(empty_value.IsValid()); + EXPECT_TRUE(empty_name.IsValid()); } TEST(ParsedCookieTest, TestQuoted) { @@ -111,17 +138,18 @@ TEST(ParsedCookieTest, TestNameless) { TEST(ParsedCookieTest, TestAttributeCase) { ParsedCookie pc( - "BLAHHH; Path=/; sECuRe; httpONLY; sAmESitE=StrIct; pRIoRitY=hIgH"); + "BLAH; Path=/; sECuRe; httpONLY; sAmESitE=LaX; pRIoRitY=hIgH; samePaRtY"); EXPECT_TRUE(pc.IsValid()); EXPECT_TRUE(pc.IsSecure()); EXPECT_TRUE(pc.IsHttpOnly()); - EXPECT_EQ(CookieSameSite::STRICT_MODE, pc.SameSite()); + EXPECT_TRUE(pc.IsSameParty()); + EXPECT_EQ(CookieSameSite::LAX_MODE, pc.SameSite()); EXPECT_TRUE(pc.HasPath()); EXPECT_EQ("/", pc.Path()); EXPECT_EQ("", pc.Name()); - EXPECT_EQ("BLAHHH", pc.Value()); + EXPECT_EQ("BLAH", pc.Value()); EXPECT_EQ(COOKIE_PRIORITY_HIGH, pc.Priority()); - EXPECT_EQ(5U, pc.NumberOfAttributes()); + EXPECT_EQ(6U, pc.NumberOfAttributes()); } TEST(ParsedCookieTest, TestDoubleQuotedNameless) { @@ -367,10 +395,11 @@ TEST(ParsedCookieTest, SetAttributes) { EXPECT_TRUE(pc.SetIsHttpOnly(true)); EXPECT_TRUE(pc.SetSameSite("LAX")); EXPECT_TRUE(pc.SetPriority("HIGH")); + EXPECT_TRUE(pc.SetIsSameParty(true)); EXPECT_EQ( "name=value; domain=domain.com; path=/; " "expires=Sun, 18-Apr-2027 21:06:29 GMT; max-age=12345; secure; " - "httponly; samesite=LAX; priority=HIGH", + "httponly; samesite=LAX; priority=HIGH; sameparty", pc.ToCookieLine()); EXPECT_TRUE(pc.HasDomain()); EXPECT_TRUE(pc.HasPath()); @@ -380,22 +409,35 @@ TEST(ParsedCookieTest, SetAttributes) { EXPECT_TRUE(pc.IsHttpOnly()); EXPECT_EQ(CookieSameSite::LAX_MODE, pc.SameSite()); EXPECT_EQ(COOKIE_PRIORITY_HIGH, pc.Priority()); + EXPECT_TRUE(pc.IsSameParty()); - // Clear one attribute from the middle. + // Modify one attribute in the middle. EXPECT_TRUE(pc.SetPath("/foo")); EXPECT_TRUE(pc.HasDomain()); EXPECT_TRUE(pc.HasPath()); + EXPECT_EQ("/foo", pc.Path()); EXPECT_TRUE(pc.HasExpires()); EXPECT_TRUE(pc.IsSecure()); EXPECT_TRUE(pc.IsHttpOnly()); + EXPECT_TRUE(pc.IsSameParty()); EXPECT_EQ( "name=value; domain=domain.com; path=/foo; " "expires=Sun, 18-Apr-2027 21:06:29 GMT; max-age=12345; secure; " - "httponly; samesite=LAX; priority=HIGH", + "httponly; samesite=LAX; priority=HIGH; sameparty", pc.ToCookieLine()); // Set priority to medium. EXPECT_TRUE(pc.SetPriority("medium")); + EXPECT_EQ(CookiePriority::COOKIE_PRIORITY_MEDIUM, pc.Priority()); + EXPECT_EQ( + "name=value; domain=domain.com; path=/foo; " + "expires=Sun, 18-Apr-2027 21:06:29 GMT; max-age=12345; secure; " + "httponly; samesite=LAX; priority=medium; sameparty", + pc.ToCookieLine()); + + // Clear attribute from the end. + EXPECT_TRUE(pc.SetIsSameParty(false)); + EXPECT_FALSE(pc.IsSameParty()); EXPECT_EQ( "name=value; domain=domain.com; path=/foo; " "expires=Sun, 18-Apr-2027 21:06:29 GMT; max-age=12345; secure; " @@ -421,6 +463,7 @@ TEST(ParsedCookieTest, SetAttributes) { EXPECT_FALSE(pc.IsHttpOnly()); EXPECT_EQ(CookieSameSite::UNSPECIFIED, pc.SameSite()); EXPECT_EQ("name2=value2", pc.ToCookieLine()); + EXPECT_FALSE(pc.IsSameParty()); } // Set the domain attribute twice in a cookie line. If the second attribute's @@ -570,8 +613,8 @@ TEST(ParsedCookieTest, SettersInputValidation) { } TEST(ParsedCookieTest, ToCookieLineSpecialTokens) { - // Special tokens "secure" and "httponly" should be treated as any other name - // when they are in the first position. + // Special tokens "secure", "httponly", and "sameparty" should be treated as + // any other name when they are in the first position. { ParsedCookie pc(""); pc.SetName("secure"); @@ -594,6 +637,10 @@ TEST(ParsedCookieTest, ToCookieLineSpecialTokens) { EXPECT_EQ(pc.ToCookieLine(), "httponly=foo"); } { + ParsedCookie pc("sameparty=foo"); + EXPECT_EQ(pc.ToCookieLine(), "sameparty=foo"); + } + { ParsedCookie pc("foo"); pc.SetName("secure"); EXPECT_EQ(pc.ToCookieLine(), "secure=foo"); @@ -622,9 +669,29 @@ TEST(ParsedCookieTest, ToCookieLineSpecialTokens) { EXPECT_EQ(pc.ToCookieLine(), "name=foo; httponly"); } { + ParsedCookie pc("name=foo; sameparty=baz"); + EXPECT_EQ(pc.ToCookieLine(), "name=foo; sameparty"); + } + { ParsedCookie pc("name=foo; bar=secure"); EXPECT_EQ(pc.ToCookieLine(), "name=foo; bar=secure"); } + // Repeated instances of the special tokens are also fine. + { + ParsedCookie pc("name=foo; secure; secure=yesplease; secure; secure"); + EXPECT_TRUE(pc.IsValid()); + EXPECT_TRUE(pc.IsSecure()); + EXPECT_FALSE(pc.IsHttpOnly()); + EXPECT_FALSE(pc.IsSameParty()); + } + { + ParsedCookie pc("sameparty; sameparty; secure; httponly; httponly; secure"); + EXPECT_EQ("", pc.Name()); + EXPECT_EQ("sameparty", pc.Value()); + EXPECT_TRUE(pc.IsSecure()); + EXPECT_TRUE(pc.IsSameParty()); + EXPECT_TRUE(pc.IsHttpOnly()); + } } TEST(ParsedCookieTest, SameSiteValues) { |