• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2024 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "net/cookies/cookie_base.h"
6 
7 #include <string>
8 
9 #include "base/test/task_environment.h"
10 #include "base/time/time.h"
11 #include "net/cookies/cookie_constants.h"
12 #include "net/cookies/cookie_partition_key.h"
13 #include "net/test/test_with_task_environment.h"
14 #include "testing/gmock/include/gmock/gmock.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16 #include "url/third_party/mozilla/url_parse.h"
17 
18 namespace net {
19 namespace {
20 
21 // A subclass of CookieBase to allow access to its protected members. Allows
22 // customizing the Lax-allow-unsafe threshold age.
23 class TestCookie : public CookieBase {
24  public:
25   // Builder interface to allow easier creation, with default values for
26   // unspecified fields.
27   class Builder {
28    public:
29     Builder() = default;
30 
SetLaxUnsafeAge(base::TimeDelta lax_unsafe_age)31     Builder& SetLaxUnsafeAge(base::TimeDelta lax_unsafe_age) {
32       lax_unsafe_age_ = lax_unsafe_age;
33       return *this;
34     }
35 
SetName(const std::string & name)36     Builder& SetName(const std::string& name) {
37       name_ = name;
38       return *this;
39     }
40 
SetDomain(const std::string & domain)41     Builder& SetDomain(const std::string& domain) {
42       domain_ = domain;
43       return *this;
44     }
45 
SetPath(const std::string & path)46     Builder& SetPath(const std::string& path) {
47       path_ = path;
48       return *this;
49     }
50 
SetCreation(base::Time creation)51     Builder& SetCreation(base::Time creation) {
52       creation_ = creation;
53       return *this;
54     }
55 
SetSecure(bool secure)56     Builder& SetSecure(bool secure) {
57       secure_ = secure;
58       return *this;
59     }
60 
SetHttpOnly(bool httponly)61     Builder& SetHttpOnly(bool httponly) {
62       httponly_ = httponly;
63       return *this;
64     }
65 
SetSameSite(CookieSameSite same_site)66     Builder& SetSameSite(CookieSameSite same_site) {
67       same_site_ = same_site;
68       return *this;
69     }
70 
SetPartitionKey(std::optional<CookiePartitionKey> partition_key)71     Builder& SetPartitionKey(std::optional<CookiePartitionKey> partition_key) {
72       partition_key_ = std::move(partition_key);
73       return *this;
74     }
75 
SetSourceScheme(CookieSourceScheme source_scheme)76     Builder& SetSourceScheme(CookieSourceScheme source_scheme) {
77       source_scheme_ = source_scheme;
78       return *this;
79     }
80 
SetSourcePort(int source_port)81     Builder& SetSourcePort(int source_port) {
82       source_port_ = source_port;
83       return *this;
84     }
85 
Build()86     TestCookie Build() {
87       return TestCookie(
88           lax_unsafe_age_, name_.value_or("name"),
89           domain_.value_or("www.example.test"), path_.value_or("/foo"),
90           creation_.value_or(base::Time::Now()), secure_.value_or(false),
91           httponly_.value_or(false),
92           same_site_.value_or(CookieSameSite::UNSPECIFIED), partition_key_,
93           source_scheme_.value_or(CookieSourceScheme::kUnset),
94           source_port_.value_or(url::PORT_UNSPECIFIED));
95     }
96 
97    private:
98     std::optional<base::TimeDelta> lax_unsafe_age_;
99     std::optional<std::string> name_;
100     std::optional<std::string> domain_;
101     std::optional<std::string> path_;
102     std::optional<base::Time> creation_;
103     std::optional<bool> secure_;
104     std::optional<bool> httponly_;
105     std::optional<CookieSameSite> same_site_;
106     std::optional<CookiePartitionKey> partition_key_;
107     std::optional<CookieSourceScheme> source_scheme_;
108     std::optional<int> source_port_;
109   };
110 
GetEffectiveSameSiteForTesting(CookieAccessSemantics access_semantics) const111   CookieEffectiveSameSite GetEffectiveSameSiteForTesting(
112       CookieAccessSemantics access_semantics) const {
113     return GetEffectiveSameSite(access_semantics);
114   }
115 
IsRecentlyCreatedForTesting() const116   bool IsRecentlyCreatedForTesting() const {
117     return IsRecentlyCreated(GetLaxAllowUnsafeThresholdAge());
118   }
119 
120   // CookieBase:
GetLaxAllowUnsafeThresholdAge() const121   base::TimeDelta GetLaxAllowUnsafeThresholdAge() const override {
122     return lax_unsafe_age_.value_or(
123         CookieBase::GetLaxAllowUnsafeThresholdAge());
124   }
125 
126  private:
127   friend class Builder;
128 
TestCookie(std::optional<base::TimeDelta> lax_unsafe_age,std::string name,std::string domain,std::string path,base::Time creation,bool secure,bool httponly,CookieSameSite same_site,std::optional<CookiePartitionKey> partition_key,CookieSourceScheme source_scheme,int source_port)129   TestCookie(std::optional<base::TimeDelta> lax_unsafe_age,
130              std::string name,
131              std::string domain,
132              std::string path,
133              base::Time creation,
134              bool secure,
135              bool httponly,
136              CookieSameSite same_site,
137              std::optional<CookiePartitionKey> partition_key,
138              CookieSourceScheme source_scheme,
139              int source_port)
140       : CookieBase(std::move(name),
141                    std::move(domain),
142                    std::move(path),
143                    creation,
144                    secure,
145                    httponly,
146                    same_site,
147                    std::move(partition_key),
148                    source_scheme,
149                    source_port),
150         lax_unsafe_age_(lax_unsafe_age) {}
151 
152   const std::optional<base::TimeDelta> lax_unsafe_age_;
153 };
154 
155 class CookieBaseTest : public ::testing::Test, public WithTaskEnvironment {
156  public:
157   // Use MOCK_TIME to test the Lax-allow-unsafe age threshold behavior.
CookieBaseTest()158   CookieBaseTest()
159       : WithTaskEnvironment(
160             base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
161 };
162 
163 // TODO(crbug.com/324405105): Add tests for other CookieBase functionality.
164 
TEST_F(CookieBaseTest,GetLaxAllowUnsafeThresholdAge)165 TEST_F(CookieBaseTest, GetLaxAllowUnsafeThresholdAge) {
166   // Create a TestCookie with no override for the Lax-allow-unsafe threshold
167   // age. This should just return the base class's value.
168   TestCookie c = TestCookie::Builder().Build();
169 
170   EXPECT_EQ(c.GetLaxAllowUnsafeThresholdAge(), base::TimeDelta::Min());
171 }
172 
TEST_F(CookieBaseTest,GetEffectiveSameSite)173 TEST_F(CookieBaseTest, GetEffectiveSameSite) {
174   // Cases whose behavior does not depend on cookie age relative to the
175   // threshold.
176   const struct {
177     CookieSameSite same_site;
178     CookieAccessSemantics access_semantics;
179     CookieEffectiveSameSite expected_effective_same_site;
180   } kCommonTestCases[] = {
181       {CookieSameSite::UNSPECIFIED, CookieAccessSemantics::LEGACY,
182        CookieEffectiveSameSite::NO_RESTRICTION},
183       {CookieSameSite::NO_RESTRICTION, CookieAccessSemantics::NONLEGACY,
184        CookieEffectiveSameSite::NO_RESTRICTION},
185       {CookieSameSite::NO_RESTRICTION, CookieAccessSemantics::LEGACY,
186        CookieEffectiveSameSite::NO_RESTRICTION},
187       {CookieSameSite::LAX_MODE, CookieAccessSemantics::NONLEGACY,
188        CookieEffectiveSameSite::LAX_MODE},
189       {CookieSameSite::LAX_MODE, CookieAccessSemantics::LEGACY,
190        CookieEffectiveSameSite::LAX_MODE},
191       {CookieSameSite::STRICT_MODE, CookieAccessSemantics::NONLEGACY,
192        CookieEffectiveSameSite::STRICT_MODE},
193       {CookieSameSite::STRICT_MODE, CookieAccessSemantics::LEGACY,
194        CookieEffectiveSameSite::STRICT_MODE},
195   };
196 
197   for (const auto& test_case : kCommonTestCases) {
198     TestCookie c = TestCookie::Builder()
199                        .SetLaxUnsafeAge(base::Minutes(1))
200                        .SetSameSite(test_case.same_site)
201                        .Build();
202     EXPECT_EQ(c.GetLaxAllowUnsafeThresholdAge(), base::Minutes(1));
203     EXPECT_TRUE(c.IsRecentlyCreatedForTesting());
204     EXPECT_EQ(c.GetEffectiveSameSiteForTesting(test_case.access_semantics),
205               test_case.expected_effective_same_site);
206 
207     // Fast forward time so the cookie is now older than the threshold.
208     FastForwardBy(base::Minutes(5));
209 
210     EXPECT_EQ(c.GetLaxAllowUnsafeThresholdAge(), base::Minutes(1));
211     EXPECT_FALSE(c.IsRecentlyCreatedForTesting());
212     EXPECT_EQ(c.GetEffectiveSameSiteForTesting(test_case.access_semantics),
213               test_case.expected_effective_same_site);
214   }
215 }
216 
217 // Test behavior where the effective samesite depends on whether the cookie is
218 // newer than the Lax-allow-unsafe age threshold.
TEST_F(CookieBaseTest,GetEffectiveSameSiteAgeThreshold)219 TEST_F(CookieBaseTest, GetEffectiveSameSiteAgeThreshold) {
220   TestCookie c = TestCookie::Builder()
221                      .SetLaxUnsafeAge(base::Minutes(1))
222                      .SetSameSite(CookieSameSite::UNSPECIFIED)
223                      .Build();
224 
225   EXPECT_EQ(c.GetLaxAllowUnsafeThresholdAge(), base::Minutes(1));
226   EXPECT_TRUE(c.IsRecentlyCreatedForTesting());
227   EXPECT_EQ(c.GetEffectiveSameSiteForTesting(CookieAccessSemantics::NONLEGACY),
228             CookieEffectiveSameSite::LAX_MODE_ALLOW_UNSAFE);
229 
230   // Fast forward time so the cookie is now older than the threshold.
231   FastForwardBy(base::Minutes(5));
232 
233   EXPECT_FALSE(c.IsRecentlyCreatedForTesting());
234   EXPECT_EQ(c.GetEffectiveSameSiteForTesting(CookieAccessSemantics::NONLEGACY),
235             CookieEffectiveSameSite::LAX_MODE);
236 }
237 
238 }  // namespace
239 }  // namespace net
240