// Copyright 2024 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "net/device_bound_sessions/cookie_craving.h" #include "base/strings/string_util.h" #include "base/unguessable_token.h" #include "net/cookies/canonical_cookie.h" #include "net/cookies/cookie_constants.h" #include "net/cookies/cookie_partition_key.h" #include "net/device_bound_sessions/proto/storage.pb.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" namespace net::device_bound_sessions { // Default values for tests. constexpr char kUrlString[] = "https://www.example.test/foo"; constexpr char kName[] = "name"; const base::Time kCreationTime = base::Time::Now(); // Helper to Create() and unwrap a CookieCraving, expecting it to be valid. CookieCraving CreateValidCookieCraving( const GURL& url, const std::string& name, const std::string& attributes, base::Time creation_time = kCreationTime, std::optional cookie_partition_key = std::nullopt) { std::optional maybe_cc = CookieCraving::Create( url, name, attributes, creation_time, cookie_partition_key); EXPECT_TRUE(maybe_cc); EXPECT_TRUE(maybe_cc->IsValid()); return std::move(*maybe_cc); } // Helper to create and unwrap a CanonicalCookie. CanonicalCookie CreateCanonicalCookie( const GURL& url, const std::string& cookie_line, base::Time creation_time = kCreationTime, std::optional cookie_partition_key = std::nullopt) { std::unique_ptr canonical_cookie = CanonicalCookie::CreateForTesting(url, cookie_line, creation_time, /*server_time=*/std::nullopt, cookie_partition_key); EXPECT_TRUE(canonical_cookie); EXPECT_TRUE(canonical_cookie->IsCanonical()); return *canonical_cookie; } TEST(CookieCravingTest, CreateBasic) { // Default cookie. CookieCraving cc = CreateValidCookieCraving(GURL(kUrlString), kName, ""); EXPECT_EQ(cc.Name(), kName); EXPECT_EQ(cc.Domain(), "www.example.test"); EXPECT_EQ(cc.Path(), "/"); EXPECT_EQ(cc.CreationDate(), kCreationTime); EXPECT_FALSE(cc.SecureAttribute()); EXPECT_FALSE(cc.IsHttpOnly()); EXPECT_EQ(cc.SameSite(), CookieSameSite::UNSPECIFIED); EXPECT_EQ(cc.PartitionKey(), std::nullopt); EXPECT_EQ(cc.SourceScheme(), CookieSourceScheme::kSecure); EXPECT_EQ(cc.SourcePort(), 443); // Non-default attributes. cc = CreateValidCookieCraving( GURL(kUrlString), kName, "Secure; HttpOnly; Path=/foo; Domain=example.test; SameSite=Lax"); EXPECT_EQ(cc.Name(), kName); EXPECT_EQ(cc.Domain(), ".example.test"); EXPECT_EQ(cc.Path(), "/foo"); EXPECT_EQ(cc.CreationDate(), kCreationTime); EXPECT_TRUE(cc.SecureAttribute()); EXPECT_TRUE(cc.IsHttpOnly()); EXPECT_EQ(cc.SameSite(), CookieSameSite::LAX_MODE); EXPECT_EQ(cc.PartitionKey(), std::nullopt); EXPECT_EQ(cc.SourceScheme(), CookieSourceScheme::kSecure); EXPECT_EQ(cc.SourcePort(), 443); // Normalize whitespace. cc = CreateValidCookieCraving( GURL(kUrlString), " name ", " Secure;HttpOnly;Path = /foo; Domain= example.test; SameSite =Lax "); EXPECT_EQ(cc.Name(), "name"); EXPECT_EQ(cc.Domain(), ".example.test"); EXPECT_EQ(cc.Path(), "/foo"); EXPECT_EQ(cc.CreationDate(), kCreationTime); EXPECT_TRUE(cc.SecureAttribute()); EXPECT_TRUE(cc.IsHttpOnly()); EXPECT_EQ(cc.SameSite(), CookieSameSite::LAX_MODE); EXPECT_EQ(cc.PartitionKey(), std::nullopt); EXPECT_EQ(cc.SourceScheme(), CookieSourceScheme::kSecure); EXPECT_EQ(cc.SourcePort(), 443); } TEST(CookieCravingTest, CreateWithPartitionKey) { // The site of the partition key is not checked in Create(), so these two // should behave the same. const CookiePartitionKey kSameSitePartitionKey = CookiePartitionKey::FromURLForTesting(GURL("https://auth.example.test")); const CookiePartitionKey kCrossSitePartitionKey = CookiePartitionKey::FromURLForTesting(GURL("https://www.other.test")); // A key with a nonce might be used for a fenced frame or anonymous iframe. const CookiePartitionKey kNoncedPartitionKey = CookiePartitionKey::FromURLForTesting( GURL("https://www.anonymous-iframe.test"), CookiePartitionKey::AncestorChainBit::kCrossSite, base::UnguessableToken::Create()); for (const CookiePartitionKey& partition_key : {kSameSitePartitionKey, kCrossSitePartitionKey, kNoncedPartitionKey}) { // Partitioned cookies must be set with Secure. The __Host- prefix is not // required. CookieCraving cc = CreateValidCookieCraving(GURL(kUrlString), kName, "Secure; Partitioned", kCreationTime, partition_key); EXPECT_TRUE(cc.SecureAttribute()); EXPECT_TRUE(cc.IsPartitioned()); EXPECT_EQ(cc.PartitionKey(), partition_key); } // If a cookie is not set with a Partitioned attribute, the partition key // should be ignored and cleared (if it's a normal partition key). for (const CookiePartitionKey& partition_key : {kSameSitePartitionKey, kCrossSitePartitionKey}) { CookieCraving cc = CreateValidCookieCraving( GURL(kUrlString), kName, "Secure", kCreationTime, partition_key); EXPECT_TRUE(cc.SecureAttribute()); EXPECT_FALSE(cc.IsPartitioned()); EXPECT_EQ(cc.PartitionKey(), std::nullopt); } // For nonced partition keys, the Partitioned attribute is not explicitly // required in order for the cookie to be considered partitioned. CookieCraving cc = CreateValidCookieCraving( GURL(kUrlString), kName, "Secure", kCreationTime, kNoncedPartitionKey); EXPECT_TRUE(cc.SecureAttribute()); EXPECT_TRUE(cc.IsPartitioned()); EXPECT_EQ(cc.PartitionKey(), kNoncedPartitionKey); // The Secure attribute is also not required for a nonced partition key. cc = CreateValidCookieCraving(GURL(kUrlString), kName, "", kCreationTime, kNoncedPartitionKey); EXPECT_FALSE(cc.SecureAttribute()); EXPECT_TRUE(cc.IsPartitioned()); EXPECT_EQ(cc.PartitionKey(), kNoncedPartitionKey); } TEST(CookieCravingTest, CreateWithPrefix) { // Valid __Host- cookie. CookieCraving cc = CreateValidCookieCraving(GURL(kUrlString), "__Host-blah", "Secure; Path=/"); EXPECT_EQ(cc.Domain(), "www.example.test"); EXPECT_EQ(cc.Path(), "/"); EXPECT_TRUE(cc.SecureAttribute()); // Valid __Secure- cookie. cc = CreateValidCookieCraving(GURL(kUrlString), "__Secure-blah", "Secure; Path=/foo; Domain=example.test"); EXPECT_TRUE(cc.SecureAttribute()); } // Test various strange inputs that should still be valid. TEST(CookieCravingTest, CreateStrange) { const char* kStrangeNames[] = { // Empty name is permitted. "", // Leading and trailing whitespace should get trimmed. " name ", // Internal whitespace is allowed. "n a m e", // Trim leading and trailing whitespace while preserving internal // whitespace. " n a m e ", }; for (const char* name : kStrangeNames) { CookieCraving cc = CreateValidCookieCraving(GURL(kUrlString), name, ""); EXPECT_EQ(cc.Name(), base::TrimWhitespaceASCII(name, base::TRIM_ALL)); } const char* kStrangeAttributesLines[] = { // Capitalization. "SECURE; PATH=/; SAMESITE=LAX", // Leading semicolon. "; Secure; Path=/; SameSite=Lax", // Empty except for semicolons. ";;;", // Extra whitespace. " Secure; Path=/; SameSite=Lax ", // No whitespace. "Secure;Path=/;SameSite=Lax", // Domain attribute with leading dot. "Domain=.example.test", // Different path from the URL is allowed. "Path=/different", // Path not beginning with '/' is allowed. (It's just ignored.) "Path=noslash", // Attributes with extraneous values. "Secure=true; HttpOnly=yes; Partitioned=absolutely", // Unknown attributes or attribute values. "Fake=totally; SameSite=SuperStrict", }; for (const char* attributes : kStrangeAttributesLines) { CreateValidCookieCraving(GURL(kUrlString), kName, attributes); } } // Another strange/maybe unexpected case is that Create() does not check the // secureness of the URL against the cookie's Secure attribute. (This is // documented in the method comment.) TEST(CookieCravingTest, CreateSecureFromInsecureUrl) { CookieCraving cc = CreateValidCookieCraving(GURL("http://insecure.test"), kName, "Secure"); EXPECT_TRUE(cc.SecureAttribute()); EXPECT_EQ(cc.SourceScheme(), CookieSourceScheme::kNonSecure); } // Test inputs that should result in a failure to parse the cookie line. TEST(CookieCravingTest, CreateFailParse) { const struct { const char* name; const char* attributes; } kParseFailInputs[] = { // Invalid characters in name. {"blah\nsomething", "Secure; Path=/"}, {"blah=something", "Secure; Path=/"}, {"blah;something", "Secure; Path=/"}, // Truncated lines are blocked. {"name", "Secure;\n Path=/"}, }; for (const auto& input : kParseFailInputs) { std::optional cc = CookieCraving::Create(GURL(kUrlString), input.name, input.attributes, kCreationTime, std::nullopt); EXPECT_FALSE(cc); } } // Test cases where the Create() params are not valid. TEST(CookieCravingTest, CreateFailInvalidParams) { // Invalid URL. std::optional cc = CookieCraving::Create(GURL(), kName, "", kCreationTime, std::nullopt); EXPECT_FALSE(cc); // Null creation time. cc = CookieCraving::Create(GURL(kUrlString), kName, "", base::Time(), std::nullopt); EXPECT_FALSE(cc); } TEST(CookieCravingTest, CreateFailBadDomain) { // URL does not match domain. std::optional cc = CookieCraving::Create(GURL(kUrlString), kName, "Domain=other.test", kCreationTime, std::nullopt); EXPECT_FALSE(cc); // Public suffix is not allowed to be Domain attribute. cc = CookieCraving::Create(GURL(kUrlString), kName, "Domain=test", kCreationTime, std::nullopt); EXPECT_FALSE(cc); // IP addresses cannot set suffixes as the Domain attribute. cc = CookieCraving::Create(GURL("http://1.2.3.4"), kName, "Domain=2.3.4", kCreationTime, std::nullopt); EXPECT_FALSE(cc); } TEST(CookieCravingTest, CreateFailBadPartitioned) { const CookiePartitionKey kPartitionKey = CookiePartitionKey::FromURLForTesting(GURL("https://example.test")); // Not Secure. std::optional cc = CookieCraving::Create( GURL(kUrlString), kName, "Partitioned", kCreationTime, kPartitionKey); EXPECT_FALSE(cc); // The URL scheme is not cryptographic. cc = CookieCraving::Create(GURL("http://example.test"), kName, "Secure; Partitioned", kCreationTime, kPartitionKey); EXPECT_FALSE(cc); } TEST(CookieCravingTest, CreateFailInvalidPrefix) { // __Host- with insecure URL. std::optional cc = CookieCraving::Create(GURL("http://insecure.test"), "__Host-blah", "Secure; Path=/", kCreationTime, std::nullopt); EXPECT_FALSE(cc); // __Host- with non-Secure cookie. cc = CookieCraving::Create(GURL(kUrlString), "__Host-blah", "Path=/", kCreationTime, std::nullopt); EXPECT_FALSE(cc); // __Host- with Domain attribute value. cc = CookieCraving::Create(GURL(kUrlString), "__Host-blah", "Secure; Path=/; Domain=example.test", kCreationTime, std::nullopt); EXPECT_FALSE(cc); // __Host- with non-root path. cc = CookieCraving::Create(GURL(kUrlString), "__Host-blah", "Secure; Path=/foo", kCreationTime, std::nullopt); EXPECT_FALSE(cc); // __Secure- with non-Secure cookie. cc = CookieCraving::Create(GURL(kUrlString), "__Secure-blah", "", kCreationTime, std::nullopt); EXPECT_FALSE(cc); // Prefixes are checked case-insensitively, so these CookieCravings are also // invalid for not satisfying the prefix requirements. // Missing Secure. cc = CookieCraving::Create(GURL(kUrlString), "__host-blah", "Path=/", kCreationTime, std::nullopt); EXPECT_FALSE(cc); // Specifies Domain. cc = CookieCraving::Create(GURL(kUrlString), "__HOST-blah", "Secure; Path=/; Domain=example.test", kCreationTime, std::nullopt); EXPECT_FALSE(cc); // Missing Secure. cc = CookieCraving::Create(GURL(kUrlString), "__SeCuRe-blah", "", kCreationTime, std::nullopt); EXPECT_FALSE(cc); } // Valid cases were tested as part of the successful Create() tests above, so // this only tests the invalid cases. TEST(CookieCravingTest, IsNotValid) { const struct { const char* name; const char* domain; const char* path; bool secure; base::Time creation = kCreationTime; } kTestCases[] = { // Invalid name. {" name", "www.example.test", "/", true}, {";", "www.example.test", "/", true}, {"=", "www.example.test", "/", true}, {"na\nme", "www.example.test", "/", true}, // Empty domain. {"name", "", "/", true}, // Non-canonical domain. {"name", "ExAmPlE.test", "/", true}, // Empty path. {"name", "www.example.test", "", true}, // Path not beginning with slash. {"name", "www.example.test", "noslash", true}, // Invalid __Host- prefix. {"__Host-name", ".example.test", "/", true}, {"__Host-name", "www.example.test", "/", false}, {"__Host-name", "www.example.test", "/foo", false}, // Invalid __Secure- prefix. {"__Secure-name", "www.example.test", "/", false}, // Invalid __Host- prefix (case insensitive). {"__HOST-name", ".example.test", "/", true}, {"__HoSt-name", "www.example.test", "/", false}, {"__host-name", "www.example.test", "/foo", false}, // Invalid __Secure- prefix (case insensitive). {"__secure-name", "www.example.test", "/", false}, // Null creation date. {"name", "www.example.test", "/", true, base::Time()}, }; for (const auto& test_case : kTestCases) { CookieCraving cc = CookieCraving::CreateUnsafeForTesting( test_case.name, test_case.domain, test_case.path, test_case.creation, test_case.secure, /*httponly=*/false, CookieSameSite::LAX_MODE, /*partition_key=*/std::nullopt, CookieSourceScheme::kSecure, 443); SCOPED_TRACE(cc.DebugString()); EXPECT_FALSE(cc.IsValid()); } // Additionally, Partitioned requires the Secure attribute. CookieCraving cc = CookieCraving::CreateUnsafeForTesting( "name", "www.example.test", "/", kCreationTime, /*secure=*/false, /*httponly=*/false, CookieSameSite::LAX_MODE, CookiePartitionKey::FromURLForTesting(GURL("https://example.test")), CookieSourceScheme::kSecure, 443); EXPECT_FALSE(cc.IsValid()); } TEST(CookieCravingTest, IsSatisfiedBy) { // Default case with no attributes. CanonicalCookie canonical_cookie = CreateCanonicalCookie(GURL(kUrlString), "name=somevalue"); CookieCraving cookie_craving = CreateValidCookieCraving(GURL(kUrlString), "name", ""); EXPECT_TRUE(cookie_craving.IsSatisfiedBy(canonical_cookie)); // With attributes. canonical_cookie = CreateCanonicalCookie(GURL(kUrlString), "name=somevalue; Domain=example.test; Path=/; " "Secure; HttpOnly; SameSite=Lax"); cookie_craving = CreateValidCookieCraving( GURL(kUrlString), "name", "Domain=example.test; Path=/; Secure; HttpOnly; SameSite=Lax"); EXPECT_TRUE(cookie_craving.IsSatisfiedBy(canonical_cookie)); // The URL may differ as long as the cookie attributes match. canonical_cookie = CreateCanonicalCookie( GURL(kUrlString), "name=somevalue; Domain=example.test"); cookie_craving = CreateValidCookieCraving( GURL("https://subdomain.example.test"), "name", "Domain=example.test"); EXPECT_TRUE(cookie_craving.IsSatisfiedBy(canonical_cookie)); // Creation time is not required to match. canonical_cookie = CreateCanonicalCookie( GURL(kUrlString), "name=somevalue; Domain=example.test", kCreationTime); cookie_craving = CreateValidCookieCraving(GURL(kUrlString), "name", "Domain=example.test", kCreationTime + base::Hours(1)); EXPECT_TRUE(cookie_craving.IsSatisfiedBy(canonical_cookie)); // Source scheme and port (and indeed source host) are not required to match. canonical_cookie = CreateCanonicalCookie( GURL(kUrlString), "name=somevalue; Domain=example.test"); cookie_craving = CreateValidCookieCraving(GURL("http://subdomain.example.test:8080"), "name", "Domain=example.test"); EXPECT_TRUE(cookie_craving.IsSatisfiedBy(canonical_cookie)); } TEST(CookieCravingTest, IsNotSatisfiedBy) { // Name does not match. CanonicalCookie canonical_cookie = CreateCanonicalCookie(GURL(kUrlString), "realname=somevalue"); CookieCraving cookie_craving = CreateValidCookieCraving(GURL(kUrlString), "fakename", ""); EXPECT_FALSE(cookie_craving.IsSatisfiedBy(canonical_cookie)); // Domain does not match. canonical_cookie = CreateCanonicalCookie( GURL(kUrlString), "name=somevalue; Domain=example.test"); cookie_craving = CreateValidCookieCraving(GURL(kUrlString), "name", "Domain=www.example.test"); EXPECT_FALSE(cookie_craving.IsSatisfiedBy(canonical_cookie)); // Host cookie vs domain cookie. canonical_cookie = CreateCanonicalCookie(GURL(kUrlString), "name=somevalue"); cookie_craving = CreateValidCookieCraving(GURL(kUrlString), "name", "Domain=www.example.test"); EXPECT_FALSE(cookie_craving.IsSatisfiedBy(canonical_cookie)); // Domain cookie vs host cookie. canonical_cookie = CreateCanonicalCookie( GURL(kUrlString), "name=somevalue; Domain=www.example.test"); cookie_craving = CreateValidCookieCraving(GURL(kUrlString), "name", ""); EXPECT_FALSE(cookie_craving.IsSatisfiedBy(canonical_cookie)); // Path does not match. canonical_cookie = CreateCanonicalCookie( GURL(kUrlString), "name=somevalue; Domain=example.test; Path=/"); cookie_craving = CreateValidCookieCraving(GURL(kUrlString), "name", "Domain=example.test; Path=/foo"); EXPECT_FALSE(cookie_craving.IsSatisfiedBy(canonical_cookie)); // Secure vs non-Secure. canonical_cookie = CreateCanonicalCookie( GURL(kUrlString), "name=somevalue; Secure; Domain=example.test; Path=/"); cookie_craving = CreateValidCookieCraving(GURL(kUrlString), "name", "Domain=example.test; Path=/foo"); EXPECT_FALSE(cookie_craving.IsSatisfiedBy(canonical_cookie)); // Non-Secure vs Secure. canonical_cookie = CreateCanonicalCookie( GURL(kUrlString), "name=somevalue; Domain=example.test; Path=/"); cookie_craving = CreateValidCookieCraving( GURL(kUrlString), "name", "Secure; Domain=example.test; Path=/foo"); EXPECT_FALSE(cookie_craving.IsSatisfiedBy(canonical_cookie)); // HttpOnly vs non-HttpOnly. canonical_cookie = CreateCanonicalCookie( GURL(kUrlString), "name=somevalue; HttpOnly; Domain=example.test; Path=/"); cookie_craving = CreateValidCookieCraving(GURL(kUrlString), "name", "Domain=example.test; Path=/foo"); EXPECT_FALSE(cookie_craving.IsSatisfiedBy(canonical_cookie)); // Non-HttpOnly vs HttpOnly. canonical_cookie = CreateCanonicalCookie( GURL(kUrlString), "name=somevalue; Domain=example.test; Path=/"); cookie_craving = CreateValidCookieCraving( GURL(kUrlString), "name", "HttpOnly; Domain=example.test; Path=/foo"); EXPECT_FALSE(cookie_craving.IsSatisfiedBy(canonical_cookie)); // SameSite does not match. canonical_cookie = CreateCanonicalCookie(GURL(kUrlString), "name=somevalue; SameSite=Lax"); cookie_craving = CreateValidCookieCraving(GURL(kUrlString), "name", "SameSite=Strict"); EXPECT_FALSE(cookie_craving.IsSatisfiedBy(canonical_cookie)); // SameSite vs unspecified SameSite. (Note that the SameSite attribute value // is compared, not the effective SameSite enforcement mode.) canonical_cookie = CreateCanonicalCookie(GURL(kUrlString), "name=somevalue; SameSite=Lax"); cookie_craving = CreateValidCookieCraving(GURL(kUrlString), "name", ""); EXPECT_FALSE(cookie_craving.IsSatisfiedBy(canonical_cookie)); } TEST(CookieCravingTest, IsSatisfiedByWithPartitionKey) { const CookiePartitionKey kPartitionKey = CookiePartitionKey::FromURLForTesting(GURL("https://example.test")); const CookiePartitionKey kOtherPartitionKey = CookiePartitionKey::FromURLForTesting(GURL("https://other.test")); const base::UnguessableToken kNonce = base::UnguessableToken::Create(); const CookiePartitionKey kNoncedPartitionKey = CookiePartitionKey::FromURLForTesting( GURL("https://example.test"), CookiePartitionKey::AncestorChainBit::kCrossSite, kNonce); // Partition keys match. CanonicalCookie canonical_cookie = CreateCanonicalCookie( GURL(kUrlString), "name=somevalue; Secure; Partitioned", kCreationTime, kPartitionKey); CookieCraving cookie_craving = CreateValidCookieCraving(GURL(kUrlString), "name", "Secure; Partitioned", kCreationTime, kPartitionKey); EXPECT_TRUE(cookie_craving.IsSatisfiedBy(canonical_cookie)); // Cookie line doesn't specified Partitioned so key gets cleared for both. canonical_cookie = CreateCanonicalCookie( GURL(kUrlString), "name=somevalue; Secure", kCreationTime, kPartitionKey); cookie_craving = CreateValidCookieCraving(GURL(kUrlString), "name", "Secure", kCreationTime, kOtherPartitionKey); EXPECT_TRUE(cookie_craving.IsSatisfiedBy(canonical_cookie)); // Without partition key for the CookieCraving, but cookie line doesn't // specify Partitioned so they are equivalent. canonical_cookie = CreateCanonicalCookie( GURL(kUrlString), "name=somevalue; Secure", kCreationTime, kPartitionKey); cookie_craving = CreateValidCookieCraving(GURL(kUrlString), "name", "Secure"); EXPECT_TRUE(cookie_craving.IsSatisfiedBy(canonical_cookie)); // Without partition key for the CanonicalCookie, but cookie line doesn't // specify Partitioned so they are equivalent. canonical_cookie = CreateCanonicalCookie(GURL(kUrlString), "name=somevalue; Secure"); cookie_craving = CreateValidCookieCraving(GURL(kUrlString), "name", "Secure", kCreationTime, kPartitionKey); EXPECT_TRUE(cookie_craving.IsSatisfiedBy(canonical_cookie)); // Identical nonced partition keys. canonical_cookie = CreateCanonicalCookie(GURL(kUrlString), "name=somevalue; Secure", kCreationTime, kNoncedPartitionKey); cookie_craving = CreateValidCookieCraving(GURL(kUrlString), "name", "Secure", kCreationTime, kNoncedPartitionKey); EXPECT_TRUE(cookie_craving.IsSatisfiedBy(canonical_cookie)); } TEST(CookieCravingTest, IsNotSatisfiedByWithPartitionKey) { const CookiePartitionKey kPartitionKey = CookiePartitionKey::FromURLForTesting(GURL("https://example.test")); const CookiePartitionKey kOtherPartitionKey = CookiePartitionKey::FromURLForTesting(GURL("https://other.test")); const base::UnguessableToken kNonce = base::UnguessableToken::Create(); const base::UnguessableToken kOtherNonce = base::UnguessableToken::Create(); const CookiePartitionKey kNoncedPartitionKey = CookiePartitionKey::FromURLForTesting( GURL("https://example.test"), CookiePartitionKey::AncestorChainBit::kCrossSite, kNonce); const CookiePartitionKey kOtherNoncedPartitionKey = CookiePartitionKey::FromURLForTesting( GURL("https://example.test"), CookiePartitionKey::AncestorChainBit::kCrossSite, kOtherNonce); // Partition keys do not match. CanonicalCookie canonical_cookie = CreateCanonicalCookie( GURL(kUrlString), "name=somevalue; Secure; Partitioned", kCreationTime, kPartitionKey); CookieCraving cookie_craving = CreateValidCookieCraving(GURL(kUrlString), "name", "Secure; Partitioned", kCreationTime, kOtherPartitionKey); EXPECT_FALSE(cookie_craving.IsSatisfiedBy(canonical_cookie)); // Nonced partition keys do not match. canonical_cookie = CreateCanonicalCookie( GURL(kUrlString), "name=somevalue; Secure; Partitioned", kCreationTime, kNoncedPartitionKey); cookie_craving = CreateValidCookieCraving(GURL(kUrlString), "name", "Secure; Partitioned", kCreationTime, kOtherNoncedPartitionKey); EXPECT_FALSE(cookie_craving.IsSatisfiedBy(canonical_cookie)); // Nonced partition key vs regular partition key. canonical_cookie = CreateCanonicalCookie( GURL(kUrlString), "name=somevalue; Secure; Partitioned", kCreationTime, kNoncedPartitionKey); cookie_craving = CreateValidCookieCraving(GURL(kUrlString), "name", "Secure; Partitioned", kCreationTime, kPartitionKey); EXPECT_FALSE(cookie_craving.IsSatisfiedBy(canonical_cookie)); // Regular partition key vs nonced partition key. canonical_cookie = CreateCanonicalCookie( GURL(kUrlString), "name=somevalue; Secure; Partitioned", kCreationTime, kPartitionKey); cookie_craving = CreateValidCookieCraving(GURL(kUrlString), "name", "Secure; Partitioned", kCreationTime, kNoncedPartitionKey); EXPECT_FALSE(cookie_craving.IsSatisfiedBy(canonical_cookie)); } TEST(CookieCravingTest, BasicCookieToFromProto) { // Default cookie. CookieCraving cc = CreateValidCookieCraving(GURL(kUrlString), kName, ""); proto::CookieCraving proto = cc.ToProto(); EXPECT_EQ(proto.name(), kName); EXPECT_EQ(proto.domain(), "www.example.test"); EXPECT_EQ(proto.path(), "/"); EXPECT_EQ(proto.creation_time(), kCreationTime.ToDeltaSinceWindowsEpoch().InMicroseconds()); EXPECT_FALSE(proto.secure()); EXPECT_FALSE(proto.httponly()); EXPECT_EQ(proto.same_site(), proto::CookieSameSite::COOKIE_SAME_SITE_UNSPECIFIED); EXPECT_FALSE(proto.has_serialized_partition_key()); EXPECT_EQ(proto.source_scheme(), proto::CookieSourceScheme::SECURE); EXPECT_EQ(proto.source_port(), 443); std::optional restored_cc = CookieCraving::CreateFromProto(proto); ASSERT_TRUE(restored_cc.has_value()); EXPECT_TRUE(restored_cc->IsEqualForTesting(cc)); // Non-default attributes. cc = CreateValidCookieCraving( GURL(kUrlString), kName, "Secure; HttpOnly; Path=/foo; Domain=example.test; SameSite=Lax"); proto = cc.ToProto(); EXPECT_EQ(proto.name(), kName); EXPECT_EQ(proto.domain(), ".example.test"); EXPECT_EQ(proto.path(), "/foo"); EXPECT_EQ(proto.creation_time(), kCreationTime.ToDeltaSinceWindowsEpoch().InMicroseconds()); EXPECT_TRUE(proto.secure()); EXPECT_TRUE(proto.httponly()); EXPECT_EQ(proto.same_site(), proto::CookieSameSite::LAX_MODE); EXPECT_FALSE(proto.has_serialized_partition_key()); EXPECT_EQ(proto.source_scheme(), proto::CookieSourceScheme::SECURE); EXPECT_EQ(proto.source_port(), 443); restored_cc = CookieCraving::CreateFromProto(proto); ASSERT_TRUE(restored_cc.has_value()); EXPECT_TRUE(restored_cc->IsEqualForTesting(cc)); } TEST(CookieCravingTest, PartitionedCookieToFromProto) { const CookiePartitionKey kSameSitePartitionKey = CookiePartitionKey::FromURLForTesting(GURL("https://auth.example.test")); const CookiePartitionKey kCrossSitePartitionKey = CookiePartitionKey::FromURLForTesting(GURL("https://www.other.test")); for (const CookiePartitionKey& partition_key : {kSameSitePartitionKey, kCrossSitePartitionKey}) { // Partitioned cookies must be set with Secure. The __Host- prefix is not // required. CookieCraving cc = CreateValidCookieCraving(GURL(kUrlString), kName, "Secure; Partitioned", kCreationTime, partition_key); EXPECT_EQ(cc.PartitionKey(), partition_key); base::expected serialized_partition_key = net::CookiePartitionKey::Serialize(partition_key); CHECK(serialized_partition_key.has_value()); proto::CookieCraving proto = cc.ToProto(); EXPECT_TRUE(proto.secure()); ASSERT_TRUE(proto.has_serialized_partition_key()); EXPECT_EQ(proto.serialized_partition_key().top_level_site(), serialized_partition_key->TopLevelSite()); EXPECT_EQ(proto.serialized_partition_key().has_cross_site_ancestor(), serialized_partition_key->has_cross_site_ancestor()); std::optional restored_cc = CookieCraving::CreateFromProto(proto); ASSERT_TRUE(restored_cc.has_value()); EXPECT_TRUE(restored_cc->IsEqualForTesting(cc)); } } TEST(CookieCravingTest, FailCreateFromInvalidProto) { // Empty proto. proto::CookieCraving proto; std::optional cc = CookieCraving::CreateFromProto(proto); EXPECT_FALSE(cc.has_value()); cc = CreateValidCookieCraving( GURL(kUrlString), kName, "Secure; HttpOnly; Path=/foo; Domain=example.test; SameSite=Lax"); proto = cc->ToProto(); // Missing parameters. { proto::CookieCraving p(proto); p.clear_name(); std::optional c = CookieCraving::CreateFromProto(p); EXPECT_FALSE(c.has_value()); } { proto::CookieCraving p(proto); p.clear_domain(); std::optional c = CookieCraving::CreateFromProto(p); EXPECT_FALSE(c.has_value()); } { proto::CookieCraving p(proto); p.clear_path(); std::optional c = CookieCraving::CreateFromProto(p); EXPECT_FALSE(c.has_value()); } { proto::CookieCraving p(proto); p.clear_secure(); std::optional c = CookieCraving::CreateFromProto(p); EXPECT_FALSE(c.has_value()); } { proto::CookieCraving p(proto); p.clear_httponly(); std::optional c = CookieCraving::CreateFromProto(p); EXPECT_FALSE(c.has_value()); } { proto::CookieCraving p(proto); p.clear_source_port(); std::optional c = CookieCraving::CreateFromProto(p); EXPECT_FALSE(c.has_value()); } { proto::CookieCraving p(proto); p.clear_creation_time(); std::optional c = CookieCraving::CreateFromProto(p); EXPECT_FALSE(c.has_value()); } { proto::CookieCraving p(proto); p.clear_same_site(); std::optional c = CookieCraving::CreateFromProto(p); EXPECT_FALSE(c.has_value()); } { proto::CookieCraving p(proto); p.clear_source_scheme(); std::optional c = CookieCraving::CreateFromProto(p); EXPECT_FALSE(c.has_value()); } // Malformed serialized partition key. { proto::CookieCraving p(proto); p.mutable_serialized_partition_key()->set_top_level_site(""); p.mutable_serialized_partition_key()->set_has_cross_site_ancestor(false); std::optional c = CookieCraving::CreateFromProto(p); EXPECT_FALSE(c.has_value()); } } } // namespace net::device_bound_sessions