1 // Copyright 2020 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/base/schemeful_site.h"
6
7 #include "base/test/metrics/histogram_tester.h"
8 #include "net/base/url_util.h"
9 #include "testing/gmock/include/gmock/gmock-matchers.h"
10 #include "testing/gtest/include/gtest/gtest.h"
11 #include "url/gurl.h"
12 #include "url/origin.h"
13 #include "url/url_util.h"
14
15 namespace net {
16
TEST(SchemefulSiteTest,DifferentOriginSameRegisterableDomain)17 TEST(SchemefulSiteTest, DifferentOriginSameRegisterableDomain) {
18 // List of origins which should all share a schemeful site.
19 url::Origin kTestOrigins[] = {
20 url::Origin::Create(GURL("http://a.foo.test")),
21 url::Origin::Create(GURL("http://b.foo.test")),
22 url::Origin::Create(GURL("http://foo.test")),
23 url::Origin::Create(GURL("http://a.b.foo.test"))};
24
25 for (const auto& origin_a : kTestOrigins) {
26 for (const auto& origin_b : kTestOrigins) {
27 EXPECT_EQ(SchemefulSite(origin_a), SchemefulSite(origin_b));
28 }
29 }
30 }
31
TEST(SchemefulSiteTest,Operators)32 TEST(SchemefulSiteTest, Operators) {
33 // Create a list of origins that should all have different schemeful sites.
34 // These are in ascending order.
35 url::Origin kTestOrigins[] = {
36 url::Origin::Create(GURL("data:text/html,<body>Hello World</body>")),
37 url::Origin::Create(GURL("file://foo")),
38 url::Origin::Create(GURL("http://a.bar.test")),
39 url::Origin::Create(GURL("http://c.test")),
40 url::Origin::Create(GURL("http://d.test")),
41 url::Origin::Create(GURL("http://a.foo.test")),
42 url::Origin::Create(GURL("https://a.bar.test")),
43 url::Origin::Create(GURL("https://c.test")),
44 url::Origin::Create(GURL("https://d.test")),
45 url::Origin::Create(GURL("https://a.foo.test"))};
46
47 // Compare each origin to every other origin and ensure the operators work as
48 // expected.
49 for (size_t first = 0; first < std::size(kTestOrigins); ++first) {
50 SchemefulSite site1 = SchemefulSite(kTestOrigins[first]);
51 SCOPED_TRACE(site1.GetDebugString());
52
53 EXPECT_EQ(site1, site1);
54 EXPECT_FALSE(site1 < site1);
55
56 // Check the operators work on copies.
57 SchemefulSite site1_copy = site1;
58 EXPECT_EQ(site1, site1_copy);
59 EXPECT_FALSE(site1 < site1_copy);
60
61 for (size_t second = first + 1; second < std::size(kTestOrigins);
62 ++second) {
63 SchemefulSite site2 = SchemefulSite(kTestOrigins[second]);
64 SCOPED_TRACE(site2.GetDebugString());
65
66 EXPECT_TRUE(site1 < site2);
67 EXPECT_FALSE(site2 < site1);
68 EXPECT_FALSE(site1 == site2);
69 EXPECT_FALSE(site2 == site1);
70 }
71 }
72 }
73
TEST(SchemefulSiteTest,SchemeUsed)74 TEST(SchemefulSiteTest, SchemeUsed) {
75 url::Origin origin_a = url::Origin::Create(GURL("https://foo.test"));
76 url::Origin origin_b = url::Origin::Create(GURL("http://foo.test"));
77 EXPECT_NE(SchemefulSite(origin_a), SchemefulSite(origin_b));
78 }
79
TEST(SchemefulSiteTest,PortIgnored)80 TEST(SchemefulSiteTest, PortIgnored) {
81 // Both origins are non-opaque.
82 url::Origin origin_a = url::Origin::Create(GURL("https://foo.test:80"));
83 url::Origin origin_b = url::Origin::Create(GURL("https://foo.test:2395"));
84
85 EXPECT_EQ(SchemefulSite(origin_a), SchemefulSite(origin_b));
86 }
87
TEST(SchemefulSiteTest,TopLevelDomainsNotModified)88 TEST(SchemefulSiteTest, TopLevelDomainsNotModified) {
89 url::Origin origin_tld = url::Origin::Create(GURL("https://com"));
90 EXPECT_EQ(url::Origin::Create(GURL("https://com")),
91 SchemefulSite(origin_tld).GetInternalOriginForTesting());
92
93 // Unknown TLD's should not be modified.
94 url::Origin origin_tld_unknown =
95 url::Origin::Create(GURL("https://bar:1234"));
96 EXPECT_EQ(url::Origin::Create(GURL("https://bar")),
97 SchemefulSite(origin_tld_unknown).GetInternalOriginForTesting());
98
99 // Check for two-part TLDs.
100 url::Origin origin_two_part_tld = url::Origin::Create(GURL("http://a.co.uk"));
101 EXPECT_EQ(url::Origin::Create(GURL("http://a.co.uk")),
102 SchemefulSite(origin_two_part_tld).GetInternalOriginForTesting());
103 }
104
TEST(SchemefulSiteTest,NonStandardScheme)105 TEST(SchemefulSiteTest, NonStandardScheme) {
106 url::ScopedSchemeRegistryForTests scoped_registry;
107 url::AddStandardScheme("foo", url::SCHEME_WITH_HOST);
108 url::Origin origin = url::Origin::Create(GURL("foo://a.b.test"));
109 EXPECT_FALSE(origin.opaque());
110
111 // We should not use registerable domains for non-standard schemes, even if
112 // one exists for the host.
113 EXPECT_EQ(url::Origin::Create(GURL("foo://a.b.test")),
114 SchemefulSite(origin).GetInternalOriginForTesting());
115 }
116
TEST(SchemefulSiteTest,IPBasedOriginsRemovePort)117 TEST(SchemefulSiteTest, IPBasedOriginsRemovePort) {
118 // IPv4 and IPv6 origins should not be modified, except for removing their
119 // ports.
120 url::Origin origin_ipv4_a =
121 url::Origin::Create(GURL("http://127.0.0.1:1234"));
122 url::Origin origin_ipv4_b = url::Origin::Create(GURL("http://127.0.0.1"));
123 EXPECT_EQ(url::Origin::Create(GURL("http://127.0.0.1")),
124 SchemefulSite(origin_ipv4_a).GetInternalOriginForTesting());
125 EXPECT_EQ(SchemefulSite(origin_ipv4_a), SchemefulSite(origin_ipv4_b));
126
127 url::Origin origin_ipv6 = url::Origin::Create(GURL("https://[::1]"));
128 EXPECT_EQ(url::Origin::Create(GURL("https://[::1]")),
129 SchemefulSite(origin_ipv6).GetInternalOriginForTesting());
130 }
131
TEST(SchemefulSiteTest,LocalhostOriginsRemovePort)132 TEST(SchemefulSiteTest, LocalhostOriginsRemovePort) {
133 // Localhost origins should not be modified, except for removing their ports.
134 url::Origin localhost_http =
135 url::Origin::Create(GURL("http://localhost:1234"));
136 EXPECT_EQ(url::Origin::Create(GURL("http://localhost")),
137 SchemefulSite(localhost_http).GetInternalOriginForTesting());
138
139 url::Origin localhost_https =
140 url::Origin::Create(GURL("https://localhost:1234"));
141 EXPECT_EQ(url::Origin::Create(GURL("https://localhost")),
142 SchemefulSite(localhost_https).GetInternalOriginForTesting());
143 }
144
TEST(SchemefulSiteTest,OpaqueOrigins)145 TEST(SchemefulSiteTest, OpaqueOrigins) {
146 url::Origin opaque_origin_a =
147 url::Origin::Create(GURL("data:text/html,<body>Hello World</body>"));
148
149 // The schemeful site of an opaque origin should always equal other schemeful
150 // site instances of the same origin.
151 EXPECT_EQ(SchemefulSite(opaque_origin_a), SchemefulSite(opaque_origin_a));
152
153 url::Origin opaque_origin_b =
154 url::Origin::Create(GURL("data:text/html,<body>Hello World</body>"));
155
156 // Two different opaque origins should never have the same SchemefulSite.
157 EXPECT_NE(SchemefulSite(opaque_origin_a), SchemefulSite(opaque_origin_b));
158 }
159
TEST(SchemefulSiteTest,FileOriginWithoutHostname)160 TEST(SchemefulSiteTest, FileOriginWithoutHostname) {
161 SchemefulSite site1(url::Origin::Create(GURL("file:///")));
162 SchemefulSite site2(url::Origin::Create(GURL("file:///path/")));
163
164 EXPECT_EQ(site1, site2);
165 EXPECT_TRUE(site1.GetInternalOriginForTesting().host().empty());
166 }
167
TEST(SchemefulSiteTest,SchemeWithNetworkHost)168 TEST(SchemefulSiteTest, SchemeWithNetworkHost) {
169 url::ScopedSchemeRegistryForTests scheme_registry;
170 AddStandardScheme("network", url::SCHEME_WITH_HOST_PORT_AND_USER_INFORMATION);
171 AddStandardScheme("non-network", url::SCHEME_WITH_HOST);
172
173 ASSERT_TRUE(IsStandardSchemeWithNetworkHost("network"));
174 ASSERT_FALSE(IsStandardSchemeWithNetworkHost("non-network"));
175
176 std::optional<SchemefulSite> network_host_site =
177 SchemefulSite::CreateIfHasRegisterableDomain(
178 url::Origin::Create(GURL("network://site.example.test:1337")));
179 EXPECT_TRUE(network_host_site.has_value());
180 EXPECT_EQ("network",
181 network_host_site->GetInternalOriginForTesting().scheme());
182 EXPECT_EQ("example.test",
183 network_host_site->GetInternalOriginForTesting().host());
184
185 std::optional<SchemefulSite> non_network_host_site_null =
186 SchemefulSite::CreateIfHasRegisterableDomain(
187 url::Origin::Create(GURL("non-network://site.example.test")));
188 EXPECT_FALSE(non_network_host_site_null.has_value());
189 SchemefulSite non_network_host_site(GURL("non-network://site.example.test"));
190 EXPECT_EQ("non-network",
191 non_network_host_site.GetInternalOriginForTesting().scheme());
192 // The host is used as-is, without attempting to get a registrable domain.
193 EXPECT_EQ("site.example.test",
194 non_network_host_site.GetInternalOriginForTesting().host());
195 }
196
TEST(SchemefulSiteTest,FileSchemeHasRegistrableDomain)197 TEST(SchemefulSiteTest, FileSchemeHasRegistrableDomain) {
198 // Test file origin without host.
199 url::Origin origin_file =
200 url::Origin::Create(GURL("file:///dir1/dir2/file.txt"));
201 EXPECT_TRUE(origin_file.host().empty());
202 SchemefulSite site_file(origin_file);
203 EXPECT_EQ(url::Origin::Create(GURL("file:///")),
204 site_file.GetInternalOriginForTesting());
205
206 // Test file origin with host (with registrable domain).
207 url::Origin origin_file_with_host =
208 url::Origin::Create(GURL("file://host.example.test/file"));
209 ASSERT_EQ("host.example.test", origin_file_with_host.host());
210 SchemefulSite site_file_with_host(origin_file_with_host);
211 EXPECT_EQ(url::Origin::Create(GURL("file://example.test")),
212 site_file_with_host.GetInternalOriginForTesting());
213
214 // Test file origin with host same as registrable domain.
215 url::Origin origin_file_registrable_domain =
216 url::Origin::Create(GURL("file://example.test/file"));
217 ASSERT_EQ("example.test", origin_file_registrable_domain.host());
218 SchemefulSite site_file_registrable_domain(origin_file_registrable_domain);
219 EXPECT_EQ(url::Origin::Create(GURL("file://example.test")),
220 site_file_registrable_domain.GetInternalOriginForTesting());
221
222 EXPECT_NE(site_file, site_file_with_host);
223 EXPECT_NE(site_file, site_file_registrable_domain);
224 EXPECT_EQ(site_file_with_host, site_file_registrable_domain);
225 }
226
TEST(SchemefulSiteTest,SerializationConsistent)227 TEST(SchemefulSiteTest, SerializationConsistent) {
228 url::ScopedSchemeRegistryForTests scoped_registry;
229 url::AddStandardScheme("chrome", url::SCHEME_WITH_HOST);
230
231 // List of origins which should all share a schemeful site.
232 SchemefulSite kTestSites[] = {
233 SchemefulSite(url::Origin::Create(GURL("http://a.foo.test"))),
234 SchemefulSite(url::Origin::Create(GURL("https://b.foo.test"))),
235 SchemefulSite(url::Origin::Create(GURL("http://b.foo.test"))),
236 SchemefulSite(url::Origin::Create(GURL("http://a.b.foo.test"))),
237 SchemefulSite(url::Origin::Create(GURL("chrome://a.b.test")))};
238
239 for (const auto& site : kTestSites) {
240 SCOPED_TRACE(site.GetDebugString());
241 EXPECT_FALSE(site.GetInternalOriginForTesting().opaque());
242
243 std::optional<SchemefulSite> deserialized_site =
244 SchemefulSite::Deserialize(site.Serialize());
245 EXPECT_TRUE(deserialized_site);
246 EXPECT_EQ(site, deserialized_site);
247 }
248 }
249
TEST(SchemefulSiteTest,SerializationFileSiteWithHost)250 TEST(SchemefulSiteTest, SerializationFileSiteWithHost) {
251 const struct {
252 SchemefulSite site;
253 std::string expected;
254 } kTestCases[] = {
255 {SchemefulSite(GURL("file:///etc/passwd")), "file://"},
256 {SchemefulSite(GURL("file://example.com/etc/passwd")),
257 "file://example.com"},
258 {SchemefulSite(GURL("file://example.com")), "file://example.com"},
259 };
260
261 for (const auto& test_case : kTestCases) {
262 SCOPED_TRACE(test_case.site.GetDebugString());
263 std::string serialized_site = test_case.site.SerializeFileSiteWithHost();
264 EXPECT_EQ(test_case.expected, serialized_site);
265 std::optional<SchemefulSite> deserialized_site =
266 SchemefulSite::Deserialize(serialized_site);
267 EXPECT_TRUE(deserialized_site);
268 EXPECT_EQ(test_case.site, deserialized_site);
269 }
270 }
271
TEST(SchemefulSiteTest,FileURLWithHostEquality)272 TEST(SchemefulSiteTest, FileURLWithHostEquality) {
273 // Two file URLs with different hosts should result in unequal SchemefulSites.
274 SchemefulSite site1(GURL("file://foo/some/path.txt"));
275 SchemefulSite site2(GURL("file://bar/some/path.txt"));
276 EXPECT_NE(site1, site2);
277
278 // Two file URLs with the same host should result in equal SchemefulSites.
279 SchemefulSite site3(GURL("file://foo/another/path.pdf"));
280 EXPECT_EQ(site1, site3);
281 }
282
TEST(SchemefulSiteTest,OpaqueSerialization)283 TEST(SchemefulSiteTest, OpaqueSerialization) {
284 // List of origins which should all share a schemeful site.
285 SchemefulSite kTestSites[] = {
286 SchemefulSite(), SchemefulSite(url::Origin()),
287 SchemefulSite(GURL("data:text/html,<body>Hello World</body>"))};
288
289 for (auto& site : kTestSites) {
290 std::optional<SchemefulSite> deserialized_site =
291 SchemefulSite::DeserializeWithNonce(*site.SerializeWithNonce());
292 EXPECT_TRUE(deserialized_site);
293 EXPECT_EQ(site, *deserialized_site);
294 }
295 }
296
TEST(SchemefulSiteTest,FromWire)297 TEST(SchemefulSiteTest, FromWire) {
298 SchemefulSite out;
299
300 // Opaque origin.
301 EXPECT_TRUE(SchemefulSite::FromWire(url::Origin(), &out));
302 EXPECT_TRUE(out.opaque());
303
304 // Valid origin.
305 EXPECT_TRUE(SchemefulSite::FromWire(
306 url::Origin::Create(GURL("https://example.test")), &out));
307 EXPECT_EQ(SchemefulSite(url::Origin::Create(GURL("https://example.test"))),
308 out);
309
310 // Invalid origin (not a registrable domain).
311 EXPECT_FALSE(SchemefulSite::FromWire(
312 url::Origin::Create(GURL("https://sub.example.test")), &out));
313
314 // Invalid origin (non-default port).
315 EXPECT_FALSE(SchemefulSite::FromWire(
316 url::Origin::Create(GURL("https://example.test:1337")), &out));
317 }
318
TEST(SchemefulSiteTest,CreateIfHasRegisterableDomain)319 TEST(SchemefulSiteTest, CreateIfHasRegisterableDomain) {
320 for (const auto& site : std::initializer_list<std::string>{
321 "http://a.bar.test",
322 "http://c.test",
323 "http://a.foo.test",
324 "https://a.bar.test",
325 "https://c.test",
326 "https://a.foo.test",
327 }) {
328 url::Origin origin = url::Origin::Create(GURL(site));
329 EXPECT_THAT(SchemefulSite::CreateIfHasRegisterableDomain(origin),
330 testing::Optional(SchemefulSite(origin)))
331 << "site = \"" << site << "\"";
332 }
333
334 for (const auto& site : std::initializer_list<std::string>{
335 "data:text/html,<body>Hello World</body>",
336 "file:///",
337 "file://foo",
338 "http://127.0.0.1:1234",
339 "https://127.0.0.1:1234",
340 }) {
341 url::Origin origin = url::Origin::Create(GURL(site));
342 EXPECT_EQ(SchemefulSite::CreateIfHasRegisterableDomain(origin),
343 std::nullopt)
344 << "site = \"" << site << "\"";
345 }
346 }
347
TEST(SchemefulSiteTest,ConvertWebSocketToHttp)348 TEST(SchemefulSiteTest, ConvertWebSocketToHttp) {
349 SchemefulSite ws_site(url::Origin::Create(GURL("ws://site.example.test")));
350 SchemefulSite http_site(
351 url::Origin::Create(GURL("http://site.example.test")));
352 SchemefulSite wss_site(url::Origin::Create(GURL("wss://site.example.test")));
353 SchemefulSite https_site(
354 url::Origin::Create(GURL("https://site.example.test")));
355
356 ASSERT_NE(ws_site, wss_site);
357 ASSERT_NE(ws_site, http_site);
358 ASSERT_NE(ws_site, https_site);
359 ASSERT_NE(wss_site, http_site);
360 ASSERT_NE(wss_site, https_site);
361
362 ws_site.ConvertWebSocketToHttp();
363 wss_site.ConvertWebSocketToHttp();
364
365 EXPECT_EQ(ws_site, http_site);
366 EXPECT_EQ(wss_site, https_site);
367
368 // Does not change non-WebSocket sites.
369 SchemefulSite http_site_copy(http_site);
370 http_site_copy.ConvertWebSocketToHttp();
371 EXPECT_EQ(http_site, http_site_copy);
372 EXPECT_EQ(url::kHttpScheme,
373 http_site_copy.GetInternalOriginForTesting().scheme());
374
375 SchemefulSite file_site(url::Origin::Create(GURL("file:///")));
376 file_site.ConvertWebSocketToHttp();
377 EXPECT_EQ(url::kFileScheme, file_site.GetInternalOriginForTesting().scheme());
378 }
379
TEST(SchemefulSiteTest,GetGURL)380 TEST(SchemefulSiteTest, GetGURL) {
381 struct {
382 url::Origin origin;
383 GURL wantGURL;
384 } kTestCases[] = {
385 {
386 url::Origin::Create(GURL("data:text/html,<body>Hello World</body>")),
387 GURL(),
388 },
389 {url::Origin::Create(GURL("file://foo")), GURL("file:///")},
390 {url::Origin::Create(GURL("http://a.bar.test")), GURL("http://bar.test")},
391 {url::Origin::Create(GURL("http://c.test")), GURL("http://c.test")},
392 {url::Origin::Create(GURL("http://c.test:8000")), GURL("http://c.test")},
393 {
394 url::Origin::Create(GURL("https://a.bar.test")),
395 GURL("https://bar.test"),
396 },
397 {
398 url::Origin::Create(GURL("https://c.test")),
399 GURL("https://c.test"),
400 },
401 {
402 url::Origin::Create(GURL("https://c.test:1337")),
403 GURL("https://c.test"),
404 },
405 };
406
407 for (const auto& testcase : kTestCases) {
408 SchemefulSite site(testcase.origin);
409 EXPECT_EQ(site.GetURL(), testcase.wantGURL);
410 }
411 }
412
TEST(SchemefulSiteTest,InternalValue)413 TEST(SchemefulSiteTest, InternalValue) {
414 url::Origin origin = url::Origin::Create(GURL("https://example.com"));
415 SchemefulSite site(origin);
416 EXPECT_EQ(site.internal_value(), origin);
417 url::Origin opaque_origin;
418 SchemefulSite opaque_site(opaque_origin);
419 EXPECT_EQ(opaque_site.internal_value(), opaque_origin);
420 }
421
422 } // namespace net
423