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