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/device_bound_sessions/cookie_craving.h"
6
7 #include "base/strings/string_util.h"
8 #include "base/unguessable_token.h"
9 #include "net/cookies/canonical_cookie.h"
10 #include "net/cookies/cookie_constants.h"
11 #include "net/cookies/cookie_partition_key.h"
12 #include "net/device_bound_sessions/proto/storage.pb.h"
13 #include "testing/gmock/include/gmock/gmock.h"
14 #include "testing/gtest/include/gtest/gtest.h"
15
16 namespace net::device_bound_sessions {
17
18 // Default values for tests.
19 constexpr char kUrlString[] = "https://www.example.test/foo";
20 constexpr char kName[] = "name";
21 const base::Time kCreationTime = base::Time::Now();
22
23 // Helper to Create() and unwrap a CookieCraving, expecting it to be valid.
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)24 CookieCraving CreateValidCookieCraving(
25 const GURL& url,
26 const std::string& name,
27 const std::string& attributes,
28 base::Time creation_time = kCreationTime,
29 std::optional<CookiePartitionKey> cookie_partition_key = std::nullopt) {
30 std::optional<CookieCraving> maybe_cc = CookieCraving::Create(
31 url, name, attributes, creation_time, cookie_partition_key);
32 EXPECT_TRUE(maybe_cc);
33 EXPECT_TRUE(maybe_cc->IsValid());
34 return std::move(*maybe_cc);
35 }
36
37 // Helper to create and unwrap a CanonicalCookie.
CreateCanonicalCookie(const GURL & url,const std::string & cookie_line,base::Time creation_time=kCreationTime,std::optional<CookiePartitionKey> cookie_partition_key=std::nullopt)38 CanonicalCookie CreateCanonicalCookie(
39 const GURL& url,
40 const std::string& cookie_line,
41 base::Time creation_time = kCreationTime,
42 std::optional<CookiePartitionKey> cookie_partition_key = std::nullopt) {
43 std::unique_ptr<CanonicalCookie> canonical_cookie =
44 CanonicalCookie::CreateForTesting(url, cookie_line, creation_time,
45 /*server_time=*/std::nullopt,
46 cookie_partition_key);
47 EXPECT_TRUE(canonical_cookie);
48 EXPECT_TRUE(canonical_cookie->IsCanonical());
49 return *canonical_cookie;
50 }
51
TEST(CookieCravingTest,CreateBasic)52 TEST(CookieCravingTest, CreateBasic) {
53 // Default cookie.
54 CookieCraving cc = CreateValidCookieCraving(GURL(kUrlString), kName, "");
55 EXPECT_EQ(cc.Name(), kName);
56 EXPECT_EQ(cc.Domain(), "www.example.test");
57 EXPECT_EQ(cc.Path(), "/");
58 EXPECT_EQ(cc.CreationDate(), kCreationTime);
59 EXPECT_FALSE(cc.SecureAttribute());
60 EXPECT_FALSE(cc.IsHttpOnly());
61 EXPECT_EQ(cc.SameSite(), CookieSameSite::UNSPECIFIED);
62 EXPECT_EQ(cc.PartitionKey(), std::nullopt);
63 EXPECT_EQ(cc.SourceScheme(), CookieSourceScheme::kSecure);
64 EXPECT_EQ(cc.SourcePort(), 443);
65
66 // Non-default attributes.
67 cc = CreateValidCookieCraving(
68 GURL(kUrlString), kName,
69 "Secure; HttpOnly; Path=/foo; Domain=example.test; SameSite=Lax");
70 EXPECT_EQ(cc.Name(), kName);
71 EXPECT_EQ(cc.Domain(), ".example.test");
72 EXPECT_EQ(cc.Path(), "/foo");
73 EXPECT_EQ(cc.CreationDate(), kCreationTime);
74 EXPECT_TRUE(cc.SecureAttribute());
75 EXPECT_TRUE(cc.IsHttpOnly());
76 EXPECT_EQ(cc.SameSite(), CookieSameSite::LAX_MODE);
77 EXPECT_EQ(cc.PartitionKey(), std::nullopt);
78 EXPECT_EQ(cc.SourceScheme(), CookieSourceScheme::kSecure);
79 EXPECT_EQ(cc.SourcePort(), 443);
80
81 // Normalize whitespace.
82 cc = CreateValidCookieCraving(
83 GURL(kUrlString), " name ",
84 " Secure;HttpOnly;Path = /foo; Domain= example.test; SameSite =Lax ");
85 EXPECT_EQ(cc.Name(), "name");
86 EXPECT_EQ(cc.Domain(), ".example.test");
87 EXPECT_EQ(cc.Path(), "/foo");
88 EXPECT_EQ(cc.CreationDate(), kCreationTime);
89 EXPECT_TRUE(cc.SecureAttribute());
90 EXPECT_TRUE(cc.IsHttpOnly());
91 EXPECT_EQ(cc.SameSite(), CookieSameSite::LAX_MODE);
92 EXPECT_EQ(cc.PartitionKey(), std::nullopt);
93 EXPECT_EQ(cc.SourceScheme(), CookieSourceScheme::kSecure);
94 EXPECT_EQ(cc.SourcePort(), 443);
95 }
96
TEST(CookieCravingTest,CreateWithPartitionKey)97 TEST(CookieCravingTest, CreateWithPartitionKey) {
98 // The site of the partition key is not checked in Create(), so these two
99 // should behave the same.
100 const CookiePartitionKey kSameSitePartitionKey =
101 CookiePartitionKey::FromURLForTesting(GURL("https://auth.example.test"));
102 const CookiePartitionKey kCrossSitePartitionKey =
103 CookiePartitionKey::FromURLForTesting(GURL("https://www.other.test"));
104 // A key with a nonce might be used for a fenced frame or anonymous iframe.
105 const CookiePartitionKey kNoncedPartitionKey =
106 CookiePartitionKey::FromURLForTesting(
107 GURL("https://www.anonymous-iframe.test"),
108 CookiePartitionKey::AncestorChainBit::kCrossSite,
109 base::UnguessableToken::Create());
110
111 for (const CookiePartitionKey& partition_key :
112 {kSameSitePartitionKey, kCrossSitePartitionKey, kNoncedPartitionKey}) {
113 // Partitioned cookies must be set with Secure. The __Host- prefix is not
114 // required.
115 CookieCraving cc =
116 CreateValidCookieCraving(GURL(kUrlString), kName, "Secure; Partitioned",
117 kCreationTime, partition_key);
118 EXPECT_TRUE(cc.SecureAttribute());
119 EXPECT_TRUE(cc.IsPartitioned());
120 EXPECT_EQ(cc.PartitionKey(), partition_key);
121 }
122
123 // If a cookie is not set with a Partitioned attribute, the partition key
124 // should be ignored and cleared (if it's a normal partition key).
125 for (const CookiePartitionKey& partition_key :
126 {kSameSitePartitionKey, kCrossSitePartitionKey}) {
127 CookieCraving cc = CreateValidCookieCraving(
128 GURL(kUrlString), kName, "Secure", kCreationTime, partition_key);
129 EXPECT_TRUE(cc.SecureAttribute());
130 EXPECT_FALSE(cc.IsPartitioned());
131 EXPECT_EQ(cc.PartitionKey(), std::nullopt);
132 }
133
134 // For nonced partition keys, the Partitioned attribute is not explicitly
135 // required in order for the cookie to be considered partitioned.
136 CookieCraving cc = CreateValidCookieCraving(
137 GURL(kUrlString), kName, "Secure", kCreationTime, kNoncedPartitionKey);
138 EXPECT_TRUE(cc.SecureAttribute());
139 EXPECT_TRUE(cc.IsPartitioned());
140 EXPECT_EQ(cc.PartitionKey(), kNoncedPartitionKey);
141
142 // The Secure attribute is also not required for a nonced partition key.
143 cc = CreateValidCookieCraving(GURL(kUrlString), kName, "", kCreationTime,
144 kNoncedPartitionKey);
145 EXPECT_FALSE(cc.SecureAttribute());
146 EXPECT_TRUE(cc.IsPartitioned());
147 EXPECT_EQ(cc.PartitionKey(), kNoncedPartitionKey);
148 }
149
TEST(CookieCravingTest,CreateWithPrefix)150 TEST(CookieCravingTest, CreateWithPrefix) {
151 // Valid __Host- cookie.
152 CookieCraving cc = CreateValidCookieCraving(GURL(kUrlString), "__Host-blah",
153 "Secure; Path=/");
154 EXPECT_EQ(cc.Domain(), "www.example.test");
155 EXPECT_EQ(cc.Path(), "/");
156 EXPECT_TRUE(cc.SecureAttribute());
157
158 // Valid __Secure- cookie.
159 cc = CreateValidCookieCraving(GURL(kUrlString), "__Secure-blah",
160 "Secure; Path=/foo; Domain=example.test");
161 EXPECT_TRUE(cc.SecureAttribute());
162 }
163
164 // Test various strange inputs that should still be valid.
TEST(CookieCravingTest,CreateStrange)165 TEST(CookieCravingTest, CreateStrange) {
166 const char* kStrangeNames[] = {
167 // Empty name is permitted.
168 "",
169 // Leading and trailing whitespace should get trimmed.
170 " name ",
171 // Internal whitespace is allowed.
172 "n a m e",
173 // Trim leading and trailing whitespace while preserving internal
174 // whitespace.
175 " n a m e ",
176 };
177 for (const char* name : kStrangeNames) {
178 CookieCraving cc = CreateValidCookieCraving(GURL(kUrlString), name, "");
179 EXPECT_EQ(cc.Name(), base::TrimWhitespaceASCII(name, base::TRIM_ALL));
180 }
181
182 const char* kStrangeAttributesLines[] = {
183 // Capitalization.
184 "SECURE; PATH=/; SAMESITE=LAX",
185 // Leading semicolon.
186 "; Secure; Path=/; SameSite=Lax",
187 // Empty except for semicolons.
188 ";;;",
189 // Extra whitespace.
190 " Secure; Path=/; SameSite=Lax ",
191 // No whitespace.
192 "Secure;Path=/;SameSite=Lax",
193 // Domain attribute with leading dot.
194 "Domain=.example.test",
195 // Different path from the URL is allowed.
196 "Path=/different",
197 // Path not beginning with '/' is allowed. (It's just ignored.)
198 "Path=noslash",
199 // Attributes with extraneous values.
200 "Secure=true; HttpOnly=yes; Partitioned=absolutely",
201 // Unknown attributes or attribute values.
202 "Fake=totally; SameSite=SuperStrict",
203 };
204 for (const char* attributes : kStrangeAttributesLines) {
205 CreateValidCookieCraving(GURL(kUrlString), kName, attributes);
206 }
207 }
208
209 // Another strange/maybe unexpected case is that Create() does not check the
210 // secureness of the URL against the cookie's Secure attribute. (This is
211 // documented in the method comment.)
TEST(CookieCravingTest,CreateSecureFromInsecureUrl)212 TEST(CookieCravingTest, CreateSecureFromInsecureUrl) {
213 CookieCraving cc =
214 CreateValidCookieCraving(GURL("http://insecure.test"), kName, "Secure");
215 EXPECT_TRUE(cc.SecureAttribute());
216 EXPECT_EQ(cc.SourceScheme(), CookieSourceScheme::kNonSecure);
217 }
218
219 // Test inputs that should result in a failure to parse the cookie line.
TEST(CookieCravingTest,CreateFailParse)220 TEST(CookieCravingTest, CreateFailParse) {
221 const struct {
222 const char* name;
223 const char* attributes;
224 } kParseFailInputs[] = {
225 // Invalid characters in name.
226 {"blah\nsomething", "Secure; Path=/"},
227 {"blah=something", "Secure; Path=/"},
228 {"blah;something", "Secure; Path=/"},
229 // Truncated lines are blocked.
230 {"name", "Secure;\n Path=/"},
231 };
232 for (const auto& input : kParseFailInputs) {
233 std::optional<CookieCraving> cc =
234 CookieCraving::Create(GURL(kUrlString), input.name, input.attributes,
235 kCreationTime, std::nullopt);
236 EXPECT_FALSE(cc);
237 }
238 }
239
240 // Test cases where the Create() params are not valid.
TEST(CookieCravingTest,CreateFailInvalidParams)241 TEST(CookieCravingTest, CreateFailInvalidParams) {
242 // Invalid URL.
243 std::optional<CookieCraving> cc =
244 CookieCraving::Create(GURL(), kName, "", kCreationTime, std::nullopt);
245 EXPECT_FALSE(cc);
246
247 // Null creation time.
248 cc = CookieCraving::Create(GURL(kUrlString), kName, "", base::Time(),
249 std::nullopt);
250 EXPECT_FALSE(cc);
251 }
252
TEST(CookieCravingTest,CreateFailBadDomain)253 TEST(CookieCravingTest, CreateFailBadDomain) {
254 // URL does not match domain.
255 std::optional<CookieCraving> cc =
256 CookieCraving::Create(GURL(kUrlString), kName, "Domain=other.test",
257 kCreationTime, std::nullopt);
258 EXPECT_FALSE(cc);
259
260 // Public suffix is not allowed to be Domain attribute.
261 cc = CookieCraving::Create(GURL(kUrlString), kName, "Domain=test",
262 kCreationTime, std::nullopt);
263 EXPECT_FALSE(cc);
264
265 // IP addresses cannot set suffixes as the Domain attribute.
266 cc = CookieCraving::Create(GURL("http://1.2.3.4"), kName, "Domain=2.3.4",
267 kCreationTime, std::nullopt);
268 EXPECT_FALSE(cc);
269 }
270
TEST(CookieCravingTest,CreateFailBadPartitioned)271 TEST(CookieCravingTest, CreateFailBadPartitioned) {
272 const CookiePartitionKey kPartitionKey =
273 CookiePartitionKey::FromURLForTesting(GURL("https://example.test"));
274
275 // Not Secure.
276 std::optional<CookieCraving> cc = CookieCraving::Create(
277 GURL(kUrlString), kName, "Partitioned", kCreationTime, kPartitionKey);
278 EXPECT_FALSE(cc);
279
280 // The URL scheme is not cryptographic.
281 cc = CookieCraving::Create(GURL("http://example.test"), kName,
282 "Secure; Partitioned", kCreationTime,
283 kPartitionKey);
284 EXPECT_FALSE(cc);
285 }
286
TEST(CookieCravingTest,CreateFailInvalidPrefix)287 TEST(CookieCravingTest, CreateFailInvalidPrefix) {
288 // __Host- with insecure URL.
289 std::optional<CookieCraving> cc =
290 CookieCraving::Create(GURL("http://insecure.test"), "__Host-blah",
291 "Secure; Path=/", kCreationTime, std::nullopt);
292 EXPECT_FALSE(cc);
293
294 // __Host- with non-Secure cookie.
295 cc = CookieCraving::Create(GURL(kUrlString), "__Host-blah", "Path=/",
296 kCreationTime, std::nullopt);
297 EXPECT_FALSE(cc);
298
299 // __Host- with Domain attribute value.
300 cc = CookieCraving::Create(GURL(kUrlString), "__Host-blah",
301 "Secure; Path=/; Domain=example.test",
302 kCreationTime, std::nullopt);
303 EXPECT_FALSE(cc);
304
305 // __Host- with non-root path.
306 cc = CookieCraving::Create(GURL(kUrlString), "__Host-blah",
307 "Secure; Path=/foo", kCreationTime, std::nullopt);
308 EXPECT_FALSE(cc);
309
310 // __Secure- with non-Secure cookie.
311 cc = CookieCraving::Create(GURL(kUrlString), "__Secure-blah", "",
312 kCreationTime, std::nullopt);
313 EXPECT_FALSE(cc);
314
315 // Prefixes are checked case-insensitively, so these CookieCravings are also
316 // invalid for not satisfying the prefix requirements.
317 // Missing Secure.
318 cc = CookieCraving::Create(GURL(kUrlString), "__host-blah", "Path=/",
319 kCreationTime, std::nullopt);
320 EXPECT_FALSE(cc);
321 // Specifies Domain.
322 cc = CookieCraving::Create(GURL(kUrlString), "__HOST-blah",
323 "Secure; Path=/; Domain=example.test",
324 kCreationTime, std::nullopt);
325 EXPECT_FALSE(cc);
326 // Missing Secure.
327 cc = CookieCraving::Create(GURL(kUrlString), "__SeCuRe-blah", "",
328 kCreationTime, std::nullopt);
329 EXPECT_FALSE(cc);
330 }
331
332 // Valid cases were tested as part of the successful Create() tests above, so
333 // this only tests the invalid cases.
TEST(CookieCravingTest,IsNotValid)334 TEST(CookieCravingTest, IsNotValid) {
335 const struct {
336 const char* name;
337 const char* domain;
338 const char* path;
339 bool secure;
340 base::Time creation = kCreationTime;
341 } kTestCases[] = {
342 // Invalid name.
343 {" name", "www.example.test", "/", true},
344 {";", "www.example.test", "/", true},
345 {"=", "www.example.test", "/", true},
346 {"na\nme", "www.example.test", "/", true},
347 // Empty domain.
348 {"name", "", "/", true},
349 // Non-canonical domain.
350 {"name", "ExAmPlE.test", "/", true},
351 // Empty path.
352 {"name", "www.example.test", "", true},
353 // Path not beginning with slash.
354 {"name", "www.example.test", "noslash", true},
355 // Invalid __Host- prefix.
356 {"__Host-name", ".example.test", "/", true},
357 {"__Host-name", "www.example.test", "/", false},
358 {"__Host-name", "www.example.test", "/foo", false},
359 // Invalid __Secure- prefix.
360 {"__Secure-name", "www.example.test", "/", false},
361 // Invalid __Host- prefix (case insensitive).
362 {"__HOST-name", ".example.test", "/", true},
363 {"__HoSt-name", "www.example.test", "/", false},
364 {"__host-name", "www.example.test", "/foo", false},
365 // Invalid __Secure- prefix (case insensitive).
366 {"__secure-name", "www.example.test", "/", false},
367 // Null creation date.
368 {"name", "www.example.test", "/", true, base::Time()},
369 };
370
371 for (const auto& test_case : kTestCases) {
372 CookieCraving cc = CookieCraving::CreateUnsafeForTesting(
373 test_case.name, test_case.domain, test_case.path, test_case.creation,
374 test_case.secure,
375 /*httponly=*/false, CookieSameSite::LAX_MODE,
376 /*partition_key=*/std::nullopt, CookieSourceScheme::kSecure, 443);
377 SCOPED_TRACE(cc.DebugString());
378 EXPECT_FALSE(cc.IsValid());
379 }
380
381 // Additionally, Partitioned requires the Secure attribute.
382 CookieCraving cc = CookieCraving::CreateUnsafeForTesting(
383 "name", "www.example.test", "/", kCreationTime, /*secure=*/false,
384 /*httponly=*/false, CookieSameSite::LAX_MODE,
385 CookiePartitionKey::FromURLForTesting(GURL("https://example.test")),
386 CookieSourceScheme::kSecure, 443);
387 EXPECT_FALSE(cc.IsValid());
388 }
389
TEST(CookieCravingTest,IsSatisfiedBy)390 TEST(CookieCravingTest, IsSatisfiedBy) {
391 // Default case with no attributes.
392 CanonicalCookie canonical_cookie =
393 CreateCanonicalCookie(GURL(kUrlString), "name=somevalue");
394 CookieCraving cookie_craving =
395 CreateValidCookieCraving(GURL(kUrlString), "name", "");
396 EXPECT_TRUE(cookie_craving.IsSatisfiedBy(canonical_cookie));
397
398 // With attributes.
399 canonical_cookie =
400 CreateCanonicalCookie(GURL(kUrlString),
401 "name=somevalue; Domain=example.test; Path=/; "
402 "Secure; HttpOnly; SameSite=Lax");
403 cookie_craving = CreateValidCookieCraving(
404 GURL(kUrlString), "name",
405 "Domain=example.test; Path=/; Secure; HttpOnly; SameSite=Lax");
406 EXPECT_TRUE(cookie_craving.IsSatisfiedBy(canonical_cookie));
407
408 // The URL may differ as long as the cookie attributes match.
409 canonical_cookie = CreateCanonicalCookie(
410 GURL(kUrlString), "name=somevalue; Domain=example.test");
411 cookie_craving = CreateValidCookieCraving(
412 GURL("https://subdomain.example.test"), "name", "Domain=example.test");
413 EXPECT_TRUE(cookie_craving.IsSatisfiedBy(canonical_cookie));
414
415 // Creation time is not required to match.
416 canonical_cookie = CreateCanonicalCookie(
417 GURL(kUrlString), "name=somevalue; Domain=example.test", kCreationTime);
418 cookie_craving =
419 CreateValidCookieCraving(GURL(kUrlString), "name", "Domain=example.test",
420 kCreationTime + base::Hours(1));
421 EXPECT_TRUE(cookie_craving.IsSatisfiedBy(canonical_cookie));
422
423 // Source scheme and port (and indeed source host) are not required to match.
424 canonical_cookie = CreateCanonicalCookie(
425 GURL(kUrlString), "name=somevalue; Domain=example.test");
426 cookie_craving =
427 CreateValidCookieCraving(GURL("http://subdomain.example.test:8080"),
428 "name", "Domain=example.test");
429 EXPECT_TRUE(cookie_craving.IsSatisfiedBy(canonical_cookie));
430 }
431
TEST(CookieCravingTest,IsNotSatisfiedBy)432 TEST(CookieCravingTest, IsNotSatisfiedBy) {
433 // Name does not match.
434 CanonicalCookie canonical_cookie =
435 CreateCanonicalCookie(GURL(kUrlString), "realname=somevalue");
436 CookieCraving cookie_craving =
437 CreateValidCookieCraving(GURL(kUrlString), "fakename", "");
438 EXPECT_FALSE(cookie_craving.IsSatisfiedBy(canonical_cookie));
439
440 // Domain does not match.
441 canonical_cookie = CreateCanonicalCookie(
442 GURL(kUrlString), "name=somevalue; Domain=example.test");
443 cookie_craving = CreateValidCookieCraving(GURL(kUrlString), "name",
444 "Domain=www.example.test");
445 EXPECT_FALSE(cookie_craving.IsSatisfiedBy(canonical_cookie));
446
447 // Host cookie vs domain cookie.
448 canonical_cookie = CreateCanonicalCookie(GURL(kUrlString), "name=somevalue");
449 cookie_craving = CreateValidCookieCraving(GURL(kUrlString), "name",
450 "Domain=www.example.test");
451 EXPECT_FALSE(cookie_craving.IsSatisfiedBy(canonical_cookie));
452
453 // Domain cookie vs host cookie.
454 canonical_cookie = CreateCanonicalCookie(
455 GURL(kUrlString), "name=somevalue; Domain=www.example.test");
456 cookie_craving = CreateValidCookieCraving(GURL(kUrlString), "name", "");
457 EXPECT_FALSE(cookie_craving.IsSatisfiedBy(canonical_cookie));
458
459 // Path does not match.
460 canonical_cookie = CreateCanonicalCookie(
461 GURL(kUrlString), "name=somevalue; Domain=example.test; Path=/");
462 cookie_craving = CreateValidCookieCraving(GURL(kUrlString), "name",
463 "Domain=example.test; Path=/foo");
464 EXPECT_FALSE(cookie_craving.IsSatisfiedBy(canonical_cookie));
465
466 // Secure vs non-Secure.
467 canonical_cookie = CreateCanonicalCookie(
468 GURL(kUrlString), "name=somevalue; Secure; Domain=example.test; Path=/");
469 cookie_craving = CreateValidCookieCraving(GURL(kUrlString), "name",
470 "Domain=example.test; Path=/foo");
471 EXPECT_FALSE(cookie_craving.IsSatisfiedBy(canonical_cookie));
472
473 // Non-Secure vs Secure.
474 canonical_cookie = CreateCanonicalCookie(
475 GURL(kUrlString), "name=somevalue; Domain=example.test; Path=/");
476 cookie_craving = CreateValidCookieCraving(
477 GURL(kUrlString), "name", "Secure; Domain=example.test; Path=/foo");
478 EXPECT_FALSE(cookie_craving.IsSatisfiedBy(canonical_cookie));
479
480 // HttpOnly vs non-HttpOnly.
481 canonical_cookie = CreateCanonicalCookie(
482 GURL(kUrlString),
483 "name=somevalue; HttpOnly; Domain=example.test; Path=/");
484 cookie_craving = CreateValidCookieCraving(GURL(kUrlString), "name",
485 "Domain=example.test; Path=/foo");
486 EXPECT_FALSE(cookie_craving.IsSatisfiedBy(canonical_cookie));
487
488 // Non-HttpOnly vs HttpOnly.
489 canonical_cookie = CreateCanonicalCookie(
490 GURL(kUrlString), "name=somevalue; Domain=example.test; Path=/");
491 cookie_craving = CreateValidCookieCraving(
492 GURL(kUrlString), "name", "HttpOnly; Domain=example.test; Path=/foo");
493 EXPECT_FALSE(cookie_craving.IsSatisfiedBy(canonical_cookie));
494
495 // SameSite does not match.
496 canonical_cookie =
497 CreateCanonicalCookie(GURL(kUrlString), "name=somevalue; SameSite=Lax");
498 cookie_craving =
499 CreateValidCookieCraving(GURL(kUrlString), "name", "SameSite=Strict");
500 EXPECT_FALSE(cookie_craving.IsSatisfiedBy(canonical_cookie));
501
502 // SameSite vs unspecified SameSite. (Note that the SameSite attribute value
503 // is compared, not the effective SameSite enforcement mode.)
504 canonical_cookie =
505 CreateCanonicalCookie(GURL(kUrlString), "name=somevalue; SameSite=Lax");
506 cookie_craving = CreateValidCookieCraving(GURL(kUrlString), "name", "");
507 EXPECT_FALSE(cookie_craving.IsSatisfiedBy(canonical_cookie));
508 }
509
TEST(CookieCravingTest,IsSatisfiedByWithPartitionKey)510 TEST(CookieCravingTest, IsSatisfiedByWithPartitionKey) {
511 const CookiePartitionKey kPartitionKey =
512 CookiePartitionKey::FromURLForTesting(GURL("https://example.test"));
513 const CookiePartitionKey kOtherPartitionKey =
514 CookiePartitionKey::FromURLForTesting(GURL("https://other.test"));
515
516 const base::UnguessableToken kNonce = base::UnguessableToken::Create();
517 const CookiePartitionKey kNoncedPartitionKey =
518 CookiePartitionKey::FromURLForTesting(
519 GURL("https://example.test"),
520 CookiePartitionKey::AncestorChainBit::kCrossSite, kNonce);
521
522 // Partition keys match.
523 CanonicalCookie canonical_cookie = CreateCanonicalCookie(
524 GURL(kUrlString), "name=somevalue; Secure; Partitioned", kCreationTime,
525 kPartitionKey);
526 CookieCraving cookie_craving =
527 CreateValidCookieCraving(GURL(kUrlString), "name", "Secure; Partitioned",
528 kCreationTime, kPartitionKey);
529 EXPECT_TRUE(cookie_craving.IsSatisfiedBy(canonical_cookie));
530
531 // Cookie line doesn't specified Partitioned so key gets cleared for both.
532 canonical_cookie = CreateCanonicalCookie(
533 GURL(kUrlString), "name=somevalue; Secure", kCreationTime, kPartitionKey);
534 cookie_craving = CreateValidCookieCraving(GURL(kUrlString), "name", "Secure",
535 kCreationTime, kOtherPartitionKey);
536 EXPECT_TRUE(cookie_craving.IsSatisfiedBy(canonical_cookie));
537
538 // Without partition key for the CookieCraving, but cookie line doesn't
539 // specify Partitioned so they are equivalent.
540 canonical_cookie = CreateCanonicalCookie(
541 GURL(kUrlString), "name=somevalue; Secure", kCreationTime, kPartitionKey);
542 cookie_craving = CreateValidCookieCraving(GURL(kUrlString), "name", "Secure");
543 EXPECT_TRUE(cookie_craving.IsSatisfiedBy(canonical_cookie));
544
545 // Without partition key for the CanonicalCookie, but cookie line doesn't
546 // specify Partitioned so they are equivalent.
547 canonical_cookie =
548 CreateCanonicalCookie(GURL(kUrlString), "name=somevalue; Secure");
549 cookie_craving = CreateValidCookieCraving(GURL(kUrlString), "name", "Secure",
550 kCreationTime, kPartitionKey);
551 EXPECT_TRUE(cookie_craving.IsSatisfiedBy(canonical_cookie));
552
553 // Identical nonced partition keys.
554 canonical_cookie =
555 CreateCanonicalCookie(GURL(kUrlString), "name=somevalue; Secure",
556 kCreationTime, kNoncedPartitionKey);
557 cookie_craving = CreateValidCookieCraving(GURL(kUrlString), "name", "Secure",
558 kCreationTime, kNoncedPartitionKey);
559 EXPECT_TRUE(cookie_craving.IsSatisfiedBy(canonical_cookie));
560 }
561
TEST(CookieCravingTest,IsNotSatisfiedByWithPartitionKey)562 TEST(CookieCravingTest, IsNotSatisfiedByWithPartitionKey) {
563 const CookiePartitionKey kPartitionKey =
564 CookiePartitionKey::FromURLForTesting(GURL("https://example.test"));
565 const CookiePartitionKey kOtherPartitionKey =
566 CookiePartitionKey::FromURLForTesting(GURL("https://other.test"));
567
568 const base::UnguessableToken kNonce = base::UnguessableToken::Create();
569 const base::UnguessableToken kOtherNonce = base::UnguessableToken::Create();
570 const CookiePartitionKey kNoncedPartitionKey =
571 CookiePartitionKey::FromURLForTesting(
572 GURL("https://example.test"),
573 CookiePartitionKey::AncestorChainBit::kCrossSite, kNonce);
574 const CookiePartitionKey kOtherNoncedPartitionKey =
575 CookiePartitionKey::FromURLForTesting(
576 GURL("https://example.test"),
577 CookiePartitionKey::AncestorChainBit::kCrossSite, kOtherNonce);
578
579 // Partition keys do not match.
580 CanonicalCookie canonical_cookie = CreateCanonicalCookie(
581 GURL(kUrlString), "name=somevalue; Secure; Partitioned", kCreationTime,
582 kPartitionKey);
583 CookieCraving cookie_craving =
584 CreateValidCookieCraving(GURL(kUrlString), "name", "Secure; Partitioned",
585 kCreationTime, kOtherPartitionKey);
586 EXPECT_FALSE(cookie_craving.IsSatisfiedBy(canonical_cookie));
587
588 // Nonced partition keys do not match.
589 canonical_cookie = CreateCanonicalCookie(
590 GURL(kUrlString), "name=somevalue; Secure; Partitioned", kCreationTime,
591 kNoncedPartitionKey);
592 cookie_craving =
593 CreateValidCookieCraving(GURL(kUrlString), "name", "Secure; Partitioned",
594 kCreationTime, kOtherNoncedPartitionKey);
595 EXPECT_FALSE(cookie_craving.IsSatisfiedBy(canonical_cookie));
596
597 // Nonced partition key vs regular partition key.
598 canonical_cookie = CreateCanonicalCookie(
599 GURL(kUrlString), "name=somevalue; Secure; Partitioned", kCreationTime,
600 kNoncedPartitionKey);
601 cookie_craving =
602 CreateValidCookieCraving(GURL(kUrlString), "name", "Secure; Partitioned",
603 kCreationTime, kPartitionKey);
604 EXPECT_FALSE(cookie_craving.IsSatisfiedBy(canonical_cookie));
605
606 // Regular partition key vs nonced partition key.
607 canonical_cookie = CreateCanonicalCookie(
608 GURL(kUrlString), "name=somevalue; Secure; Partitioned", kCreationTime,
609 kPartitionKey);
610 cookie_craving =
611 CreateValidCookieCraving(GURL(kUrlString), "name", "Secure; Partitioned",
612 kCreationTime, kNoncedPartitionKey);
613 EXPECT_FALSE(cookie_craving.IsSatisfiedBy(canonical_cookie));
614 }
615
TEST(CookieCravingTest,BasicCookieToFromProto)616 TEST(CookieCravingTest, BasicCookieToFromProto) {
617 // Default cookie.
618 CookieCraving cc = CreateValidCookieCraving(GURL(kUrlString), kName, "");
619
620 proto::CookieCraving proto = cc.ToProto();
621 EXPECT_EQ(proto.name(), kName);
622 EXPECT_EQ(proto.domain(), "www.example.test");
623 EXPECT_EQ(proto.path(), "/");
624 EXPECT_EQ(proto.creation_time(),
625 kCreationTime.ToDeltaSinceWindowsEpoch().InMicroseconds());
626 EXPECT_FALSE(proto.secure());
627 EXPECT_FALSE(proto.httponly());
628 EXPECT_EQ(proto.same_site(),
629 proto::CookieSameSite::COOKIE_SAME_SITE_UNSPECIFIED);
630 EXPECT_FALSE(proto.has_serialized_partition_key());
631 EXPECT_EQ(proto.source_scheme(), proto::CookieSourceScheme::SECURE);
632 EXPECT_EQ(proto.source_port(), 443);
633
634 std::optional<CookieCraving> restored_cc =
635 CookieCraving::CreateFromProto(proto);
636 ASSERT_TRUE(restored_cc.has_value());
637 EXPECT_TRUE(restored_cc->IsEqualForTesting(cc));
638
639 // Non-default attributes.
640 cc = CreateValidCookieCraving(
641 GURL(kUrlString), kName,
642 "Secure; HttpOnly; Path=/foo; Domain=example.test; SameSite=Lax");
643
644 proto = cc.ToProto();
645 EXPECT_EQ(proto.name(), kName);
646 EXPECT_EQ(proto.domain(), ".example.test");
647 EXPECT_EQ(proto.path(), "/foo");
648 EXPECT_EQ(proto.creation_time(),
649 kCreationTime.ToDeltaSinceWindowsEpoch().InMicroseconds());
650 EXPECT_TRUE(proto.secure());
651 EXPECT_TRUE(proto.httponly());
652 EXPECT_EQ(proto.same_site(), proto::CookieSameSite::LAX_MODE);
653 EXPECT_FALSE(proto.has_serialized_partition_key());
654 EXPECT_EQ(proto.source_scheme(), proto::CookieSourceScheme::SECURE);
655 EXPECT_EQ(proto.source_port(), 443);
656
657 restored_cc = CookieCraving::CreateFromProto(proto);
658 ASSERT_TRUE(restored_cc.has_value());
659 EXPECT_TRUE(restored_cc->IsEqualForTesting(cc));
660 }
661
TEST(CookieCravingTest,PartitionedCookieToFromProto)662 TEST(CookieCravingTest, PartitionedCookieToFromProto) {
663 const CookiePartitionKey kSameSitePartitionKey =
664 CookiePartitionKey::FromURLForTesting(GURL("https://auth.example.test"));
665 const CookiePartitionKey kCrossSitePartitionKey =
666 CookiePartitionKey::FromURLForTesting(GURL("https://www.other.test"));
667
668 for (const CookiePartitionKey& partition_key :
669 {kSameSitePartitionKey, kCrossSitePartitionKey}) {
670 // Partitioned cookies must be set with Secure. The __Host- prefix is not
671 // required.
672 CookieCraving cc =
673 CreateValidCookieCraving(GURL(kUrlString), kName, "Secure; Partitioned",
674 kCreationTime, partition_key);
675 EXPECT_EQ(cc.PartitionKey(), partition_key);
676 base::expected<net::CookiePartitionKey::SerializedCookiePartitionKey,
677 std::string>
678 serialized_partition_key =
679 net::CookiePartitionKey::Serialize(partition_key);
680 CHECK(serialized_partition_key.has_value());
681
682 proto::CookieCraving proto = cc.ToProto();
683 EXPECT_TRUE(proto.secure());
684 ASSERT_TRUE(proto.has_serialized_partition_key());
685 EXPECT_EQ(proto.serialized_partition_key().top_level_site(),
686 serialized_partition_key->TopLevelSite());
687 EXPECT_EQ(proto.serialized_partition_key().has_cross_site_ancestor(),
688 serialized_partition_key->has_cross_site_ancestor());
689
690 std::optional<CookieCraving> restored_cc =
691 CookieCraving::CreateFromProto(proto);
692 ASSERT_TRUE(restored_cc.has_value());
693 EXPECT_TRUE(restored_cc->IsEqualForTesting(cc));
694 }
695 }
696
TEST(CookieCravingTest,FailCreateFromInvalidProto)697 TEST(CookieCravingTest, FailCreateFromInvalidProto) {
698 // Empty proto.
699 proto::CookieCraving proto;
700 std::optional<CookieCraving> cc = CookieCraving::CreateFromProto(proto);
701 EXPECT_FALSE(cc.has_value());
702
703 cc = CreateValidCookieCraving(
704 GURL(kUrlString), kName,
705 "Secure; HttpOnly; Path=/foo; Domain=example.test; SameSite=Lax");
706 proto = cc->ToProto();
707
708 // Missing parameters.
709 {
710 proto::CookieCraving p(proto);
711 p.clear_name();
712 std::optional<CookieCraving> c = CookieCraving::CreateFromProto(p);
713 EXPECT_FALSE(c.has_value());
714 }
715 {
716 proto::CookieCraving p(proto);
717 p.clear_domain();
718 std::optional<CookieCraving> c = CookieCraving::CreateFromProto(p);
719 EXPECT_FALSE(c.has_value());
720 }
721 {
722 proto::CookieCraving p(proto);
723 p.clear_path();
724 std::optional<CookieCraving> c = CookieCraving::CreateFromProto(p);
725 EXPECT_FALSE(c.has_value());
726 }
727 {
728 proto::CookieCraving p(proto);
729 p.clear_secure();
730 std::optional<CookieCraving> c = CookieCraving::CreateFromProto(p);
731 EXPECT_FALSE(c.has_value());
732 }
733 {
734 proto::CookieCraving p(proto);
735 p.clear_httponly();
736 std::optional<CookieCraving> c = CookieCraving::CreateFromProto(p);
737 EXPECT_FALSE(c.has_value());
738 }
739 {
740 proto::CookieCraving p(proto);
741 p.clear_source_port();
742 std::optional<CookieCraving> c = CookieCraving::CreateFromProto(p);
743 EXPECT_FALSE(c.has_value());
744 }
745 {
746 proto::CookieCraving p(proto);
747 p.clear_creation_time();
748 std::optional<CookieCraving> c = CookieCraving::CreateFromProto(p);
749 EXPECT_FALSE(c.has_value());
750 }
751 {
752 proto::CookieCraving p(proto);
753 p.clear_same_site();
754 std::optional<CookieCraving> c = CookieCraving::CreateFromProto(p);
755 EXPECT_FALSE(c.has_value());
756 }
757 {
758 proto::CookieCraving p(proto);
759 p.clear_source_scheme();
760 std::optional<CookieCraving> c = CookieCraving::CreateFromProto(p);
761 EXPECT_FALSE(c.has_value());
762 }
763 // Malformed serialized partition key.
764 {
765 proto::CookieCraving p(proto);
766 p.mutable_serialized_partition_key()->set_top_level_site("");
767 p.mutable_serialized_partition_key()->set_has_cross_site_ancestor(false);
768 std::optional<CookieCraving> c = CookieCraving::CreateFromProto(p);
769 EXPECT_FALSE(c.has_value());
770 }
771 }
772
773 } // namespace net::device_bound_sessions
774