• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2021 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 <string>
6 #include <tuple>
7 
8 #include "base/test/scoped_feature_list.h"
9 #include "net/base/features.h"
10 #include "net/cookies/cookie_constants.h"
11 #include "net/cookies/cookie_partition_key.h"
12 #include "testing/gtest/include/gtest/gtest.h"
13 
14 namespace net {
15 
16 class CookiePartitionKeyTest : public testing::TestWithParam<bool> {
17  protected:
18   // testing::Test
SetUp()19   void SetUp() override {
20     scoped_feature_list_.InitWithFeatureState(features::kPartitionedCookies,
21                                               PartitionedCookiesEnabled());
22   }
23 
PartitionedCookiesEnabled()24   bool PartitionedCookiesEnabled() { return GetParam(); }
25 
26  private:
27   base::test::ScopedFeatureList scoped_feature_list_;
28 };
29 
30 INSTANTIATE_TEST_SUITE_P(/* no label */,
31                          CookiePartitionKeyTest,
32                          ::testing::Bool());
33 
TEST_P(CookiePartitionKeyTest,Serialization)34 TEST_P(CookiePartitionKeyTest, Serialization) {
35   base::UnguessableToken nonce = base::UnguessableToken::Create();
36   struct {
37     absl::optional<CookiePartitionKey> input;
38     bool expected_ret;
39     std::string expected_output;
40   } cases[] = {
41       // No partition key
42       {absl::nullopt, true, kEmptyCookiePartitionKey},
43       // Partition key present
44       {CookiePartitionKey::FromURLForTesting(GURL("https://toplevelsite.com")),
45        true, "https://toplevelsite.com"},
46       // Local file URL
47       {CookiePartitionKey::FromURLForTesting(GURL("file:///path/to/file.txt")),
48        true, "file://"},
49       // File URL with host
50       {CookiePartitionKey::FromURLForTesting(
51            GURL("file://toplevelsite.com/path/to/file.pdf")),
52        true, "file://toplevelsite.com"},
53       // Opaque origin
54       {CookiePartitionKey::FromURLForTesting(GURL()), false, ""},
55       // With nonce
56       {CookiePartitionKey::FromNetworkIsolationKey(NetworkIsolationKey(
57            SchemefulSite(GURL("https://toplevelsite.com")),
58            SchemefulSite(GURL("https://cookiesite.com")), nonce)),
59        false, ""},
60       // Invalid partition key
61       {absl::make_optional(
62            CookiePartitionKey::FromURLForTesting(GURL("abc123foobar!!"))),
63        false, ""},
64   };
65 
66   for (const auto& tc : cases) {
67     std::string got;
68     if (PartitionedCookiesEnabled()) {
69       EXPECT_EQ(tc.expected_ret, CookiePartitionKey::Serialize(tc.input, got));
70       EXPECT_EQ(tc.expected_output, got);
71     } else {
72       // Serialize should only return true for unpartitioned cookies if the
73       // feature is disabled.
74       EXPECT_NE(tc.input.has_value(),
75                 CookiePartitionKey::Serialize(tc.input, got));
76     }
77   }
78 }
79 
TEST_P(CookiePartitionKeyTest,Deserialization)80 TEST_P(CookiePartitionKeyTest, Deserialization) {
81   struct {
82     std::string input;
83     bool expected_ret;
84     absl::optional<CookiePartitionKey> expected_output;
85   } cases[] = {
86       {kEmptyCookiePartitionKey, true, absl::nullopt},
87       {"https://toplevelsite.com", true,
88        CookiePartitionKey::FromURLForTesting(GURL("https://toplevelsite.com"))},
89       {"abc123foobar!!", false, absl::nullopt},
90   };
91 
92   for (const auto& tc : cases) {
93     absl::optional<CookiePartitionKey> got;
94     if (PartitionedCookiesEnabled()) {
95       EXPECT_EQ(tc.expected_ret,
96                 CookiePartitionKey::Deserialize(tc.input, got));
97       if (tc.expected_output.has_value()) {
98         EXPECT_TRUE(got.has_value());
99         EXPECT_EQ(tc.expected_output.value(), got.value());
100       } else {
101         EXPECT_FALSE(got.has_value());
102       }
103     } else {
104       // Deserialize should only return true for unpartitioned cookies if the
105       // feature is disabled.
106       EXPECT_EQ(tc.input == kEmptyCookiePartitionKey,
107                 CookiePartitionKey::Deserialize(tc.input, got));
108     }
109   }
110 }
111 
TEST_P(CookiePartitionKeyTest,FromNetworkIsolationKey)112 TEST_P(CookiePartitionKeyTest, FromNetworkIsolationKey) {
113   const SchemefulSite kTopLevelSite =
114       SchemefulSite(GURL("https://toplevelsite.com"));
115   const SchemefulSite kCookieSite =
116       SchemefulSite(GURL("https://cookiesite.com"));
117   const base::UnguessableToken kNonce = base::UnguessableToken::Create();
118 
119   struct TestCase {
120     const std::string desc;
121     const NetworkIsolationKey network_isolation_key;
122     const absl::optional<CookiePartitionKey> expected;
123   } test_cases[] = {
124       {
125           "Empty",
126           NetworkIsolationKey(),
127           absl::nullopt,
128       },
129       {
130           "WithTopLevelSite",
131           NetworkIsolationKey(kTopLevelSite, kCookieSite),
132           CookiePartitionKey::FromURLForTesting(kTopLevelSite.GetURL()),
133       },
134       {
135           "WithNonce",
136           NetworkIsolationKey(kTopLevelSite, kCookieSite, kNonce),
137           CookiePartitionKey::FromURLForTesting(kCookieSite.GetURL(), kNonce),
138       },
139   };
140 
141   for (const auto& test_case : test_cases) {
142     SCOPED_TRACE(test_case.desc);
143 
144     base::test::ScopedFeatureList feature_list;
145     std::vector<base::test::FeatureRef> enabled_features;
146     std::vector<base::test::FeatureRef> disabled_features;
147     if (PartitionedCookiesEnabled()) {
148       enabled_features.push_back(features::kPartitionedCookies);
149     } else {
150       disabled_features.push_back(features::kPartitionedCookies);
151     }
152     feature_list.InitWithFeatures(enabled_features, disabled_features);
153 
154     absl::optional<CookiePartitionKey> got =
155         CookiePartitionKey::FromNetworkIsolationKey(
156             test_case.network_isolation_key);
157 
158     if (PartitionedCookiesEnabled()) {
159       EXPECT_EQ(test_case.expected, got);
160       if (got) {
161         EXPECT_EQ(test_case.network_isolation_key.GetNonce(), got->nonce());
162       }
163     } else {
164       EXPECT_FALSE(got);
165     }
166   }
167 }
168 
TEST_P(CookiePartitionKeyTest,FromWire)169 TEST_P(CookiePartitionKeyTest, FromWire) {
170   struct TestCase {
171     const GURL url;
172     const absl::optional<base::UnguessableToken> nonce;
173   } test_cases[] = {
174       {GURL("https://foo.com"), absl::nullopt},
175       {GURL(), absl::nullopt},
176       {GURL("https://foo.com"),
177        absl::make_optional(base::UnguessableToken::Create())},
178   };
179 
180   for (const auto& test_case : test_cases) {
181     auto want =
182         CookiePartitionKey::FromURLForTesting(test_case.url, test_case.nonce);
183     auto got = CookiePartitionKey::FromWire(want.site(), want.nonce());
184     EXPECT_EQ(want, got);
185     EXPECT_FALSE(got.from_script());
186   }
187 }
188 
TEST_P(CookiePartitionKeyTest,FromStorageKeyComponents)189 TEST_P(CookiePartitionKeyTest, FromStorageKeyComponents) {
190   struct TestCase {
191     const GURL url;
192     const absl::optional<base::UnguessableToken> nonce = absl::nullopt;
193   } test_cases[] = {
194       {GURL("https://foo.com")},
195       {GURL()},
196       {GURL("https://foo.com"), base::UnguessableToken::Create()},
197   };
198 
199   for (const auto& test_case : test_cases) {
200     auto want =
201         CookiePartitionKey::FromURLForTesting(test_case.url, test_case.nonce);
202     absl::optional<CookiePartitionKey> got =
203         CookiePartitionKey::FromStorageKeyComponents(want.site(), want.nonce());
204     if (PartitionedCookiesEnabled()) {
205       EXPECT_EQ(got, want);
206     } else {
207       EXPECT_FALSE(got);
208     }
209   }
210 }
211 
TEST_P(CookiePartitionKeyTest,FromScript)212 TEST_P(CookiePartitionKeyTest, FromScript) {
213   auto key = CookiePartitionKey::FromScript();
214   EXPECT_TRUE(key);
215   EXPECT_TRUE(key->from_script());
216   EXPECT_TRUE(key->site().opaque());
217 
218   auto key2 = CookiePartitionKey::FromScript();
219   EXPECT_TRUE(key2);
220   EXPECT_TRUE(key2->from_script());
221   EXPECT_TRUE(key2->site().opaque());
222 
223   // The keys should not be equal because they get created with different opaque
224   // sites. Test both the '==' and '!=' operators here.
225   EXPECT_FALSE(key == key2);
226   EXPECT_TRUE(key != key2);
227 }
228 
TEST_P(CookiePartitionKeyTest,IsSerializeable)229 TEST_P(CookiePartitionKeyTest, IsSerializeable) {
230   EXPECT_FALSE(CookiePartitionKey::FromURLForTesting(GURL()).IsSerializeable());
231   EXPECT_EQ(PartitionedCookiesEnabled(), CookiePartitionKey::FromURLForTesting(
232                                              GURL("https://www.example.com"))
233                                              .IsSerializeable());
234 }
235 
TEST_P(CookiePartitionKeyTest,Equality)236 TEST_P(CookiePartitionKeyTest, Equality) {
237   // Same eTLD+1 but different scheme are not equal.
238   EXPECT_NE(CookiePartitionKey::FromURLForTesting(GURL("https://foo.com")),
239             CookiePartitionKey::FromURLForTesting(GURL("http://foo.com")));
240 
241   // Different subdomains of the same site are equal.
242   EXPECT_EQ(CookiePartitionKey::FromURLForTesting(GURL("https://a.foo.com")),
243             CookiePartitionKey::FromURLForTesting(GURL("https://b.foo.com")));
244 }
245 
TEST_P(CookiePartitionKeyTest,Equality_WithNonce)246 TEST_P(CookiePartitionKeyTest, Equality_WithNonce) {
247   SchemefulSite top_level_site =
248       SchemefulSite(GURL("https://toplevelsite.com"));
249   SchemefulSite frame_site = SchemefulSite(GURL("https://cookiesite.com"));
250   base::UnguessableToken nonce1 = base::UnguessableToken::Create();
251   base::UnguessableToken nonce2 = base::UnguessableToken::Create();
252   EXPECT_NE(nonce1, nonce2);
253   auto key1 = CookiePartitionKey::FromNetworkIsolationKey(
254       NetworkIsolationKey(top_level_site, frame_site, nonce1));
255   EXPECT_EQ(PartitionedCookiesEnabled(), key1.has_value());
256   if (!PartitionedCookiesEnabled()) {
257     return;
258   }
259 
260   auto key2 = CookiePartitionKey::FromNetworkIsolationKey(
261       NetworkIsolationKey(top_level_site, frame_site, nonce2));
262   EXPECT_TRUE(key1.has_value() && key2.has_value());
263   EXPECT_NE(key1, key2);
264 
265   auto key3 = CookiePartitionKey::FromNetworkIsolationKey(
266       NetworkIsolationKey(top_level_site, frame_site, nonce1));
267   EXPECT_EQ(key1, key3);
268 
269   auto unnonced_key = CookiePartitionKey::FromNetworkIsolationKey(
270       NetworkIsolationKey(top_level_site, frame_site));
271   EXPECT_NE(key1, unnonced_key);
272 }
273 
TEST_P(CookiePartitionKeyTest,Localhost)274 TEST_P(CookiePartitionKeyTest, Localhost) {
275   SchemefulSite top_level_site(GURL("https://localhost:8000"));
276 
277   auto key = CookiePartitionKey::FromNetworkIsolationKey(
278       NetworkIsolationKey(top_level_site, top_level_site));
279   EXPECT_EQ(PartitionedCookiesEnabled(), key.has_value());
280 
281   SchemefulSite frame_site(GURL("https://cookiesite.com"));
282   key = CookiePartitionKey::FromNetworkIsolationKey(
283       NetworkIsolationKey(top_level_site, frame_site));
284   EXPECT_EQ(PartitionedCookiesEnabled(), key.has_value());
285 }
286 
287 // Test that creating nonced partition keys works with both types of
288 // NetworkIsolationKey modes. See https://crbug.com/1442260.
TEST_P(CookiePartitionKeyTest,NetworkIsolationKeyMode)289 TEST_P(CookiePartitionKeyTest, NetworkIsolationKeyMode) {
290   if (!PartitionedCookiesEnabled()) {
291     return;
292   }
293 
294   const net::SchemefulSite kTopFrameSite(GURL("https://a.com"));
295   const net::SchemefulSite kFrameSite(GURL("https://b.com"));
296   const auto kNonce = base::UnguessableToken::Create();
297 
298   {  // Frame site mode.
299     base::test::ScopedFeatureList feature_list;
300     feature_list.InitWithFeatures(
301         {}, {net::features::kEnableCrossSiteFlagNetworkIsolationKey});
302 
303     const auto key = CookiePartitionKey::FromNetworkIsolationKey(
304         NetworkIsolationKey(kTopFrameSite, kFrameSite, kNonce));
305     EXPECT_TRUE(key);
306     EXPECT_EQ(key->site(), kFrameSite);
307     EXPECT_EQ(key->nonce().value(), kNonce);
308   }
309 
310   {  // Cross-site flag mode.
311     base::test::ScopedFeatureList feature_list;
312     feature_list.InitWithFeatures(
313         {net::features::kEnableCrossSiteFlagNetworkIsolationKey}, {});
314 
315     const auto key = CookiePartitionKey::FromNetworkIsolationKey(
316         NetworkIsolationKey(kTopFrameSite, kFrameSite, kNonce));
317     EXPECT_TRUE(key);
318     EXPECT_EQ(key->site(), kFrameSite);
319     EXPECT_EQ(key->nonce().value(), kNonce);
320   }
321 }
322 
323 }  // namespace net
324