1 // Copyright 2018 The Chromium Authors. All rights reserved.
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 "services/network/public/cpp/cookie_manager_mojom_traits.h"
6 
7 #include <set>
8 #include <vector>
9 
10 #include "base/test/gtest_util.h"
11 #include "mojo/public/cpp/base/time_mojom_traits.h"
12 #include "mojo/public/cpp/test_support/test_utils.h"
13 #include "net/base/schemeful_site.h"
14 #include "net/cookies/cookie_constants.h"
15 #include "services/network/public/cpp/cookie_manager_mojom_traits.h"
16 #include "services/network/public/mojom/cookie_manager.mojom.h"
17 #include "testing/gtest/include/gtest/gtest.h"
18 #include "url/third_party/mozilla/url_parse.h"
19 
20 namespace network {
21 namespace {
22 
23 template <typename MojoType, typename NativeType>
SerializeAndDeserializeEnum(NativeType in,NativeType * out)24 bool SerializeAndDeserializeEnum(NativeType in, NativeType* out) {
25   MojoType intermediate = mojo::EnumTraits<MojoType, NativeType>::ToMojom(in);
26   return mojo::EnumTraits<MojoType, NativeType>::FromMojom(intermediate, out);
27 }
28 
TEST(CookieManagerTraitsTest,Roundtrips_CanonicalCookie)29 TEST(CookieManagerTraitsTest, Roundtrips_CanonicalCookie) {
30   net::CanonicalCookie original(
31       "A", "B", "x.y", "/path", base::Time(), base::Time(), base::Time(), false,
32       false, net::CookieSameSite::NO_RESTRICTION, net::COOKIE_PRIORITY_LOW,
33       false, net::CookieSourceScheme::kSecure, 8433);
34 
35   net::CanonicalCookie copied;
36 
37   EXPECT_TRUE(mojo::test::SerializeAndDeserialize<mojom::CanonicalCookie>(
38       &original, &copied));
39 
40   EXPECT_EQ(original.Name(), copied.Name());
41   EXPECT_EQ(original.Value(), copied.Value());
42   EXPECT_EQ(original.Domain(), copied.Domain());
43   EXPECT_EQ(original.Path(), copied.Path());
44   EXPECT_EQ(original.CreationDate(), copied.CreationDate());
45   EXPECT_EQ(original.LastAccessDate(), copied.LastAccessDate());
46   EXPECT_EQ(original.ExpiryDate(), copied.ExpiryDate());
47   EXPECT_EQ(original.IsSecure(), copied.IsSecure());
48   EXPECT_EQ(original.IsHttpOnly(), copied.IsHttpOnly());
49   EXPECT_EQ(original.SameSite(), copied.SameSite());
50   EXPECT_EQ(original.Priority(), copied.Priority());
51   EXPECT_EQ(original.IsSameParty(), copied.IsSameParty());
52   EXPECT_EQ(original.SourceScheme(), copied.SourceScheme());
53   EXPECT_EQ(original.SourcePort(), copied.SourcePort());
54 
55   // Test port edge cases: unspecified.
56   net::CanonicalCookie original_unspecified(
57       "A", "B", "x.y", "/path", base::Time(), base::Time(), base::Time(), false,
58       false, net::CookieSameSite::NO_RESTRICTION, net::COOKIE_PRIORITY_LOW,
59       false, net::CookieSourceScheme::kSecure, url::PORT_UNSPECIFIED);
60   net::CanonicalCookie copied_unspecified;
61 
62   EXPECT_TRUE(mojo::test::SerializeAndDeserialize<mojom::CanonicalCookie>(
63       &original_unspecified, &copied_unspecified));
64 
65   EXPECT_EQ(original_unspecified.SourcePort(), copied_unspecified.SourcePort());
66 
67   // Test port edge cases: invalid.
68   net::CanonicalCookie original_invalid(
69       "A", "B", "x.y", "/path", base::Time(), base::Time(), base::Time(), false,
70       false, net::CookieSameSite::NO_RESTRICTION, net::COOKIE_PRIORITY_LOW,
71       false, net::CookieSourceScheme::kSecure, url::PORT_INVALID);
72   net::CanonicalCookie copied_invalid;
73 
74   EXPECT_TRUE(mojo::test::SerializeAndDeserialize<mojom::CanonicalCookie>(
75       &original_invalid, &copied_invalid));
76 
77   EXPECT_EQ(original_invalid.SourcePort(), copied_invalid.SourcePort());
78 
79   // Serializer returns false if cookie is non-canonical.
80   // Example is non-canonical because of newline in name.
81 
82   original = net::CanonicalCookie("A\n", "B", "x.y", "/path", base::Time(),
83                                   base::Time(), base::Time(), false, false,
84                                   net::CookieSameSite::NO_RESTRICTION,
85                                   net::COOKIE_PRIORITY_LOW, false);
86 
87   EXPECT_FALSE(mojo::test::SerializeAndDeserialize<mojom::CanonicalCookie>(
88       &original, &copied));
89 }
90 
TEST(CookieManagerTraitsTest,Roundtrips_CookieInclusionStatus)91 TEST(CookieManagerTraitsTest, Roundtrips_CookieInclusionStatus) {
92   // This status + warning combo doesn't really make sense. It's just an
93   // arbitrary selection of values to test the serialization/deserialization.
94   net::CookieInclusionStatus original =
95       net::CookieInclusionStatus::MakeFromReasonsForTesting(
96           {net::CookieInclusionStatus::EXCLUDE_SAMESITE_LAX,
97            net::CookieInclusionStatus::EXCLUDE_INVALID_PREFIX,
98            net::CookieInclusionStatus::EXCLUDE_SECURE_ONLY},
99           {net::CookieInclusionStatus::
100                WARN_SAMESITE_UNSPECIFIED_CROSS_SITE_CONTEXT,
101            net::CookieInclusionStatus::WARN_SAMESITE_NONE_INSECURE,
102            net::CookieInclusionStatus::
103                WARN_SAMESITE_UNSPECIFIED_LAX_ALLOW_UNSAFE});
104 
105   net::CookieInclusionStatus copied;
106 
107   EXPECT_TRUE(mojo::test::SerializeAndDeserialize<mojom::CookieInclusionStatus>(
108       &original, &copied));
109   EXPECT_TRUE(copied.HasExactlyExclusionReasonsForTesting(
110       {net::CookieInclusionStatus::EXCLUDE_SAMESITE_LAX,
111        net::CookieInclusionStatus::EXCLUDE_INVALID_PREFIX,
112        net::CookieInclusionStatus::EXCLUDE_SECURE_ONLY}));
113   EXPECT_TRUE(copied.HasExactlyWarningReasonsForTesting(
114       {net::CookieInclusionStatus::WARN_SAMESITE_UNSPECIFIED_CROSS_SITE_CONTEXT,
115        net::CookieInclusionStatus::WARN_SAMESITE_NONE_INSECURE,
116        net::CookieInclusionStatus::
117            WARN_SAMESITE_UNSPECIFIED_LAX_ALLOW_UNSAFE}));
118 
119   net::CookieInclusionStatus invalid;
120   invalid.set_exclusion_reasons(~0u);
121 
122   EXPECT_FALSE(
123       mojo::test::SerializeAndDeserialize<mojom::CookieInclusionStatus>(
124           &invalid, &copied));
125 }
126 
TEST(CookieManagerTraitsTest,Rountrips_CookieAccessResult)127 TEST(CookieManagerTraitsTest, Rountrips_CookieAccessResult) {
128   net::CookieAccessResult original = net::CookieAccessResult(
129       net::CookieEffectiveSameSite::LAX_MODE,
130       net::CookieInclusionStatus(
131           net::CookieInclusionStatus::
132               EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX,
133           net::CookieInclusionStatus::
134               WARN_SAMESITE_UNSPECIFIED_CROSS_SITE_CONTEXT),
135       net::CookieAccessSemantics::LEGACY);
136   net::CookieAccessResult copied;
137 
138   EXPECT_TRUE(mojo::test::SerializeAndDeserialize<mojom::CookieAccessResult>(
139       &original, &copied));
140 
141   EXPECT_EQ(original.effective_same_site, copied.effective_same_site);
142   EXPECT_TRUE(copied.status.HasExactlyExclusionReasonsForTesting(
143       {net::CookieInclusionStatus::
144            EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX}));
145   EXPECT_TRUE(copied.status.HasExactlyWarningReasonsForTesting(
146       {net::CookieInclusionStatus::
147            WARN_SAMESITE_UNSPECIFIED_CROSS_SITE_CONTEXT}));
148 }
149 
TEST(CookieManagerTraitsTest,Rountrips_CookieWithAccessResult)150 TEST(CookieManagerTraitsTest, Rountrips_CookieWithAccessResult) {
151   net::CanonicalCookie original_cookie(
152       "A", "B", "x.y", "/path", base::Time(), base::Time(), base::Time(),
153       /* secure = */ true, /* http_only = */ false,
154       net::CookieSameSite::NO_RESTRICTION, net::COOKIE_PRIORITY_LOW, false);
155 
156   net::CookieWithAccessResult original = {original_cookie,
157                                           net::CookieAccessResult()};
158   net::CookieWithAccessResult copied;
159 
160   EXPECT_TRUE(
161       mojo::test::SerializeAndDeserialize<mojom::CookieWithAccessResult>(
162           &original, &copied));
163 
164   EXPECT_EQ(original.cookie.Name(), copied.cookie.Name());
165   EXPECT_EQ(original.cookie.Value(), copied.cookie.Value());
166   EXPECT_EQ(original.cookie.Domain(), copied.cookie.Domain());
167   EXPECT_EQ(original.cookie.Path(), copied.cookie.Path());
168   EXPECT_EQ(original.cookie.CreationDate(), copied.cookie.CreationDate());
169   EXPECT_EQ(original.cookie.LastAccessDate(), copied.cookie.LastAccessDate());
170   EXPECT_EQ(original.cookie.ExpiryDate(), copied.cookie.ExpiryDate());
171   EXPECT_EQ(original.cookie.IsSecure(), copied.cookie.IsSecure());
172   EXPECT_EQ(original.cookie.IsHttpOnly(), copied.cookie.IsHttpOnly());
173   EXPECT_EQ(original.cookie.SameSite(), copied.cookie.SameSite());
174   EXPECT_EQ(original.cookie.Priority(), copied.cookie.Priority());
175   EXPECT_EQ(original.cookie.IsSameParty(), copied.cookie.IsSameParty());
176   EXPECT_EQ(original.access_result.effective_same_site,
177             copied.access_result.effective_same_site);
178   EXPECT_EQ(original.access_result.status, copied.access_result.status);
179 }
180 
TEST(CookieManagerTraitsTest,Rountrips_CookieAndLineWithAccessResult)181 TEST(CookieManagerTraitsTest, Rountrips_CookieAndLineWithAccessResult) {
182   net::CanonicalCookie original_cookie(
183       "A", "B", "x.y", "/path", base::Time(), base::Time(), base::Time(),
184       /* secure = */ true, /* http_only = */ false,
185       net::CookieSameSite::NO_RESTRICTION, net::COOKIE_PRIORITY_LOW, false);
186 
187   net::CookieAndLineWithAccessResult original(original_cookie, "cookie-string",
188                                               net::CookieAccessResult());
189   net::CookieAndLineWithAccessResult copied;
190 
191   EXPECT_TRUE(
192       mojo::test::SerializeAndDeserialize<mojom::CookieAndLineWithAccessResult>(
193           &original, &copied));
194 
195   EXPECT_EQ(original.cookie->Name(), copied.cookie->Name());
196   EXPECT_EQ(original.cookie->Value(), copied.cookie->Value());
197   EXPECT_EQ(original.cookie->Domain(), copied.cookie->Domain());
198   EXPECT_EQ(original.cookie->Path(), copied.cookie->Path());
199   EXPECT_EQ(original.cookie->CreationDate(), copied.cookie->CreationDate());
200   EXPECT_EQ(original.cookie->LastAccessDate(), copied.cookie->LastAccessDate());
201   EXPECT_EQ(original.cookie->ExpiryDate(), copied.cookie->ExpiryDate());
202   EXPECT_EQ(original.cookie->IsSecure(), copied.cookie->IsSecure());
203   EXPECT_EQ(original.cookie->IsHttpOnly(), copied.cookie->IsHttpOnly());
204   EXPECT_EQ(original.cookie->SameSite(), copied.cookie->SameSite());
205   EXPECT_EQ(original.cookie->Priority(), copied.cookie->Priority());
206   EXPECT_EQ(original.access_result.effective_same_site,
207             copied.access_result.effective_same_site);
208   EXPECT_EQ(original.cookie_string, copied.cookie_string);
209 }
210 
TEST(CookieManagerTraitsTest,Roundtrips_CookieSameSite)211 TEST(CookieManagerTraitsTest, Roundtrips_CookieSameSite) {
212   for (net::CookieSameSite cookie_state :
213        {net::CookieSameSite::NO_RESTRICTION, net::CookieSameSite::LAX_MODE,
214         net::CookieSameSite::STRICT_MODE, net::CookieSameSite::UNSPECIFIED}) {
215     net::CookieSameSite roundtrip;
216     ASSERT_TRUE(SerializeAndDeserializeEnum<mojom::CookieSameSite>(cookie_state,
217                                                                    &roundtrip));
218     EXPECT_EQ(cookie_state, roundtrip);
219   }
220 }
221 
TEST(CookieManagerTraitsTest,Roundtrips_CookieEffectiveSameSite)222 TEST(CookieManagerTraitsTest, Roundtrips_CookieEffectiveSameSite) {
223   for (net::CookieEffectiveSameSite cookie_state :
224        {net::CookieEffectiveSameSite::NO_RESTRICTION,
225         net::CookieEffectiveSameSite::LAX_MODE,
226         net::CookieEffectiveSameSite::STRICT_MODE,
227         net::CookieEffectiveSameSite::LAX_MODE_ALLOW_UNSAFE,
228         net::CookieEffectiveSameSite::UNDEFINED}) {
229     net::CookieEffectiveSameSite roundtrip;
230     ASSERT_TRUE(SerializeAndDeserializeEnum<mojom::CookieEffectiveSameSite>(
231         cookie_state, &roundtrip));
232     EXPECT_EQ(cookie_state, roundtrip);
233   }
234 }
235 
TEST(CookieManagerTraitsTest,Roundtrips_ContextType)236 TEST(CookieManagerTraitsTest, Roundtrips_ContextType) {
237   using ContextType = net::CookieOptions::SameSiteCookieContext::ContextType;
238   for (ContextType context_type :
239        {ContextType::CROSS_SITE, ContextType::SAME_SITE_LAX_METHOD_UNSAFE,
240         ContextType::SAME_SITE_LAX, ContextType::SAME_SITE_STRICT}) {
241     ContextType roundtrip;
242     ASSERT_TRUE(SerializeAndDeserializeEnum<mojom::ContextType>(context_type,
243                                                                 &roundtrip));
244     EXPECT_EQ(context_type, roundtrip);
245   }
246 }
247 
TEST(CookieManagerTraitsTest,Roundtrips_CookieAccessSemantics)248 TEST(CookieManagerTraitsTest, Roundtrips_CookieAccessSemantics) {
249   for (net::CookieAccessSemantics access_semantics :
250        {net::CookieAccessSemantics::UNKNOWN,
251         net::CookieAccessSemantics::NONLEGACY,
252         net::CookieAccessSemantics::LEGACY}) {
253     net::CookieAccessSemantics roundtrip;
254     ASSERT_TRUE(SerializeAndDeserializeEnum<mojom::CookieAccessSemantics>(
255         access_semantics, &roundtrip));
256     EXPECT_EQ(access_semantics, roundtrip);
257   }
258 }
259 
TEST(CookieManagerTraitsTest,Roundtrips_CookieChangeCause)260 TEST(CookieManagerTraitsTest, Roundtrips_CookieChangeCause) {
261   for (net::CookieChangeCause change_cause :
262        {net::CookieChangeCause::INSERTED, net::CookieChangeCause::EXPLICIT,
263         net::CookieChangeCause::UNKNOWN_DELETION,
264         net::CookieChangeCause::OVERWRITE, net::CookieChangeCause::EXPIRED,
265         net::CookieChangeCause::EVICTED,
266         net::CookieChangeCause::EXPIRED_OVERWRITE}) {
267     net::CookieChangeCause roundtrip;
268     ASSERT_TRUE(SerializeAndDeserializeEnum<mojom::CookieChangeCause>(
269         change_cause, &roundtrip));
270     EXPECT_EQ(change_cause, roundtrip);
271   }
272 }
273 
TEST(CookieManagerTraitsTest,Roundtrips_CookieSameSiteContext)274 TEST(CookieManagerTraitsTest, Roundtrips_CookieSameSiteContext) {
275   using ContextType = net::CookieOptions::SameSiteCookieContext::ContextType;
276 
277   const ContextType all_context_types[]{
278       ContextType::CROSS_SITE, ContextType::SAME_SITE_LAX_METHOD_UNSAFE,
279       ContextType::SAME_SITE_LAX, ContextType::SAME_SITE_STRICT};
280 
281   for (ContextType context_type : all_context_types) {
282     for (ContextType schemeful_context_type : all_context_types) {
283       net::CookieOptions::SameSiteCookieContext context_in, copy;
284       // We want to test malformed SameSiteCookieContexts. Since the constructor
285       // will DCHECK for these use the setters to bypass it.
286       context_in.set_context(context_type);
287       context_in.set_schemeful_context(schemeful_context_type);
288 
289       EXPECT_EQ(
290           mojo::test::SerializeAndDeserialize<mojom::CookieSameSiteContext>(
291               &context_in, &copy),
292           schemeful_context_type <= context_type);
293 
294       if (schemeful_context_type <= context_type)
295         EXPECT_EQ(context_in, copy);
296     }
297   }
298 }
299 
TEST(CookieManagerTraitsTest,Roundtrips_CookieOptions)300 TEST(CookieManagerTraitsTest, Roundtrips_CookieOptions) {
301   {
302     net::CookieOptions least_trusted, copy;
303     EXPECT_FALSE(least_trusted.return_excluded_cookies());
304 
305     least_trusted.set_return_excluded_cookies();  // differ from default.
306 
307     EXPECT_TRUE(mojo::test::SerializeAndDeserialize<mojom::CookieOptions>(
308         &least_trusted, &copy));
309     EXPECT_TRUE(copy.exclude_httponly());
310     EXPECT_EQ(
311         net::CookieOptions::SameSiteCookieContext(
312             net::CookieOptions::SameSiteCookieContext::ContextType::CROSS_SITE),
313         copy.same_site_cookie_context());
314     EXPECT_TRUE(copy.return_excluded_cookies());
315   }
316 
317   {
318     net::CookieOptions very_trusted, copy;
319     auto kPartyContext = std::set<net::SchemefulSite>{
320         net::SchemefulSite(url::Origin::Create(GURL("https://a.test")))};
321     very_trusted.set_include_httponly();
322     very_trusted.set_same_site_cookie_context(
323         net::CookieOptions::SameSiteCookieContext::MakeInclusive());
324     very_trusted.set_full_party_context(kPartyContext);
325 
326     EXPECT_TRUE(mojo::test::SerializeAndDeserialize<mojom::CookieOptions>(
327         &very_trusted, &copy));
328     EXPECT_FALSE(copy.exclude_httponly());
329     EXPECT_EQ(net::CookieOptions::SameSiteCookieContext::MakeInclusive(),
330               copy.same_site_cookie_context());
331     EXPECT_FALSE(copy.return_excluded_cookies());
332     EXPECT_EQ(kPartyContext, copy.full_party_context());
333   }
334 }
335 
TEST(CookieManagerTraitsTest,Roundtrips_FullPartyContext)336 TEST(CookieManagerTraitsTest, Roundtrips_FullPartyContext) {
337   {
338     std::vector<std::set<net::SchemefulSite>> kTestCases = {
339         std::set<net::SchemefulSite>(),
340         std::set<net::SchemefulSite>{net::SchemefulSite()},
341         std::set<net::SchemefulSite>{
342             net::SchemefulSite(url::Origin::Create(GURL("https://a.test")))},
343         std::set<net::SchemefulSite>{
344             net::SchemefulSite(url::Origin::Create(GURL("http://a.test"))),
345             net::SchemefulSite(url::Origin::Create(GURL("http://b.test")))},
346     };
347 
348     for (const std::set<net::SchemefulSite>& fpc : kTestCases) {
349       net::CookieOptions options, copy;
350       options.set_full_party_context(fpc);
351       EXPECT_TRUE(mojo::test::SerializeAndDeserialize<mojom::CookieOptions>(
352           &options, &copy));
353       EXPECT_EQ(fpc, copy.full_party_context());
354     }
355   }
356   {
357     base::Optional<std::set<net::SchemefulSite>> kFullPartyContext =
358         base::nullopt;
359     net::CookieOptions options, copy;
360     options.set_full_party_context(kFullPartyContext);
361     EXPECT_TRUE(mojo::test::SerializeAndDeserialize<mojom::CookieOptions>(
362         &options, &copy));
363     EXPECT_EQ(kFullPartyContext, copy.full_party_context());
364   }
365 }
366 
TEST(CookieManagerTraitsTest,Roundtrips_CookieChangeInfo)367 TEST(CookieManagerTraitsTest, Roundtrips_CookieChangeInfo) {
368   net::CanonicalCookie original_cookie(
369       "A", "B", "x.y", "/path", base::Time(), base::Time(), base::Time(),
370       /* secure = */ false, /* http_only = */ false,
371       net::CookieSameSite::UNSPECIFIED, net::COOKIE_PRIORITY_LOW, false);
372 
373   net::CookieChangeInfo original(
374       original_cookie,
375       net::CookieAccessResult(net::CookieEffectiveSameSite::UNDEFINED,
376                               net::CookieInclusionStatus(),
377                               net::CookieAccessSemantics::LEGACY),
378       net::CookieChangeCause::EXPLICIT);
379 
380   net::CookieChangeInfo copied;
381 
382   EXPECT_TRUE(mojo::test::SerializeAndDeserialize<mojom::CookieChangeInfo>(
383       &original, &copied));
384 
385   EXPECT_EQ(original.cookie.Name(), copied.cookie.Name());
386   EXPECT_EQ(original.cookie.Value(), copied.cookie.Value());
387   EXPECT_EQ(original.cookie.Domain(), copied.cookie.Domain());
388   EXPECT_EQ(original.cookie.Path(), copied.cookie.Path());
389   EXPECT_EQ(original.cookie.CreationDate(), copied.cookie.CreationDate());
390   EXPECT_EQ(original.cookie.LastAccessDate(), copied.cookie.LastAccessDate());
391   EXPECT_EQ(original.cookie.ExpiryDate(), copied.cookie.ExpiryDate());
392   EXPECT_EQ(original.cookie.IsSecure(), copied.cookie.IsSecure());
393   EXPECT_EQ(original.cookie.IsHttpOnly(), copied.cookie.IsHttpOnly());
394   EXPECT_EQ(original.cookie.SameSite(), copied.cookie.SameSite());
395   EXPECT_EQ(original.cookie.Priority(), copied.cookie.Priority());
396   EXPECT_EQ(original.access_result.access_semantics,
397             copied.access_result.access_semantics);
398   EXPECT_EQ(original.cause, copied.cause);
399 }
400 
401 // TODO: Add tests for CookiePriority, more extensive CookieOptions ones
402 
403 }  // namespace
404 }  // namespace network
405