// 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<CookiePartitionKey> cookie_partition_key = std::nullopt) {
std::optional<CookieCraving> 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<CookiePartitionKey> cookie_partition_key = std::nullopt) {
std::unique_ptr<CanonicalCookie> 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<CookieCraving> 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<CookieCraving> 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<CookieCraving> 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<CookieCraving> 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<CookieCraving> 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<CookieCraving> 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<net::CookiePartitionKey::SerializedCookiePartitionKey,
std::string>
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<CookieCraving> 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<CookieCraving> 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<CookieCraving> c = CookieCraving::CreateFromProto(p);
EXPECT_FALSE(c.has_value());
}
{
proto::CookieCraving p(proto);
p.clear_domain();
std::optional<CookieCraving> c = CookieCraving::CreateFromProto(p);
EXPECT_FALSE(c.has_value());
}
{
proto::CookieCraving p(proto);
p.clear_path();
std::optional<CookieCraving> c = CookieCraving::CreateFromProto(p);
EXPECT_FALSE(c.has_value());
}
{
proto::CookieCraving p(proto);
p.clear_secure();
std::optional<CookieCraving> c = CookieCraving::CreateFromProto(p);
EXPECT_FALSE(c.has_value());
}
{
proto::CookieCraving p(proto);
p.clear_httponly();
std::optional<CookieCraving> c = CookieCraving::CreateFromProto(p);
EXPECT_FALSE(c.has_value());
}
{
proto::CookieCraving p(proto);
p.clear_source_port();
std::optional<CookieCraving> c = CookieCraving::CreateFromProto(p);
EXPECT_FALSE(c.has_value());
}
{
proto::CookieCraving p(proto);
p.clear_creation_time();
std::optional<CookieCraving> c = CookieCraving::CreateFromProto(p);
EXPECT_FALSE(c.has_value());
}
{
proto::CookieCraving p(proto);
p.clear_same_site();
std::optional<CookieCraving> c = CookieCraving::CreateFromProto(p);
EXPECT_FALSE(c.has_value());
}
{
proto::CookieCraving p(proto);
p.clear_source_scheme();
std::optional<CookieCraving> 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<CookieCraving> c = CookieCraving::CreateFromProto(p);
EXPECT_FALSE(c.has_value());
}
}
} // namespace net::device_bound_sessions