• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 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 #ifdef UNSAFE_BUFFERS_BUILD
6 // TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
7 #pragma allow_unsafe_buffers
8 #endif
9 
10 #include "net/cookies/parsed_cookie.h"
11 
12 #include <string>
13 
14 #include "base/strings/strcat.h"
15 #include "base/test/scoped_feature_list.h"
16 #include "net/base/features.h"
17 #include "net/cookies/cookie_constants.h"
18 #include "net/cookies/cookie_inclusion_status.h"
19 #include "testing/gtest/include/gtest/gtest.h"
20 
21 namespace net {
22 
TEST(ParsedCookieTest,TestBasic)23 TEST(ParsedCookieTest, TestBasic) {
24   ParsedCookie pc1("a=b");
25   EXPECT_TRUE(pc1.IsValid());
26   EXPECT_FALSE(pc1.IsSecure());
27   EXPECT_FALSE(pc1.IsHttpOnly());
28   EXPECT_FALSE(pc1.IsPartitioned());
29   EXPECT_EQ("a", pc1.Name());
30   EXPECT_EQ("b", pc1.Value());
31   EXPECT_FALSE(pc1.HasPath());
32   EXPECT_FALSE(pc1.HasDomain());
33   EXPECT_FALSE(pc1.HasExpires());
34   EXPECT_FALSE(pc1.HasMaxAge());
35   EXPECT_EQ(CookieSameSite::UNSPECIFIED, pc1.SameSite());
36   EXPECT_EQ(CookiePriority::COOKIE_PRIORITY_DEFAULT, pc1.Priority());
37 
38   ParsedCookie pc2(
39       "c=d; secure; httponly; path=/foo; domain=bar.test; "
40       "max-age=60; samesite=lax; priority=high; partitioned;");
41   EXPECT_TRUE(pc2.IsValid());
42   EXPECT_TRUE(pc2.IsSecure());
43   EXPECT_TRUE(pc2.IsHttpOnly());
44   EXPECT_TRUE(pc2.IsPartitioned());
45   EXPECT_EQ("c", pc2.Name());
46   EXPECT_EQ("d", pc2.Value());
47   EXPECT_TRUE(pc2.HasPath());
48   EXPECT_EQ("/foo", pc2.Path());
49   EXPECT_TRUE(pc2.HasDomain());
50   EXPECT_EQ("bar.test", pc2.Domain());
51   EXPECT_FALSE(pc2.HasExpires());
52   EXPECT_TRUE(pc2.HasMaxAge());
53   EXPECT_EQ("60", pc2.MaxAge());
54   EXPECT_EQ(CookieSameSite::LAX_MODE, pc2.SameSite());
55   EXPECT_EQ(CookiePriority::COOKIE_PRIORITY_HIGH, pc2.Priority());
56 }
57 
TEST(ParsedCookieTest,TestEmpty)58 TEST(ParsedCookieTest, TestEmpty) {
59   const char* kTestCookieLines[]{"",    "     ", "=",     "=;",  " =;",
60                                  "= ;", " = ;",  ";",     " ;",  " ; ",
61                                  "\t",  "\t;",   "\t=\t", "\t=", "=\t"};
62 
63   for (const char* test : kTestCookieLines) {
64     ParsedCookie pc(test);
65     EXPECT_FALSE(pc.IsValid());
66   }
67 }
68 
TEST(ParsedCookieTest,TestSetEmptyNameValue)69 TEST(ParsedCookieTest, TestSetEmptyNameValue) {
70   CookieInclusionStatus status;
71   ParsedCookie empty("", &status);
72   EXPECT_FALSE(empty.IsValid());
73   EXPECT_TRUE(status.HasExclusionReason(
74       CookieInclusionStatus::ExclusionReason::EXCLUDE_NO_COOKIE_CONTENT));
75   EXPECT_FALSE(empty.SetValue(""));
76   EXPECT_FALSE(empty.IsValid());
77 
78   ParsedCookie empty_value("name=");
79   EXPECT_TRUE(empty_value.IsValid());
80   EXPECT_EQ("name", empty_value.Name());
81   EXPECT_FALSE(empty_value.SetName(""));
82   EXPECT_EQ("name", empty_value.Name());
83   EXPECT_TRUE(empty_value.IsValid());
84 
85   ParsedCookie empty_name("value");
86   EXPECT_TRUE(empty_name.IsValid());
87   EXPECT_EQ("value", empty_name.Value());
88   EXPECT_FALSE(empty_name.SetValue(""));
89   EXPECT_EQ("value", empty_name.Value());
90   EXPECT_TRUE(empty_name.IsValid());
91 }
92 
TEST(ParsedCookieTest,ParseValueStrings)93 TEST(ParsedCookieTest, ParseValueStrings) {
94   std::string valid_values[] = {
95       "httpONLY", "1%7C1624663551161", "<K0<r<C_<G_<S0",
96       "lastRequest=1624663552846&activeDays=%5B0%2C0", "si=8da88dce-5fee-4835"};
97   for (const auto& value : valid_values) {
98     EXPECT_EQ(ParsedCookie::ParseValueString(value), value);
99     EXPECT_TRUE(ParsedCookie::ValueMatchesParsedValue(value));
100   }
101 
102   std::string invalid_values[] = {
103       "\nhttpONLYsecure",            // Newline char at start
104       "httpONLY\nsecure",            // Newline char in middle
105       "httpONLYsecure\n",            // Newline char at end
106       "\r<K0<r<C_<G_<S0",            // Carriage return at start
107       "<K0<r\r<C_<G_<S0",            // Carriage return in middle
108       "<K0<r<C_<G_<S0\r",            // Carriage return at end
109       ";lastRequest=1624663552846",  // Token separator at start
110       "lastRequest=1624663552846; activeDays=%5B0%2C0",  // Token separator in
111                                                          // middle
112       std::string("\0abcdef", 7),                        // 0 byte at start
113       std::string("abc\0def", 7),                        // 0 byte in middle
114       std::string("abcdef\0", 7)};                       // 0 byte at end
115   for (const auto& value : invalid_values) {
116     EXPECT_NE(ParsedCookie::ParseValueString(value), value);
117     EXPECT_FALSE(ParsedCookie::ValueMatchesParsedValue(value));
118   }
119 
120   // Strings with leading whitespace should parse OK but
121   // ValueMatchesParsedValue() should fail.
122   std::string leading_whitespace_values[] = {
123       " 1%7C1624663551161",   // Space at start
124       "\t1%7C1624663551161",  // Tab at start
125   };
126   for (const auto& value : leading_whitespace_values) {
127     EXPECT_TRUE(ParsedCookie::ParseValueString(value).length() ==
128                 value.length() - 1);
129     EXPECT_FALSE(ParsedCookie::ValueMatchesParsedValue(value));
130   }
131 
132   // Strings with trailing whitespace or the separator character should parse OK
133   // but ValueMatchesParsedValue() should fail.
134   std::string valid_values_with_trailing_chars[] = {
135       "lastRequest=1624663552846 ",   // Space at end
136       "lastRequest=1624663552846\t",  // Tab at end
137       "lastRequest=1624663552846;",   // Token separator at end
138   };
139   const size_t valid_value_length =
140       valid_values_with_trailing_chars[0].length() - 1;
141   for (const auto& value : valid_values_with_trailing_chars) {
142     EXPECT_TRUE(ParsedCookie::ParseValueString(value).length() ==
143                 valid_value_length);
144     EXPECT_FALSE(ParsedCookie::ValueMatchesParsedValue(value));
145   }
146 
147   // A valid value (truncated after the ';') but parses out to a substring.
148   std::string value_with_separator_in_middle(
149       "lastRequest=1624663552846; activeDays=%5B0%2C0");
150   EXPECT_TRUE(
151       ParsedCookie::ParseValueString(value_with_separator_in_middle).length() ==
152       value_with_separator_in_middle.find(';'));
153   EXPECT_FALSE(
154       ParsedCookie::ValueMatchesParsedValue(value_with_separator_in_middle));
155 }
156 
TEST(ParsedCookieTest,TestQuoted)157 TEST(ParsedCookieTest, TestQuoted) {
158   // These are some quoting cases which the major browsers all
159   // handle differently.  I've tested Internet Explorer 6, Opera 9.6,
160   // Firefox 3, and Safari Windows 3.2.1.  We originally tried to match
161   // Firefox closely, however we now match Internet Explorer and Safari.
162   const struct {
163     const char* input;
164     const char* expected;
165   } kTests[] = {
166       // Trailing whitespace after a quoted value.  The whitespace after
167       // the quote is stripped in all browsers.
168       {"\"zzz \"  ", "\"zzz \""},
169       // Handling a quoted value with a ';', like FOO="zz;pp"  ;
170       // IE and Safari: "zz;
171       // Firefox and Opera: "zz;pp"
172       {"\"zz;pp\" ;", "\"zz"},
173       // Handling a value with multiple quoted parts, like FOO="zzz "   "ppp" ;
174       // IE and Safari: "zzz "   "ppp";
175       // Firefox: "zzz ";
176       // Opera: <rejects cookie>
177       {
178           "\"zzz \"   \"ppp\" ",
179           "\"zzz \"   \"ppp\"",
180       },
181       // A quote in a value that didn't start quoted.  like FOO=A"B ;
182       // IE, Safari, and Firefox: A"B;
183       // Opera: <rejects cookie>
184       {
185           "A\"B",
186           "A\"B",
187       }};
188 
189   for (const auto& test : kTests) {
190     ParsedCookie pc(std::string("aBc=") + test.input +
191                     " ; path=\"/\"  ; httponly ");
192     EXPECT_TRUE(pc.IsValid());
193     EXPECT_FALSE(pc.IsSecure());
194     EXPECT_TRUE(pc.IsHttpOnly());
195     EXPECT_TRUE(pc.HasPath());
196     EXPECT_EQ("aBc", pc.Name());
197     EXPECT_EQ(test.expected, pc.Value());
198 
199     EXPECT_TRUE(pc.SetValue(pc.Value()));
200     EXPECT_EQ(test.expected, pc.Value());
201 
202     // If a path was quoted, the path attribute keeps the quotes.  This will
203     // make the cookie effectively useless, but path parameters aren't
204     // supposed to be quoted.  Bug 1261605.
205     EXPECT_EQ("\"/\"", pc.Path());
206   }
207 }
208 
TEST(ParsedCookieTest,TestNameless)209 TEST(ParsedCookieTest, TestNameless) {
210   ParsedCookie pc("BLAHHH; path=/; secure;");
211   EXPECT_TRUE(pc.IsValid());
212   EXPECT_TRUE(pc.IsSecure());
213   EXPECT_TRUE(pc.HasPath());
214   EXPECT_EQ("/", pc.Path());
215   EXPECT_EQ("", pc.Name());
216   EXPECT_EQ("BLAHHH", pc.Value());
217   EXPECT_EQ(COOKIE_PRIORITY_DEFAULT, pc.Priority());
218 }
219 
TEST(ParsedCookieTest,TestAttributeCase)220 TEST(ParsedCookieTest, TestAttributeCase) {
221   ParsedCookie pc(
222       "BLAH; Path=/; sECuRe; httpONLY; sAmESitE=LaX; pRIoRitY=hIgH; "
223       "pARTitIoNeD;");
224   EXPECT_TRUE(pc.IsValid());
225   EXPECT_TRUE(pc.IsSecure());
226   EXPECT_TRUE(pc.IsHttpOnly());
227   EXPECT_TRUE(pc.IsPartitioned());
228   EXPECT_EQ(CookieSameSite::LAX_MODE, pc.SameSite());
229   EXPECT_TRUE(pc.HasPath());
230   EXPECT_EQ("/", pc.Path());
231   EXPECT_EQ("", pc.Name());
232   EXPECT_EQ("BLAH", pc.Value());
233   EXPECT_EQ(COOKIE_PRIORITY_HIGH, pc.Priority());
234   EXPECT_EQ(6U, pc.NumberOfAttributes());
235 }
236 
TEST(ParsedCookieTest,TestDoubleQuotedNameless)237 TEST(ParsedCookieTest, TestDoubleQuotedNameless) {
238   ParsedCookie pc("\"BLA\\\"HHH\"; path=/; secure;");
239   EXPECT_TRUE(pc.IsValid());
240   EXPECT_TRUE(pc.IsSecure());
241   EXPECT_TRUE(pc.HasPath());
242   EXPECT_EQ("/", pc.Path());
243   EXPECT_EQ("", pc.Name());
244   EXPECT_EQ("\"BLA\\\"HHH\"", pc.Value());
245   EXPECT_EQ(COOKIE_PRIORITY_DEFAULT, pc.Priority());
246   EXPECT_EQ(2U, pc.NumberOfAttributes());
247 }
248 
TEST(ParsedCookieTest,QuoteOffTheEnd)249 TEST(ParsedCookieTest, QuoteOffTheEnd) {
250   ParsedCookie pc("a=\"B");
251   EXPECT_TRUE(pc.IsValid());
252   EXPECT_EQ("a", pc.Name());
253   EXPECT_EQ("\"B", pc.Value());
254   EXPECT_EQ(COOKIE_PRIORITY_DEFAULT, pc.Priority());
255   EXPECT_EQ(0U, pc.NumberOfAttributes());
256 }
257 
TEST(ParsedCookieTest,MissingName)258 TEST(ParsedCookieTest, MissingName) {
259   ParsedCookie pc("=ABC");
260   EXPECT_TRUE(pc.IsValid());
261   EXPECT_EQ("", pc.Name());
262   EXPECT_EQ("ABC", pc.Value());
263   EXPECT_EQ(COOKIE_PRIORITY_DEFAULT, pc.Priority());
264   EXPECT_EQ(0U, pc.NumberOfAttributes());
265 
266   // Ensure that a preceding equal sign is emitted in the cookie line.
267 
268   // Note that this goes against what's specified in RFC6265bis and differs from
269   // how CanonicalCookie produces cookie lines. As currently written (draft 9),
270   // the spec says that a cookie with an empty name should not prepend an '='
271   // character when writing out the cookie line, but in the case where the value
272   // already contains an equal sign the cookie line will be parsed incorrectly
273   // on the receiving end. ParsedCookie.ToCookieLine is only used by the
274   // extensions API to feed modified cookies into a network request for
275   // reparsing, though, so here it's more important that the values always
276   // deserialize correctly than conform to the spec
277   ParsedCookie pc2("=ABC");
278   EXPECT_EQ("=ABC", pc2.ToCookieLine());
279   EXPECT_TRUE(pc2.SetValue("param=value"));
280   EXPECT_EQ("=param=value", pc2.ToCookieLine());
281   ParsedCookie pc3("=param=value");
282   EXPECT_EQ("", pc3.Name());
283   EXPECT_EQ("param=value", pc3.Value());
284   EXPECT_EQ("=param=value", pc3.ToCookieLine());
285 }
286 
TEST(ParsedCookieTest,MissingValue)287 TEST(ParsedCookieTest, MissingValue) {
288   ParsedCookie pc("ABC=;  path = /wee");
289   EXPECT_TRUE(pc.IsValid());
290   EXPECT_EQ("ABC", pc.Name());
291   EXPECT_EQ("", pc.Value());
292   EXPECT_TRUE(pc.HasPath());
293   EXPECT_EQ("/wee", pc.Path());
294   EXPECT_EQ(COOKIE_PRIORITY_DEFAULT, pc.Priority());
295   EXPECT_EQ(1U, pc.NumberOfAttributes());
296 
297   // Ensure that a trailing equal sign is emitted in the cookie line
298   ParsedCookie pc2("ABC=");
299   EXPECT_EQ("ABC=", pc2.ToCookieLine());
300 }
301 
TEST(ParsedCookieTest,Whitespace)302 TEST(ParsedCookieTest, Whitespace) {
303   ParsedCookie pc("  A  = BC  ;secure;;;   samesite = lax     ");
304   EXPECT_TRUE(pc.IsValid());
305   EXPECT_EQ("A", pc.Name());
306   EXPECT_EQ("BC", pc.Value());
307   EXPECT_FALSE(pc.HasPath());
308   EXPECT_FALSE(pc.HasDomain());
309   EXPECT_TRUE(pc.IsSecure());
310   EXPECT_FALSE(pc.IsHttpOnly());
311   EXPECT_EQ(CookieSameSite::LAX_MODE, pc.SameSite());
312   EXPECT_EQ(COOKIE_PRIORITY_DEFAULT, pc.Priority());
313   // We parse anything between ; as attributes, so we end up with two
314   // attributes with an empty string name and value.
315   EXPECT_EQ(4U, pc.NumberOfAttributes());
316 }
TEST(ParsedCookieTest,MultipleEquals)317 TEST(ParsedCookieTest, MultipleEquals) {
318   ParsedCookie pc("  A=== BC  ;secure;;;   httponly");
319   EXPECT_TRUE(pc.IsValid());
320   EXPECT_EQ("A", pc.Name());
321   EXPECT_EQ("== BC", pc.Value());
322   EXPECT_FALSE(pc.HasPath());
323   EXPECT_FALSE(pc.HasDomain());
324   EXPECT_TRUE(pc.IsSecure());
325   EXPECT_TRUE(pc.IsHttpOnly());
326   EXPECT_EQ(CookieSameSite::UNSPECIFIED, pc.SameSite());
327   EXPECT_EQ(COOKIE_PRIORITY_DEFAULT, pc.Priority());
328   EXPECT_EQ(4U, pc.NumberOfAttributes());
329 }
330 
TEST(ParsedCookieTest,QuotedTrailingWhitespace)331 TEST(ParsedCookieTest, QuotedTrailingWhitespace) {
332   ParsedCookie pc(
333       "ANCUUID=\"zohNumRKgI0oxyhSsV3Z7D\"  ; "
334       "expires=Sun, 18-Apr-2027 21:06:29 GMT ; "
335       "path=/  ;  ");
336   EXPECT_TRUE(pc.IsValid());
337   EXPECT_EQ("ANCUUID", pc.Name());
338   // Stripping whitespace after the quotes matches all other major browsers.
339   EXPECT_EQ("\"zohNumRKgI0oxyhSsV3Z7D\"", pc.Value());
340   EXPECT_TRUE(pc.HasExpires());
341   EXPECT_TRUE(pc.HasPath());
342   EXPECT_EQ("/", pc.Path());
343   EXPECT_EQ(COOKIE_PRIORITY_DEFAULT, pc.Priority());
344   EXPECT_EQ(2U, pc.NumberOfAttributes());
345 }
346 
TEST(ParsedCookieTest,TrailingWhitespace)347 TEST(ParsedCookieTest, TrailingWhitespace) {
348   ParsedCookie pc(
349       "ANCUUID=zohNumRKgI0oxyhSsV3Z7D  ; "
350       "expires=Sun, 18-Apr-2027 21:06:29 GMT ; "
351       "path=/  ;  ");
352   EXPECT_TRUE(pc.IsValid());
353   EXPECT_EQ("ANCUUID", pc.Name());
354   EXPECT_EQ("zohNumRKgI0oxyhSsV3Z7D", pc.Value());
355   EXPECT_TRUE(pc.HasExpires());
356   EXPECT_TRUE(pc.HasPath());
357   EXPECT_EQ("/", pc.Path());
358   EXPECT_EQ(COOKIE_PRIORITY_DEFAULT, pc.Priority());
359   EXPECT_EQ(2U, pc.NumberOfAttributes());
360 }
361 
TEST(ParsedCookieTest,LotsOfPairs)362 TEST(ParsedCookieTest, LotsOfPairs) {
363   for (int i = 1; i < 100; i++) {
364     std::string blankpairs;
365     blankpairs.resize(i, ';');
366 
367     ParsedCookie c("a=b;" + blankpairs + "secure");
368     EXPECT_EQ("a", c.Name());
369     EXPECT_EQ("b", c.Value());
370     EXPECT_TRUE(c.IsValid());
371     EXPECT_TRUE(c.IsSecure());
372   }
373 }
374 
TEST(ParsedCookieTest,EnforceSizeConstraints)375 TEST(ParsedCookieTest, EnforceSizeConstraints) {
376   CookieInclusionStatus status;
377 
378   // Create maximum size and one-less-than-maximum size name and value
379   // strings for testing.
380   std::string max_name(ParsedCookie::kMaxCookieNamePlusValueSize, 'a');
381   std::string max_value(ParsedCookie::kMaxCookieNamePlusValueSize, 'b');
382   std::string almost_max_name = max_name.substr(1, std::string::npos);
383   std::string almost_max_value = max_value.substr(1, std::string::npos);
384 
385   // Test name + value size limits enforced by the constructor.
386   ParsedCookie pc1(max_name + "=");
387   EXPECT_TRUE(pc1.IsValid());
388   EXPECT_EQ(max_name, pc1.Name());
389 
390   ParsedCookie pc2(max_name + "=; path=/foo;");
391   EXPECT_TRUE(pc2.IsValid());
392   EXPECT_EQ(max_name, pc2.Name());
393 
394   ParsedCookie pc3(max_name + "X=", &status);
395   EXPECT_FALSE(pc3.IsValid());
396   EXPECT_TRUE(status.HasOnlyExclusionReason(
397       CookieInclusionStatus::ExclusionReason::
398           EXCLUDE_NAME_VALUE_PAIR_EXCEEDS_MAX_SIZE));
399 
400   ParsedCookie pc4("=" + max_value);
401   EXPECT_TRUE(pc4.IsValid());
402   EXPECT_EQ(max_value, pc4.Value());
403 
404   ParsedCookie pc5("=" + max_value + "; path=/foo;");
405   EXPECT_TRUE(pc5.IsValid());
406   EXPECT_EQ(max_value, pc5.Value());
407 
408   ParsedCookie pc6("=" + max_value + "X", &status);
409   EXPECT_FALSE(pc6.IsValid());
410   EXPECT_TRUE(status.HasOnlyExclusionReason(
411       CookieInclusionStatus::ExclusionReason::
412           EXCLUDE_NAME_VALUE_PAIR_EXCEEDS_MAX_SIZE));
413 
414   ParsedCookie pc7(almost_max_name + "=x");
415   EXPECT_TRUE(pc7.IsValid());
416   EXPECT_EQ(almost_max_name, pc7.Name());
417   EXPECT_EQ("x", pc7.Value());
418 
419   ParsedCookie pc8(almost_max_name + "=x; path=/foo;");
420   EXPECT_TRUE(pc8.IsValid());
421   EXPECT_EQ(almost_max_name, pc8.Name());
422   EXPECT_EQ("x", pc8.Value());
423 
424   ParsedCookie pc9(almost_max_name + "=xX", &status);
425   EXPECT_FALSE(pc9.IsValid());
426   EXPECT_TRUE(status.HasOnlyExclusionReason(
427       CookieInclusionStatus::ExclusionReason::
428           EXCLUDE_NAME_VALUE_PAIR_EXCEEDS_MAX_SIZE));
429 
430   ParsedCookie pc10("x=" + almost_max_value);
431   EXPECT_TRUE(pc10.IsValid());
432   EXPECT_EQ("x", pc10.Name());
433   EXPECT_EQ(almost_max_value, pc10.Value());
434 
435   ParsedCookie pc11("x=" + almost_max_value + "; path=/foo;");
436   EXPECT_TRUE(pc11.IsValid());
437   EXPECT_EQ("x", pc11.Name());
438   EXPECT_EQ(almost_max_value, pc11.Value());
439 
440   ParsedCookie pc12("xX=" + almost_max_value, &status);
441   EXPECT_FALSE(pc12.IsValid());
442   EXPECT_TRUE(status.HasOnlyExclusionReason(
443       CookieInclusionStatus::ExclusionReason::
444           EXCLUDE_NAME_VALUE_PAIR_EXCEEDS_MAX_SIZE));
445 
446   // Test attribute value size limits enforced by the constructor.
447   std::string almost_max_path(ParsedCookie::kMaxCookieAttributeValueSize - 1,
448                               'c');
449   std::string max_path = "/" + almost_max_path;
450   std::string too_long_path = "/X" + almost_max_path;
451 
452   ParsedCookie pc20("name=value; path=" + max_path);
453   EXPECT_TRUE(pc20.IsValid());
454   EXPECT_TRUE(pc20.HasPath());
455   EXPECT_EQ("/" + almost_max_path, pc20.Path());
456 
457   ParsedCookie pc21("name=value; path=" + too_long_path, &status);
458   EXPECT_TRUE(pc21.IsValid());
459   EXPECT_FALSE(pc21.HasPath());
460   EXPECT_TRUE(status.HasWarningReason(
461       CookieInclusionStatus::WARN_ATTRIBUTE_VALUE_EXCEEDS_MAX_SIZE));
462 
463   // NOTE: max_domain is based on the max attribute value as defined in
464   // RFC6525bis, but this is larger than what is recommended by RFC1123.
465   // In theory some browsers could restrict domains to that smaller size,
466   // but ParsedCookie doesn't.
467   std::string max_domain(ParsedCookie::kMaxCookieAttributeValueSize, 'd');
468   max_domain.replace(ParsedCookie::kMaxCookieAttributeValueSize - 4, 4, ".com");
469   std::string too_long_domain = "x" + max_domain;
470 
471   ParsedCookie pc30("name=value; domain=" + max_domain);
472   EXPECT_TRUE(pc30.IsValid());
473   EXPECT_TRUE(pc30.HasDomain());
474   EXPECT_EQ(max_domain, pc30.Domain());
475 
476   ParsedCookie pc31("name=value; domain=" + too_long_domain);
477   EXPECT_TRUE(pc31.IsValid());
478   EXPECT_FALSE(pc31.HasDomain());
479   EXPECT_TRUE(status.HasWarningReason(
480       CookieInclusionStatus::WARN_ATTRIBUTE_VALUE_EXCEEDS_MAX_SIZE));
481 
482   std::string pc40_suffix = "; domain=example.com";
483 
484   ParsedCookie pc40("a=b" + pc40_suffix);
485   EXPECT_TRUE(pc40.IsValid());
486 
487   // Test name + value size limits enforced by SetName / SetValue
488   EXPECT_FALSE(pc40.SetName(max_name));
489   EXPECT_EQ("a=b" + pc40_suffix, pc40.ToCookieLine());
490   EXPECT_TRUE(pc40.IsValid());
491 
492   EXPECT_FALSE(pc40.SetValue(max_value));
493   EXPECT_EQ("a=b" + pc40_suffix, pc40.ToCookieLine());
494   EXPECT_TRUE(pc40.IsValid());
495 
496   EXPECT_TRUE(pc40.SetName(almost_max_name));
497   EXPECT_EQ(almost_max_name + "=b" + pc40_suffix, pc40.ToCookieLine());
498   EXPECT_TRUE(pc40.IsValid());
499 
500   EXPECT_FALSE(pc40.SetValue("xX"));
501   EXPECT_EQ(almost_max_name + "=b" + pc40_suffix, pc40.ToCookieLine());
502   EXPECT_TRUE(pc40.IsValid());
503 
504   EXPECT_TRUE(pc40.SetName("a"));
505   EXPECT_TRUE(pc40.SetValue(almost_max_value));
506   EXPECT_EQ("a=" + almost_max_value + pc40_suffix, pc40.ToCookieLine());
507   EXPECT_TRUE(pc40.IsValid());
508 
509   EXPECT_FALSE(pc40.SetName("xX"));
510   EXPECT_EQ("a=" + almost_max_value + pc40_suffix, pc40.ToCookieLine());
511   EXPECT_TRUE(pc40.IsValid());
512 
513   std::string lots_of_spaces(ParsedCookie::kMaxCookieNamePlusValueSize, ' ');
514   std::string test_str = "test";
515   std::string padded_test_str = lots_of_spaces + test_str + lots_of_spaces;
516 
517   // Ensure that leading/trailing whitespace gets stripped before the length
518   // calculations are enforced.
519   ParsedCookie pc41("name=value");
520   EXPECT_TRUE(pc41.SetName(padded_test_str));
521   EXPECT_TRUE(pc41.SetValue(padded_test_str));
522   EXPECT_EQ(test_str, pc41.Name());
523   EXPECT_EQ(test_str, pc41.Value());
524 
525   std::string name_equals_value = "name=value";
526   ParsedCookie pc50(name_equals_value);
527 
528   EXPECT_TRUE(pc50.SetPath(max_path));
529   EXPECT_EQ(pc50.Path(), max_path);
530   EXPECT_EQ(name_equals_value + "; path=" + max_path, pc50.ToCookieLine());
531   EXPECT_TRUE(pc50.IsValid());
532 
533   // Test attribute value size limits enforced by SetPath
534   EXPECT_FALSE(pc50.SetPath(too_long_path));
535   EXPECT_EQ(pc50.Path(), max_path);
536   EXPECT_EQ(name_equals_value + "; path=" + max_path, pc50.ToCookieLine());
537   EXPECT_TRUE(pc50.IsValid());
538 
539   std::string test_path = "/test";
540   std::string padded_test_path = lots_of_spaces + test_path + lots_of_spaces;
541 
542   EXPECT_TRUE(pc50.SetPath(padded_test_path));
543   EXPECT_EQ(test_path, pc50.Path());
544 
545   ParsedCookie pc51(name_equals_value);
546 
547   EXPECT_TRUE(pc51.SetDomain(max_domain));
548   EXPECT_EQ(pc51.Domain(), max_domain);
549   EXPECT_EQ(name_equals_value + "; domain=" + max_domain, pc51.ToCookieLine());
550   EXPECT_TRUE(pc51.IsValid());
551 
552   // Test attribute value size limits enforced by SetDomain
553   EXPECT_FALSE(pc51.SetDomain(too_long_domain));
554   EXPECT_EQ(pc51.Domain(), max_domain);
555   EXPECT_EQ(name_equals_value + "; domain=" + max_domain, pc51.ToCookieLine());
556   EXPECT_TRUE(pc51.IsValid());
557 
558   std::string test_domain = "example.com";
559   std::string padded_test_domain =
560       lots_of_spaces + test_domain + lots_of_spaces;
561 
562   EXPECT_TRUE(pc51.SetDomain(padded_test_domain));
563   EXPECT_EQ(test_domain, pc51.Domain());
564 }
565 
TEST(ParsedCookieTest,EmbeddedTerminator)566 TEST(ParsedCookieTest, EmbeddedTerminator) {
567   using std::string_literals::operator""s;
568 
569   CookieInclusionStatus status1;
570   CookieInclusionStatus status2;
571   CookieInclusionStatus status3;
572   ParsedCookie pc1("AAA=BB\0ZYX"s, &status1);
573   ParsedCookie pc2("AAA=BB\rZYX"s, &status2);
574   ParsedCookie pc3("AAA=BB\nZYX"s, &status3);
575 
576   EXPECT_FALSE(pc1.IsValid());
577   EXPECT_FALSE(pc2.IsValid());
578   EXPECT_FALSE(pc3.IsValid());
579   EXPECT_TRUE(status1.HasOnlyExclusionReason(
580       CookieInclusionStatus::ExclusionReason::EXCLUDE_DISALLOWED_CHARACTER));
581   EXPECT_TRUE(status2.HasOnlyExclusionReason(
582       CookieInclusionStatus::ExclusionReason::EXCLUDE_DISALLOWED_CHARACTER));
583   EXPECT_TRUE(status3.HasOnlyExclusionReason(
584       CookieInclusionStatus::ExclusionReason::EXCLUDE_DISALLOWED_CHARACTER));
585 }
586 
TEST(ParsedCookieTest,ParseTokensAndValues)587 TEST(ParsedCookieTest, ParseTokensAndValues) {
588   EXPECT_EQ("hello", ParsedCookie::ParseTokenString("hello\nworld"));
589   EXPECT_EQ("fs!!@", ParsedCookie::ParseTokenString("fs!!@;helloworld"));
590   EXPECT_EQ("hello world\tgood",
591             ParsedCookie::ParseTokenString("hello world\tgood\rbye"));
592   EXPECT_EQ("A", ParsedCookie::ParseTokenString("A=B=C;D=E"));
593   EXPECT_EQ("hello", ParsedCookie::ParseValueString("hello\nworld"));
594   EXPECT_EQ("fs!!@", ParsedCookie::ParseValueString("fs!!@;helloworld"));
595   EXPECT_EQ("hello world\tgood",
596             ParsedCookie::ParseValueString("hello world\tgood\rbye"));
597   EXPECT_EQ("A=B=C", ParsedCookie::ParseValueString("A=B=C;D=E"));
598 }
599 
TEST(ParsedCookieTest,SerializeCookieLine)600 TEST(ParsedCookieTest, SerializeCookieLine) {
601   const char input[] =
602       "ANCUUID=zohNumRKgI0oxyhSsV3Z7D  ; "
603       "expires=Sun, 18-Apr-2027 21:06:29 GMT ; "
604       "path=/  ;  priority=low  ;  ";
605   const char output[] =
606       "ANCUUID=zohNumRKgI0oxyhSsV3Z7D; "
607       "expires=Sun, 18-Apr-2027 21:06:29 GMT; "
608       "path=/; priority=low";
609   ParsedCookie pc(input);
610   EXPECT_EQ(output, pc.ToCookieLine());
611 }
612 
TEST(ParsedCookieTest,SetNameAndValue)613 TEST(ParsedCookieTest, SetNameAndValue) {
614   ParsedCookie cookie("a=b");
615   EXPECT_TRUE(cookie.IsValid());
616   EXPECT_TRUE(cookie.SetDomain("foobar.com"));
617   EXPECT_TRUE(cookie.SetName("name"));
618   EXPECT_TRUE(cookie.SetValue("value"));
619   EXPECT_EQ("name=value; domain=foobar.com", cookie.ToCookieLine());
620   EXPECT_TRUE(cookie.IsValid());
621 
622   ParsedCookie pc("name=value");
623   EXPECT_TRUE(pc.IsValid());
624 
625   // Set invalid name / value.
626   EXPECT_FALSE(pc.SetName("foo\nbar"));
627   EXPECT_EQ("name=value", pc.ToCookieLine());
628   EXPECT_TRUE(pc.IsValid());
629 
630   EXPECT_FALSE(pc.SetName("foo\rbar"));
631   EXPECT_EQ("name=value", pc.ToCookieLine());
632   EXPECT_TRUE(pc.IsValid());
633 
634   EXPECT_FALSE(pc.SetValue(std::string("foo\0bar", 7)));
635   EXPECT_EQ("name=value", pc.ToCookieLine());
636   EXPECT_TRUE(pc.IsValid());
637 
638   // Set previously invalid name / value.
639   EXPECT_TRUE(pc.SetName("@foobar"));
640   EXPECT_EQ("@foobar=value", pc.ToCookieLine());
641   EXPECT_TRUE(pc.IsValid());
642 
643   EXPECT_TRUE(pc.SetName("foo bar"));
644   EXPECT_EQ("foo bar=value", pc.ToCookieLine());
645   EXPECT_TRUE(pc.IsValid());
646 
647   EXPECT_TRUE(pc.SetName("\"foobar"));
648   EXPECT_EQ("\"foobar=value", pc.ToCookieLine());
649   EXPECT_TRUE(pc.IsValid());
650 
651   EXPECT_TRUE(pc.SetValue("foo bar"));
652   EXPECT_EQ("\"foobar=foo bar", pc.ToCookieLine());
653   EXPECT_TRUE(pc.IsValid());
654 
655   EXPECT_TRUE(pc.SetValue("\"foobar"));
656   EXPECT_EQ("\"foobar=\"foobar", pc.ToCookieLine());
657   EXPECT_TRUE(pc.IsValid());
658 
659   EXPECT_TRUE(pc.SetName("  foo bar  "));
660   EXPECT_EQ("foo bar=\"foobar", pc.ToCookieLine());
661   EXPECT_TRUE(pc.IsValid());
662 
663   EXPECT_TRUE(pc.SetValue("  foo bar  "));
664   EXPECT_EQ("foo bar=foo bar", pc.ToCookieLine());
665   EXPECT_TRUE(pc.IsValid());
666 
667   // Set valid name / value.
668   EXPECT_TRUE(pc.SetValue("value"));
669   EXPECT_TRUE(pc.SetName(std::string()));
670   EXPECT_EQ("=value", pc.ToCookieLine());
671   EXPECT_TRUE(pc.IsValid());
672 
673   EXPECT_TRUE(pc.SetName("test"));
674   EXPECT_EQ("test=value", pc.ToCookieLine());
675   EXPECT_TRUE(pc.IsValid());
676 
677   EXPECT_TRUE(pc.SetValue("\"foobar\""));
678   EXPECT_EQ("test=\"foobar\"", pc.ToCookieLine());
679   EXPECT_TRUE(pc.IsValid());
680 
681   EXPECT_TRUE(pc.SetValue(std::string()));
682   EXPECT_EQ("test=", pc.ToCookieLine());
683   EXPECT_TRUE(pc.IsValid());
684 
685   // Ensure that failure occurs when trying to set a name containing '='.
686   EXPECT_FALSE(pc.SetName("invalid=name"));
687   EXPECT_EQ("test=", pc.ToCookieLine());
688   EXPECT_TRUE(pc.IsValid());
689 
690   // Ensure that trying to set a name containing ';' fails.
691   EXPECT_FALSE(pc.SetName("invalid;name"));
692   EXPECT_EQ("test=", pc.ToCookieLine());
693   EXPECT_TRUE(pc.IsValid());
694 
695   EXPECT_FALSE(pc.SetValue("invalid;value"));
696   EXPECT_EQ("test=", pc.ToCookieLine());
697   EXPECT_TRUE(pc.IsValid());
698 
699   // Ensure tab characters are treated as control characters.
700   // TODO(crbug.com/40191620) Update this such that tab characters are allowed
701   // and are handled correctly.
702   EXPECT_FALSE(pc.SetName("\tinvalid\t"));
703   EXPECT_EQ("test=", pc.ToCookieLine());
704   EXPECT_TRUE(pc.IsValid());
705 
706   EXPECT_FALSE(pc.SetValue("\tinvalid\t"));
707   EXPECT_EQ("test=", pc.ToCookieLine());
708   EXPECT_TRUE(pc.IsValid());
709 
710   EXPECT_FALSE(pc.SetName("na\tme"));
711   EXPECT_EQ("test=", pc.ToCookieLine());
712   EXPECT_TRUE(pc.IsValid());
713 
714   EXPECT_FALSE(pc.SetValue("val\tue"));
715   EXPECT_EQ("test=", pc.ToCookieLine());
716   EXPECT_TRUE(pc.IsValid());
717 }
718 
TEST(ParsedCookieTest,SetAttributes)719 TEST(ParsedCookieTest, SetAttributes) {
720   ParsedCookie pc("name=value");
721   EXPECT_TRUE(pc.IsValid());
722 
723   // Clear an unset attribute.
724   EXPECT_TRUE(pc.SetDomain(std::string()));
725   EXPECT_FALSE(pc.HasDomain());
726   EXPECT_EQ("name=value", pc.ToCookieLine());
727   EXPECT_TRUE(pc.IsValid());
728 
729   // Set a string containing an invalid character
730   EXPECT_FALSE(pc.SetDomain("foo;bar"));
731   EXPECT_FALSE(pc.HasDomain());
732   EXPECT_EQ("name=value", pc.ToCookieLine());
733   EXPECT_TRUE(pc.IsValid());
734 
735   // Set all other attributes and check that they are appended in order.
736   EXPECT_TRUE(pc.SetDomain("domain.com"));
737   EXPECT_TRUE(pc.SetPath("/"));
738   EXPECT_TRUE(pc.SetExpires("Sun, 18-Apr-2027 21:06:29 GMT"));
739   EXPECT_TRUE(pc.SetMaxAge("12345"));
740   EXPECT_TRUE(pc.SetIsSecure(true));
741   EXPECT_TRUE(pc.SetIsHttpOnly(true));
742   EXPECT_TRUE(pc.SetIsHttpOnly(true));
743   EXPECT_TRUE(pc.SetSameSite("LAX"));
744   EXPECT_TRUE(pc.SetPriority("HIGH"));
745   EXPECT_TRUE(pc.SetIsPartitioned(true));
746   EXPECT_EQ(
747       "name=value; domain=domain.com; path=/; "
748       "expires=Sun, 18-Apr-2027 21:06:29 GMT; max-age=12345; secure; "
749       "httponly; samesite=LAX; priority=HIGH; partitioned",
750       pc.ToCookieLine());
751   EXPECT_TRUE(pc.HasDomain());
752   EXPECT_TRUE(pc.HasPath());
753   EXPECT_TRUE(pc.HasExpires());
754   EXPECT_TRUE(pc.HasMaxAge());
755   EXPECT_TRUE(pc.IsSecure());
756   EXPECT_TRUE(pc.IsHttpOnly());
757   EXPECT_EQ(CookieSameSite::LAX_MODE, pc.SameSite());
758   EXPECT_EQ(COOKIE_PRIORITY_HIGH, pc.Priority());
759 
760   // Modify one attribute in the middle.
761   EXPECT_TRUE(pc.SetPath("/foo"));
762   EXPECT_TRUE(pc.HasDomain());
763   EXPECT_TRUE(pc.HasPath());
764   EXPECT_EQ("/foo", pc.Path());
765   EXPECT_TRUE(pc.HasExpires());
766   EXPECT_TRUE(pc.IsSecure());
767   EXPECT_TRUE(pc.IsHttpOnly());
768   EXPECT_EQ(
769       "name=value; domain=domain.com; path=/foo; "
770       "expires=Sun, 18-Apr-2027 21:06:29 GMT; max-age=12345; secure; "
771       "httponly; samesite=LAX; priority=HIGH; partitioned",
772       pc.ToCookieLine());
773 
774   // Set priority to medium.
775   EXPECT_TRUE(pc.SetPriority("medium"));
776   EXPECT_EQ(CookiePriority::COOKIE_PRIORITY_MEDIUM, pc.Priority());
777   EXPECT_EQ(
778       "name=value; domain=domain.com; path=/foo; "
779       "expires=Sun, 18-Apr-2027 21:06:29 GMT; max-age=12345; secure; "
780       "httponly; samesite=LAX; priority=medium; partitioned",
781       pc.ToCookieLine());
782 
783   // Clear attribute from the end.
784   EXPECT_TRUE(pc.SetIsPartitioned(false));
785   EXPECT_FALSE(pc.IsPartitioned());
786   EXPECT_EQ(
787       "name=value; domain=domain.com; path=/foo; "
788       "expires=Sun, 18-Apr-2027 21:06:29 GMT; max-age=12345; secure; "
789       "httponly; samesite=LAX; priority=medium",
790       pc.ToCookieLine());
791 
792   // Clear the rest and change the name and value.
793   EXPECT_TRUE(pc.SetDomain(std::string()));
794   EXPECT_TRUE(pc.SetPath(std::string()));
795   EXPECT_TRUE(pc.SetExpires(std::string()));
796   EXPECT_TRUE(pc.SetMaxAge(std::string()));
797   EXPECT_TRUE(pc.SetIsSecure(false));
798   EXPECT_TRUE(pc.SetIsHttpOnly(false));
799   EXPECT_TRUE(pc.SetSameSite(std::string()));
800   EXPECT_TRUE(pc.SetName("name2"));
801   EXPECT_TRUE(pc.SetValue("value2"));
802   EXPECT_TRUE(pc.SetPriority(std::string()));
803   EXPECT_FALSE(pc.HasDomain());
804   EXPECT_FALSE(pc.HasPath());
805   EXPECT_FALSE(pc.HasExpires());
806   EXPECT_FALSE(pc.HasMaxAge());
807   EXPECT_FALSE(pc.IsSecure());
808   EXPECT_FALSE(pc.IsHttpOnly());
809   EXPECT_EQ(CookieSameSite::UNSPECIFIED, pc.SameSite());
810   EXPECT_TRUE(pc.SetIsPartitioned(false));
811   EXPECT_EQ("name2=value2", pc.ToCookieLine());
812   EXPECT_FALSE(pc.IsPartitioned());
813 }
814 
815 // Setting the domain attribute to the empty string should be valid.
TEST(ParsedCookieTest,EmptyDomainAttributeValid)816 TEST(ParsedCookieTest, EmptyDomainAttributeValid) {
817   ParsedCookie pc("name=value; domain=");
818   EXPECT_TRUE(pc.IsValid());
819 }
820 
821 // Set the domain attribute twice in a cookie line. If the second attribute's
822 // value is empty, it should equal the empty string.
TEST(ParsedCookieTest,MultipleDomainAttributes)823 TEST(ParsedCookieTest, MultipleDomainAttributes) {
824   ParsedCookie pc1("name=value; domain=foo.com; domain=bar.com");
825   EXPECT_EQ("bar.com", pc1.Domain());
826   ParsedCookie pc2("name=value; domain=foo.com; domain=");
827   EXPECT_EQ(std::string(), pc2.Domain());
828 }
829 
TEST(ParsedCookieTest,SetPriority)830 TEST(ParsedCookieTest, SetPriority) {
831   ParsedCookie pc("name=value");
832   EXPECT_TRUE(pc.IsValid());
833 
834   EXPECT_EQ("name=value", pc.ToCookieLine());
835   EXPECT_EQ(COOKIE_PRIORITY_DEFAULT, pc.Priority());
836 
837   // Test each priority, expect case-insensitive compare.
838   EXPECT_TRUE(pc.SetPriority("high"));
839   EXPECT_EQ("name=value; priority=high", pc.ToCookieLine());
840   EXPECT_EQ(COOKIE_PRIORITY_HIGH, pc.Priority());
841 
842   EXPECT_TRUE(pc.SetPriority("mEDium"));
843   EXPECT_EQ("name=value; priority=mEDium", pc.ToCookieLine());
844   EXPECT_EQ(COOKIE_PRIORITY_MEDIUM, pc.Priority());
845 
846   EXPECT_TRUE(pc.SetPriority("LOW"));
847   EXPECT_EQ("name=value; priority=LOW", pc.ToCookieLine());
848   EXPECT_EQ(COOKIE_PRIORITY_LOW, pc.Priority());
849 
850   // Interpret invalid priority values as COOKIE_PRIORITY_DEFAULT.
851   EXPECT_TRUE(pc.SetPriority("Blah"));
852   EXPECT_EQ("name=value; priority=Blah", pc.ToCookieLine());
853   EXPECT_EQ(COOKIE_PRIORITY_DEFAULT, pc.Priority());
854 
855   EXPECT_TRUE(pc.SetPriority("lowerest"));
856   EXPECT_EQ("name=value; priority=lowerest", pc.ToCookieLine());
857   EXPECT_EQ(COOKIE_PRIORITY_DEFAULT, pc.Priority());
858 
859   EXPECT_TRUE(pc.SetPriority(""));
860   EXPECT_EQ("name=value", pc.ToCookieLine());
861   EXPECT_EQ(COOKIE_PRIORITY_DEFAULT, pc.Priority());
862 }
863 
TEST(ParsedCookieTest,SetSameSite)864 TEST(ParsedCookieTest, SetSameSite) {
865   ParsedCookie pc("name=value");
866   EXPECT_TRUE(pc.IsValid());
867 
868   EXPECT_EQ("name=value", pc.ToCookieLine());
869   EXPECT_EQ(CookieSameSite::UNSPECIFIED, pc.SameSite());
870 
871   // Test each samesite directive, expect case-insensitive compare.
872   EXPECT_TRUE(pc.SetSameSite("strict"));
873   EXPECT_EQ("name=value; samesite=strict", pc.ToCookieLine());
874   EXPECT_EQ(CookieSameSite::STRICT_MODE, pc.SameSite());
875   EXPECT_TRUE(pc.IsValid());
876 
877   EXPECT_TRUE(pc.SetSameSite("lAx"));
878   EXPECT_EQ("name=value; samesite=lAx", pc.ToCookieLine());
879   EXPECT_EQ(CookieSameSite::LAX_MODE, pc.SameSite());
880   EXPECT_TRUE(pc.IsValid());
881 
882   EXPECT_TRUE(pc.SetSameSite("LAX"));
883   EXPECT_EQ("name=value; samesite=LAX", pc.ToCookieLine());
884   EXPECT_EQ(CookieSameSite::LAX_MODE, pc.SameSite());
885   EXPECT_TRUE(pc.IsValid());
886 
887   EXPECT_TRUE(pc.SetSameSite("None"));
888   EXPECT_EQ("name=value; samesite=None", pc.ToCookieLine());
889   EXPECT_EQ(CookieSameSite::NO_RESTRICTION, pc.SameSite());
890   EXPECT_TRUE(pc.IsValid());
891 
892   EXPECT_TRUE(pc.SetSameSite("NONE"));
893   EXPECT_EQ("name=value; samesite=NONE", pc.ToCookieLine());
894   EXPECT_EQ(CookieSameSite::NO_RESTRICTION, pc.SameSite());
895   EXPECT_TRUE(pc.IsValid());
896 
897   // Remove the SameSite attribute.
898   EXPECT_TRUE(pc.SetSameSite(""));
899   EXPECT_EQ("name=value", pc.ToCookieLine());
900   EXPECT_EQ(CookieSameSite::UNSPECIFIED, pc.SameSite());
901   EXPECT_TRUE(pc.IsValid());
902 
903   EXPECT_TRUE(pc.SetSameSite("Blah"));
904   EXPECT_EQ("name=value; samesite=Blah", pc.ToCookieLine());
905   EXPECT_EQ(CookieSameSite::UNSPECIFIED, pc.SameSite());
906   EXPECT_TRUE(pc.IsValid());
907 }
908 
909 // Test that the correct enum value is returned for the SameSite attribute
910 // string.
TEST(ParsedCookieTest,CookieSameSiteStringEnum)911 TEST(ParsedCookieTest, CookieSameSiteStringEnum) {
912   ParsedCookie pc("name=value; SameSite");
913   CookieSameSiteString actual = CookieSameSiteString::kLax;
914   EXPECT_EQ(CookieSameSite::UNSPECIFIED, pc.SameSite(&actual));
915   EXPECT_EQ(CookieSameSiteString::kEmptyString, actual);
916 
917   pc.SetSameSite("Strict");
918   EXPECT_EQ(CookieSameSite::STRICT_MODE, pc.SameSite(&actual));
919   EXPECT_EQ(CookieSameSiteString::kStrict, actual);
920 
921   pc.SetSameSite("Lax");
922   EXPECT_EQ(CookieSameSite::LAX_MODE, pc.SameSite(&actual));
923   EXPECT_EQ(CookieSameSiteString::kLax, actual);
924 
925   pc.SetSameSite("None");
926   EXPECT_EQ(CookieSameSite::NO_RESTRICTION, pc.SameSite(&actual));
927   EXPECT_EQ(CookieSameSiteString::kNone, actual);
928 
929   pc.SetSameSite("Extended");
930   EXPECT_EQ(CookieSameSite::UNSPECIFIED, pc.SameSite(&actual));
931   EXPECT_EQ(CookieSameSiteString::kExtended, actual);
932 
933   pc.SetSameSite("Bananas");
934   EXPECT_EQ(CookieSameSite::UNSPECIFIED, pc.SameSite(&actual));
935   EXPECT_EQ(CookieSameSiteString::kUnrecognized, actual);
936 
937   ParsedCookie pc2("no_samesite=1");
938   EXPECT_EQ(CookieSameSite::UNSPECIFIED, pc2.SameSite(&actual));
939   EXPECT_EQ(CookieSameSiteString::kUnspecified, actual);
940 }
941 
TEST(ParsedCookieTest,SettersInputValidation)942 TEST(ParsedCookieTest, SettersInputValidation) {
943   ParsedCookie pc("name=foobar");
944   EXPECT_TRUE(pc.SetPath("baz"));
945   EXPECT_EQ(pc.ToCookieLine(), "name=foobar; path=baz");
946 
947   EXPECT_TRUE(pc.SetPath("  baz "));
948   EXPECT_EQ(pc.ToCookieLine(), "name=foobar; path=baz");
949 
950   EXPECT_TRUE(pc.SetPath("     "));
951   EXPECT_EQ(pc.ToCookieLine(), "name=foobar");
952 
953   EXPECT_TRUE(pc.SetDomain("  baz "));
954   EXPECT_EQ(pc.ToCookieLine(), "name=foobar; domain=baz");
955 
956   // Invalid characters
957   EXPECT_FALSE(pc.SetPath("  baz\n "));
958   EXPECT_FALSE(pc.SetPath("f;oo"));
959   EXPECT_FALSE(pc.SetPath("\r"));
960   EXPECT_FALSE(pc.SetPath("\a"));
961   EXPECT_FALSE(pc.SetPath("\t"));
962   EXPECT_FALSE(pc.SetSameSite("\r"));
963 }
964 
TEST(ParsedCookieTest,ToCookieLineSpecialTokens)965 TEST(ParsedCookieTest, ToCookieLineSpecialTokens) {
966   // Special tokens "secure", "httponly" should be treated as
967   // any other name when they are in the first position.
968   {
969     ParsedCookie pc("");
970     pc.SetName("secure");
971     EXPECT_EQ(pc.ToCookieLine(), "secure=");
972   }
973   {
974     ParsedCookie pc("secure");
975     EXPECT_EQ(pc.ToCookieLine(), "=secure");
976   }
977   {
978     ParsedCookie pc("secure=foo");
979     EXPECT_EQ(pc.ToCookieLine(), "secure=foo");
980   }
981   {
982     ParsedCookie pc("foo=secure");
983     EXPECT_EQ(pc.ToCookieLine(), "foo=secure");
984   }
985   {
986     ParsedCookie pc("httponly=foo");
987     EXPECT_EQ(pc.ToCookieLine(), "httponly=foo");
988   }
989   {
990     ParsedCookie pc("foo");
991     pc.SetName("secure");
992     EXPECT_EQ(pc.ToCookieLine(), "secure=foo");
993   }
994   {
995     ParsedCookie pc("bar");
996     pc.SetName("httponly");
997     EXPECT_EQ(pc.ToCookieLine(), "httponly=bar");
998   }
999   {
1000     ParsedCookie pc("foo=bar; baz=bob");
1001     EXPECT_EQ(pc.ToCookieLine(), "foo=bar; baz=bob");
1002   }
1003   // Outside of the first position, the value associated with a special name
1004   // should not be printed.
1005   {
1006     ParsedCookie pc("name=foo; secure");
1007     EXPECT_EQ(pc.ToCookieLine(), "name=foo; secure");
1008   }
1009   {
1010     ParsedCookie pc("name=foo; secure=bar");
1011     EXPECT_EQ(pc.ToCookieLine(), "name=foo; secure");
1012   }
1013   {
1014     ParsedCookie pc("name=foo; httponly=baz");
1015     EXPECT_EQ(pc.ToCookieLine(), "name=foo; httponly");
1016   }
1017   {
1018     ParsedCookie pc("name=foo; bar=secure");
1019     EXPECT_EQ(pc.ToCookieLine(), "name=foo; bar=secure");
1020   }
1021   // Repeated instances of the special tokens are also fine.
1022   {
1023     ParsedCookie pc("name=foo; secure; secure=yesplease; secure; secure");
1024     EXPECT_TRUE(pc.IsValid());
1025     EXPECT_TRUE(pc.IsSecure());
1026     EXPECT_FALSE(pc.IsHttpOnly());
1027   }
1028   {
1029     ParsedCookie pc("partitioned=foo");
1030     EXPECT_EQ("partitioned", pc.Name());
1031     EXPECT_EQ("foo", pc.Value());
1032     EXPECT_FALSE(pc.IsPartitioned());
1033   }
1034   {
1035     ParsedCookie pc("partitioned=");
1036     EXPECT_EQ("partitioned", pc.Name());
1037     EXPECT_EQ("", pc.Value());
1038     EXPECT_FALSE(pc.IsPartitioned());
1039   }
1040   {
1041     ParsedCookie pc("=partitioned");
1042     EXPECT_EQ("", pc.Name());
1043     EXPECT_EQ("partitioned", pc.Value());
1044     EXPECT_FALSE(pc.IsPartitioned());
1045   }
1046   {
1047     ParsedCookie pc(
1048         "partitioned; partitioned; secure; httponly; httponly; secure");
1049     EXPECT_EQ("", pc.Name());
1050     EXPECT_EQ("partitioned", pc.Value());
1051     EXPECT_TRUE(pc.IsPartitioned());
1052   }
1053 }
1054 
TEST(ParsedCookieTest,SameSiteValues)1055 TEST(ParsedCookieTest, SameSiteValues) {
1056   struct TestCase {
1057     const char* cookie;
1058     bool valid;
1059     CookieSameSite mode;
1060   } cases[]{{"n=v; samesite=strict", true, CookieSameSite::STRICT_MODE},
1061             {"n=v; samesite=lax", true, CookieSameSite::LAX_MODE},
1062             {"n=v; samesite=none", true, CookieSameSite::NO_RESTRICTION},
1063             {"n=v; samesite=boo", true, CookieSameSite::UNSPECIFIED},
1064             {"n=v; samesite", true, CookieSameSite::UNSPECIFIED},
1065             {"n=v", true, CookieSameSite::UNSPECIFIED}};
1066 
1067   for (const auto& test : cases) {
1068     SCOPED_TRACE(test.cookie);
1069     ParsedCookie pc(test.cookie);
1070     EXPECT_EQ(test.valid, pc.IsValid());
1071     EXPECT_EQ(test.mode, pc.SameSite());
1072   }
1073 }
1074 
TEST(ParsedCookieTest,InvalidNonAlphanumericChars)1075 TEST(ParsedCookieTest, InvalidNonAlphanumericChars) {
1076   // clang-format off
1077   const char* cases[] = {
1078       "name=\x05",
1079       "name=foo\x1c" "bar",
1080       "name=foobar\x11",
1081       "name=\x02" "foobar",
1082       "\x05=value",
1083       "foo\x05" "bar=value",
1084       "foobar\x05" "=value",
1085       "\x05" "foobar=value",
1086       "foo\x05" "bar=foo\x05" "bar",
1087       "foo=ba,ba\x05" "z=boo",
1088       "foo=ba,baz=bo\x05" "o",
1089       "foo=ba,ba\05" "z=bo\x05" "o",
1090       "foo=ba,ba\x7F" "z=bo",
1091       "fo\x7F" "o=ba,z=bo",
1092       "foo=bar\x7F" ";z=bo",
1093   };
1094   // clang-format on
1095 
1096   for (size_t i = 0; i < std::size(cases); i++) {
1097     SCOPED_TRACE(testing::Message()
1098                  << "Test case #" << base::NumberToString(i + 1));
1099     CookieInclusionStatus status;
1100     ParsedCookie pc(cases[i], &status);
1101     EXPECT_FALSE(pc.IsValid());
1102     EXPECT_TRUE(status.HasOnlyExclusionReason(
1103         CookieInclusionStatus::ExclusionReason::EXCLUDE_DISALLOWED_CHARACTER));
1104   }
1105 }
1106 
TEST(ParsedCookieTest,ValidNonAlphanumericChars)1107 TEST(ParsedCookieTest, ValidNonAlphanumericChars) {
1108   // Note that some of these words are pasted backwords thanks to poor vim
1109   // bidi support. This should not affect the tests, however.
1110   const char pc1_literal[] = "name=العربية";
1111   const char pc2_literal[] = "name=普通話";
1112   const char pc3_literal[] = "name=ภาษาไทย";
1113   const char pc4_literal[] = "name=עִבְרִית";
1114   const char pc5_literal[] = "العربية=value";
1115   const char pc6_literal[] = "普通話=value";
1116   const char pc7_literal[] = "ภาษาไทย=value";
1117   const char pc8_literal[] = "עִבְרִית=value";
1118   const char pc9_literal[] = "@foo=bar";
1119 
1120   ParsedCookie pc1(pc1_literal);
1121   ParsedCookie pc2(pc2_literal);
1122   ParsedCookie pc3(pc3_literal);
1123   ParsedCookie pc4(pc4_literal);
1124   ParsedCookie pc5(pc5_literal);
1125   ParsedCookie pc6(pc6_literal);
1126   ParsedCookie pc7(pc7_literal);
1127   ParsedCookie pc8(pc8_literal);
1128   ParsedCookie pc9(pc9_literal);
1129 
1130   EXPECT_TRUE(pc1.IsValid());
1131   EXPECT_EQ(pc1_literal, pc1.ToCookieLine());
1132   EXPECT_TRUE(pc2.IsValid());
1133   EXPECT_EQ(pc2_literal, pc2.ToCookieLine());
1134   EXPECT_TRUE(pc3.IsValid());
1135   EXPECT_EQ(pc3_literal, pc3.ToCookieLine());
1136   EXPECT_TRUE(pc4.IsValid());
1137   EXPECT_EQ(pc4_literal, pc4.ToCookieLine());
1138   EXPECT_TRUE(pc5.IsValid());
1139   EXPECT_EQ(pc5_literal, pc5.ToCookieLine());
1140   EXPECT_TRUE(pc6.IsValid());
1141   EXPECT_EQ(pc6_literal, pc6.ToCookieLine());
1142   EXPECT_TRUE(pc7.IsValid());
1143   EXPECT_EQ(pc7_literal, pc7.ToCookieLine());
1144   EXPECT_TRUE(pc8.IsValid());
1145   EXPECT_EQ(pc8_literal, pc8.ToCookieLine());
1146   EXPECT_TRUE(pc9.IsValid());
1147   EXPECT_EQ(pc9_literal, pc9.ToCookieLine());
1148 
1149   EXPECT_TRUE(pc1.SetValue(pc1.Value()));
1150   EXPECT_EQ(pc1_literal, pc1.ToCookieLine());
1151   EXPECT_TRUE(pc1.IsValid());
1152   EXPECT_TRUE(pc2.SetValue(pc2.Value()));
1153   EXPECT_EQ(pc2_literal, pc2.ToCookieLine());
1154   EXPECT_TRUE(pc2.IsValid());
1155   EXPECT_TRUE(pc3.SetValue(pc3.Value()));
1156   EXPECT_EQ(pc3_literal, pc3.ToCookieLine());
1157   EXPECT_TRUE(pc3.IsValid());
1158   EXPECT_TRUE(pc4.SetValue(pc4.Value()));
1159   EXPECT_EQ(pc4_literal, pc4.ToCookieLine());
1160   EXPECT_TRUE(pc4.IsValid());
1161   EXPECT_TRUE(pc5.SetName(pc5.Name()));
1162   EXPECT_EQ(pc5_literal, pc5.ToCookieLine());
1163   EXPECT_TRUE(pc5.IsValid());
1164   EXPECT_TRUE(pc6.SetName(pc6.Name()));
1165   EXPECT_EQ(pc6_literal, pc6.ToCookieLine());
1166   EXPECT_TRUE(pc6.IsValid());
1167   EXPECT_TRUE(pc7.SetName(pc7.Name()));
1168   EXPECT_EQ(pc7_literal, pc7.ToCookieLine());
1169   EXPECT_TRUE(pc7.IsValid());
1170   EXPECT_TRUE(pc8.SetName(pc8.Name()));
1171   EXPECT_EQ(pc8_literal, pc8.ToCookieLine());
1172   EXPECT_TRUE(pc8.IsValid());
1173   EXPECT_TRUE(pc9.SetName(pc9.Name()));
1174   EXPECT_EQ(pc9_literal, pc9.ToCookieLine());
1175   EXPECT_TRUE(pc9.IsValid());
1176 }
1177 
TEST(ParsedCookieTest,PreviouslyTruncatingCharInCookieLine)1178 TEST(ParsedCookieTest, PreviouslyTruncatingCharInCookieLine) {
1179   // Test scenarios where a control char may appear at start, middle and end of
1180   // a cookie line. Control char array with NULL (\x0), CR (\xD), LF (xA),
1181   // HT (\x9) and BS (\x1B).
1182   const struct {
1183     const char ctlChar;
1184     bool invalid_character;
1185   } kTests[] = {{'\x0', true},
1186                 {'\xD', true},
1187                 {'\xA', true},
1188                 {'\x9', false},
1189                 {'\x1B', false}};
1190 
1191   for (const auto& test : kTests) {
1192     SCOPED_TRACE(testing::Message() << "Using test.ctlChar == "
1193                                     << base::NumberToString(test.ctlChar));
1194     std::string ctl_string(1, test.ctlChar);
1195     std::string ctl_at_start_cookie_string =
1196         base::StrCat({ctl_string, "foo=bar"});
1197     ParsedCookie ctl_at_start_cookie(ctl_at_start_cookie_string);
1198     // Lots of factors determine whether IsValid() is true here:
1199     //
1200     //  - For the tab character ('\x9), leading whitespace is valid and the
1201     //  spec indicates that it should just be removed and the cookie parsed
1202     //  normally. Thus, in this case the cookie is always valid.
1203     //
1204     //  - For control characters that historically truncated the cookie, they
1205     //  now cause the cookie to be deemed invalid.
1206     //
1207     //  - For other control characters the cookie is always treated as invalid.
1208     EXPECT_EQ(ctl_at_start_cookie.IsValid(), test.ctlChar == '\x9');
1209 
1210     std::string ctl_at_middle_cookie_string =
1211         base::StrCat({"foo=bar;", ctl_string, "secure"});
1212     ParsedCookie ctl_at_middle_cookie(ctl_at_middle_cookie_string);
1213     if (test.invalid_character) {
1214       EXPECT_EQ(ctl_at_middle_cookie.IsValid(), false);
1215     }
1216 
1217     std::string ctl_at_end_cookie_string =
1218         base::StrCat({"foo=bar;", "secure;", ctl_string});
1219     ParsedCookie ctl_at_end_cookie(ctl_at_end_cookie_string);
1220     if (test.invalid_character) {
1221       EXPECT_EQ(ctl_at_end_cookie.IsValid(), false);
1222     }
1223   }
1224 
1225   // Test if there are multiple control characters that terminate.
1226   std::string ctls_cookie_string = "foo=bar;\xA\xD";
1227   ParsedCookie ctls_cookie(ctls_cookie_string);
1228   EXPECT_EQ(ctls_cookie.IsValid(), false);
1229 }
1230 
TEST(ParsedCookieTest,HtabInNameOrValue)1231 TEST(ParsedCookieTest, HtabInNameOrValue) {
1232   std::string no_htab_string = "foo=bar";
1233   ParsedCookie no_htab(no_htab_string);
1234   EXPECT_FALSE(no_htab.HasInternalHtab());
1235 
1236   std::string htab_leading_trailing_string = "\tfoo=bar\t";
1237   ParsedCookie htab_leading_trailing(htab_leading_trailing_string);
1238   EXPECT_FALSE(htab_leading_trailing.HasInternalHtab());
1239 
1240   std::string htab_name_string = "f\too=bar";
1241   ParsedCookie htab_name(htab_name_string);
1242   EXPECT_TRUE(htab_name.HasInternalHtab());
1243 
1244   std::string htab_value_string = "foo=b\tar";
1245   ParsedCookie htab_value(htab_value_string);
1246   EXPECT_TRUE(htab_value.HasInternalHtab());
1247 }
1248 
1249 }  // namespace net
1250