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