// 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/cookies/cookie_base.h" #include #include "base/test/task_environment.h" #include "base/time/time.h" #include "net/cookies/cookie_constants.h" #include "net/cookies/cookie_partition_key.h" #include "net/test/test_with_task_environment.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "url/third_party/mozilla/url_parse.h" namespace net { namespace { // A subclass of CookieBase to allow access to its protected members. Allows // customizing the Lax-allow-unsafe threshold age. class TestCookie : public CookieBase { public: // Builder interface to allow easier creation, with default values for // unspecified fields. class Builder { public: Builder() = default; Builder& SetLaxUnsafeAge(base::TimeDelta lax_unsafe_age) { lax_unsafe_age_ = lax_unsafe_age; return *this; } Builder& SetName(const std::string& name) { name_ = name; return *this; } Builder& SetDomain(const std::string& domain) { domain_ = domain; return *this; } Builder& SetPath(const std::string& path) { path_ = path; return *this; } Builder& SetCreation(base::Time creation) { creation_ = creation; return *this; } Builder& SetSecure(bool secure) { secure_ = secure; return *this; } Builder& SetHttpOnly(bool httponly) { httponly_ = httponly; return *this; } Builder& SetSameSite(CookieSameSite same_site) { same_site_ = same_site; return *this; } Builder& SetPartitionKey(std::optional partition_key) { partition_key_ = std::move(partition_key); return *this; } Builder& SetSourceScheme(CookieSourceScheme source_scheme) { source_scheme_ = source_scheme; return *this; } Builder& SetSourcePort(int source_port) { source_port_ = source_port; return *this; } TestCookie Build() { return TestCookie( lax_unsafe_age_, name_.value_or("name"), domain_.value_or("www.example.test"), path_.value_or("/foo"), creation_.value_or(base::Time::Now()), secure_.value_or(false), httponly_.value_or(false), same_site_.value_or(CookieSameSite::UNSPECIFIED), partition_key_, source_scheme_.value_or(CookieSourceScheme::kUnset), source_port_.value_or(url::PORT_UNSPECIFIED)); } private: std::optional lax_unsafe_age_; std::optional name_; std::optional domain_; std::optional path_; std::optional creation_; std::optional secure_; std::optional httponly_; std::optional same_site_; std::optional partition_key_; std::optional source_scheme_; std::optional source_port_; }; CookieEffectiveSameSite GetEffectiveSameSiteForTesting( CookieAccessSemantics access_semantics) const { return GetEffectiveSameSite(access_semantics); } bool IsRecentlyCreatedForTesting() const { return IsRecentlyCreated(GetLaxAllowUnsafeThresholdAge()); } // CookieBase: base::TimeDelta GetLaxAllowUnsafeThresholdAge() const override { return lax_unsafe_age_.value_or( CookieBase::GetLaxAllowUnsafeThresholdAge()); } private: friend class Builder; TestCookie(std::optional lax_unsafe_age, std::string name, std::string domain, std::string path, base::Time creation, bool secure, bool httponly, CookieSameSite same_site, std::optional partition_key, CookieSourceScheme source_scheme, int source_port) : CookieBase(std::move(name), std::move(domain), std::move(path), creation, secure, httponly, same_site, std::move(partition_key), source_scheme, source_port), lax_unsafe_age_(lax_unsafe_age) {} const std::optional lax_unsafe_age_; }; class CookieBaseTest : public ::testing::Test, public WithTaskEnvironment { public: // Use MOCK_TIME to test the Lax-allow-unsafe age threshold behavior. CookieBaseTest() : WithTaskEnvironment( base::test::TaskEnvironment::TimeSource::MOCK_TIME) {} }; // TODO(crbug.com/324405105): Add tests for other CookieBase functionality. TEST_F(CookieBaseTest, GetLaxAllowUnsafeThresholdAge) { // Create a TestCookie with no override for the Lax-allow-unsafe threshold // age. This should just return the base class's value. TestCookie c = TestCookie::Builder().Build(); EXPECT_EQ(c.GetLaxAllowUnsafeThresholdAge(), base::TimeDelta::Min()); } TEST_F(CookieBaseTest, GetEffectiveSameSite) { // Cases whose behavior does not depend on cookie age relative to the // threshold. const struct { CookieSameSite same_site; CookieAccessSemantics access_semantics; CookieEffectiveSameSite expected_effective_same_site; } kCommonTestCases[] = { {CookieSameSite::UNSPECIFIED, CookieAccessSemantics::LEGACY, CookieEffectiveSameSite::NO_RESTRICTION}, {CookieSameSite::NO_RESTRICTION, CookieAccessSemantics::NONLEGACY, CookieEffectiveSameSite::NO_RESTRICTION}, {CookieSameSite::NO_RESTRICTION, CookieAccessSemantics::LEGACY, CookieEffectiveSameSite::NO_RESTRICTION}, {CookieSameSite::LAX_MODE, CookieAccessSemantics::NONLEGACY, CookieEffectiveSameSite::LAX_MODE}, {CookieSameSite::LAX_MODE, CookieAccessSemantics::LEGACY, CookieEffectiveSameSite::LAX_MODE}, {CookieSameSite::STRICT_MODE, CookieAccessSemantics::NONLEGACY, CookieEffectiveSameSite::STRICT_MODE}, {CookieSameSite::STRICT_MODE, CookieAccessSemantics::LEGACY, CookieEffectiveSameSite::STRICT_MODE}, }; for (const auto& test_case : kCommonTestCases) { TestCookie c = TestCookie::Builder() .SetLaxUnsafeAge(base::Minutes(1)) .SetSameSite(test_case.same_site) .Build(); EXPECT_EQ(c.GetLaxAllowUnsafeThresholdAge(), base::Minutes(1)); EXPECT_TRUE(c.IsRecentlyCreatedForTesting()); EXPECT_EQ(c.GetEffectiveSameSiteForTesting(test_case.access_semantics), test_case.expected_effective_same_site); // Fast forward time so the cookie is now older than the threshold. FastForwardBy(base::Minutes(5)); EXPECT_EQ(c.GetLaxAllowUnsafeThresholdAge(), base::Minutes(1)); EXPECT_FALSE(c.IsRecentlyCreatedForTesting()); EXPECT_EQ(c.GetEffectiveSameSiteForTesting(test_case.access_semantics), test_case.expected_effective_same_site); } } // Test behavior where the effective samesite depends on whether the cookie is // newer than the Lax-allow-unsafe age threshold. TEST_F(CookieBaseTest, GetEffectiveSameSiteAgeThreshold) { TestCookie c = TestCookie::Builder() .SetLaxUnsafeAge(base::Minutes(1)) .SetSameSite(CookieSameSite::UNSPECIFIED) .Build(); EXPECT_EQ(c.GetLaxAllowUnsafeThresholdAge(), base::Minutes(1)); EXPECT_TRUE(c.IsRecentlyCreatedForTesting()); EXPECT_EQ(c.GetEffectiveSameSiteForTesting(CookieAccessSemantics::NONLEGACY), CookieEffectiveSameSite::LAX_MODE_ALLOW_UNSAFE); // Fast forward time so the cookie is now older than the threshold. FastForwardBy(base::Minutes(5)); EXPECT_FALSE(c.IsRecentlyCreatedForTesting()); EXPECT_EQ(c.GetEffectiveSameSiteForTesting(CookieAccessSemantics::NONLEGACY), CookieEffectiveSameSite::LAX_MODE); } } // namespace } // namespace net