1 // Copyright 2015 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 <stddef.h>
6 #include <stdint.h>
7
8 #include "base/memory/raw_ptr.h"
9 #include "testing/gmock/include/gmock/gmock.h"
10 #include "testing/gtest/include/gtest/gtest.h"
11 #include "url/gurl.h"
12 #include "url/origin.h"
13 #include "url/origin_abstract_tests.h"
14 #include "url/url_util.h"
15
16 namespace url {
17
18 class OriginTest : public ::testing::Test {
19 public:
SetUp()20 void SetUp() override {
21 // Add two schemes which are local but nonstandard.
22 AddLocalScheme("local-but-nonstandard");
23 AddLocalScheme("also-local-but-nonstandard");
24
25 // Add a scheme that's both local and standard.
26 AddStandardScheme("local-and-standard", SchemeType::SCHEME_WITH_HOST);
27 AddLocalScheme("local-and-standard");
28
29 // Add a scheme that's standard but no-access. We still want these to
30 // form valid SchemeHostPorts, even though they always commit as opaque
31 // origins, so that they can represent the source of the resource even if
32 // it's not committable as a non-opaque origin.
33 AddStandardScheme("standard-but-noaccess", SchemeType::SCHEME_WITH_HOST);
34 AddNoAccessScheme("standard-but-noaccess");
35 }
36
DoEqualityComparisons(const url::Origin & a,const url::Origin & b,bool should_compare_equal)37 ::testing::AssertionResult DoEqualityComparisons(const url::Origin& a,
38 const url::Origin& b,
39 bool should_compare_equal) {
40 ::testing::AssertionResult failure = ::testing::AssertionFailure();
41 failure << "DoEqualityComparisons failure. Expecting "
42 << (should_compare_equal ? "equality" : "inequality")
43 << " between:\n a\n Which is: " << a
44 << "\n b\n Which is: " << b << "\nThe following check failed: ";
45 if (a.IsSameOriginWith(b) != should_compare_equal)
46 return failure << "a.IsSameOriginWith(b)";
47 if (b.IsSameOriginWith(a) != should_compare_equal)
48 return failure << "b.IsSameOriginWith(a)";
49 if ((a == b) != should_compare_equal)
50 return failure << "(a == b)";
51 if ((b == a) != should_compare_equal)
52 return failure << "(b == a)";
53 if ((b != a) != !should_compare_equal)
54 return failure << "(b != a)";
55 if ((a != b) != !should_compare_equal)
56 return failure << "(a != b)";
57 return ::testing::AssertionSuccess();
58 }
59
HasNonceTokenBeenInitialized(const url::Origin & origin)60 bool HasNonceTokenBeenInitialized(const url::Origin& origin) {
61 EXPECT_TRUE(origin.opaque());
62 // Avoid calling nonce_.token() here, to not trigger lazy initialization.
63 return !origin.nonce_->token_.is_empty();
64 }
65
CreateNonce()66 Origin::Nonce CreateNonce() { return Origin::Nonce(); }
67
CreateNonce(base::UnguessableToken nonce)68 Origin::Nonce CreateNonce(base::UnguessableToken nonce) {
69 return Origin::Nonce(nonce);
70 }
71
GetNonce(const Origin & origin)72 const base::UnguessableToken* GetNonce(const Origin& origin) {
73 return origin.GetNonceForSerialization();
74 }
75
76 // Wrappers around url::Origin methods to expose it to tests.
77
UnsafelyCreateOpaqueOriginWithoutNormalization(base::StringPiece precursor_scheme,base::StringPiece precursor_host,uint16_t precursor_port,const Origin::Nonce & nonce)78 absl::optional<Origin> UnsafelyCreateOpaqueOriginWithoutNormalization(
79 base::StringPiece precursor_scheme,
80 base::StringPiece precursor_host,
81 uint16_t precursor_port,
82 const Origin::Nonce& nonce) {
83 return Origin::UnsafelyCreateOpaqueOriginWithoutNormalization(
84 precursor_scheme, precursor_host, precursor_port, nonce);
85 }
86
SerializeWithNonce(const Origin & origin)87 absl::optional<std::string> SerializeWithNonce(const Origin& origin) {
88 return origin.SerializeWithNonce();
89 }
90
SerializeWithNonceAndInitIfNeeded(Origin & origin)91 absl::optional<std::string> SerializeWithNonceAndInitIfNeeded(
92 Origin& origin) {
93 return origin.SerializeWithNonceAndInitIfNeeded();
94 }
95
Deserialize(const std::string & value)96 absl::optional<Origin> Deserialize(const std::string& value) {
97 return Origin::Deserialize(value);
98 }
99
100 private:
101 ScopedSchemeRegistryForTests scoped_registry_;
102 };
103
TEST_F(OriginTest,OpaqueOriginComparison)104 TEST_F(OriginTest, OpaqueOriginComparison) {
105 // A default-constructed Origin should should be cross origin to everything
106 // but itself.
107 url::Origin opaque_a, opaque_b;
108 EXPECT_TRUE(opaque_a.opaque());
109 EXPECT_EQ("", opaque_a.scheme());
110 EXPECT_EQ("", opaque_a.host());
111 EXPECT_EQ(0, opaque_a.port());
112 EXPECT_EQ(SchemeHostPort(), opaque_a.GetTupleOrPrecursorTupleIfOpaque());
113 EXPECT_FALSE(opaque_a.GetTupleOrPrecursorTupleIfOpaque().IsValid());
114
115 EXPECT_TRUE(opaque_b.opaque());
116 EXPECT_EQ("", opaque_b.scheme());
117 EXPECT_EQ("", opaque_b.host());
118 EXPECT_EQ(0, opaque_b.port());
119 EXPECT_EQ(SchemeHostPort(), opaque_b.GetTupleOrPrecursorTupleIfOpaque());
120 EXPECT_FALSE(opaque_b.GetTupleOrPrecursorTupleIfOpaque().IsValid());
121
122 // Two default-constructed Origins should always be cross origin to each
123 // other.
124 EXPECT_TRUE(DoEqualityComparisons(opaque_a, opaque_b, false));
125 EXPECT_TRUE(DoEqualityComparisons(opaque_b, opaque_b, true));
126 EXPECT_TRUE(DoEqualityComparisons(opaque_a, opaque_a, true));
127
128 // The streaming operator should not trigger lazy initialization to the token.
129 std::ostringstream stream;
130 stream << opaque_a;
131 EXPECT_STREQ("null [internally: (nonce TBD) anonymous]",
132 stream.str().c_str());
133 EXPECT_FALSE(HasNonceTokenBeenInitialized(opaque_a));
134
135 // None of the operations thus far should have triggered lazy-generation of
136 // the UnguessableToken. Copying an origin, however, should trigger this.
137 EXPECT_FALSE(HasNonceTokenBeenInitialized(opaque_a));
138 EXPECT_FALSE(HasNonceTokenBeenInitialized(opaque_b));
139 opaque_b = opaque_a;
140
141 EXPECT_TRUE(HasNonceTokenBeenInitialized(opaque_a));
142 EXPECT_TRUE(HasNonceTokenBeenInitialized(opaque_b));
143 EXPECT_TRUE(DoEqualityComparisons(opaque_a, opaque_b, true));
144 EXPECT_TRUE(DoEqualityComparisons(opaque_b, opaque_b, true));
145 EXPECT_TRUE(DoEqualityComparisons(opaque_a, opaque_a, true));
146
147 // Move-initializing to a fresh Origin should restore the lazy initialization.
148 opaque_a = url::Origin();
149 EXPECT_FALSE(HasNonceTokenBeenInitialized(opaque_a));
150 EXPECT_TRUE(HasNonceTokenBeenInitialized(opaque_b));
151 EXPECT_TRUE(DoEqualityComparisons(opaque_a, opaque_b, false));
152 EXPECT_TRUE(DoEqualityComparisons(opaque_b, opaque_b, true));
153 EXPECT_TRUE(DoEqualityComparisons(opaque_a, opaque_a, true));
154
155 // Comparing two opaque Origins with matching SchemeHostPorts should trigger
156 // lazy initialization.
157 EXPECT_FALSE(HasNonceTokenBeenInitialized(opaque_a));
158 EXPECT_TRUE(HasNonceTokenBeenInitialized(opaque_b));
159 bool should_swap = opaque_b < opaque_a;
160 EXPECT_TRUE(HasNonceTokenBeenInitialized(opaque_a));
161 EXPECT_TRUE(HasNonceTokenBeenInitialized(opaque_b));
162
163 if (should_swap)
164 std::swap(opaque_a, opaque_b);
165 EXPECT_LT(opaque_a, opaque_b);
166 EXPECT_FALSE(opaque_b < opaque_a);
167
168 EXPECT_TRUE(DoEqualityComparisons(opaque_a, opaque_b, false));
169 EXPECT_TRUE(DoEqualityComparisons(opaque_b, opaque_b, true));
170 EXPECT_TRUE(DoEqualityComparisons(opaque_a, opaque_a, true));
171
172 EXPECT_LT(opaque_a, url::Origin::Create(GURL("http://www.google.com")));
173 EXPECT_LT(opaque_b, url::Origin::Create(GURL("http://www.google.com")));
174
175 EXPECT_EQ(opaque_b, url::Origin::Resolve(GURL(), opaque_b));
176 EXPECT_EQ(opaque_b, url::Origin::Resolve(GURL("about:blank"), opaque_b));
177 EXPECT_EQ(opaque_b, url::Origin::Resolve(GURL("about:srcdoc"), opaque_b));
178 EXPECT_EQ(opaque_b,
179 url::Origin::Resolve(GURL("about:blank?hello#whee"), opaque_b));
180 }
181
TEST_F(OriginTest,ConstructFromTuple)182 TEST_F(OriginTest, ConstructFromTuple) {
183 struct TestCases {
184 const char* const scheme;
185 const char* const host;
186 const uint16_t port;
187 } cases[] = {
188 {"http", "example.com", 80},
189 {"http", "example.com", 123},
190 {"https", "example.com", 443},
191 };
192
193 for (const auto& test_case : cases) {
194 testing::Message scope_message;
195 scope_message << test_case.scheme << "://" << test_case.host << ":"
196 << test_case.port;
197 SCOPED_TRACE(scope_message);
198 Origin origin = Origin::CreateFromNormalizedTuple(
199 test_case.scheme, test_case.host, test_case.port);
200
201 EXPECT_EQ(test_case.scheme, origin.scheme());
202 EXPECT_EQ(test_case.host, origin.host());
203 EXPECT_EQ(test_case.port, origin.port());
204 }
205 }
206
TEST_F(OriginTest,Serialization)207 TEST_F(OriginTest, Serialization) {
208 struct TestCases {
209 const char* const url;
210 const char* const expected;
211 const char* const expected_log;
212 } cases[] = {
213 {"http://192.168.9.1/", "http://192.168.9.1"},
214 {"http://[2001:db8::1]/", "http://[2001:db8::1]"},
215 {"http://☃.net/", "http://xn--n3h.net"},
216 {"http://example.com/", "http://example.com"},
217 {"http://example.com:123/", "http://example.com:123"},
218 {"https://example.com/", "https://example.com"},
219 {"https://example.com:123/", "https://example.com:123"},
220 {"file:///etc/passwd", "file://", "file:// [internally: file://]"},
221 {"file://example.com/etc/passwd", "file://",
222 "file:// [internally: file://example.com]"},
223 {"data:,", "null", "null [internally: (nonce TBD) anonymous]"},
224 };
225
226 for (const auto& test_case : cases) {
227 SCOPED_TRACE(test_case.url);
228 GURL url(test_case.url);
229 EXPECT_TRUE(url.is_valid());
230 Origin origin = Origin::Create(url);
231 std::string serialized = origin.Serialize();
232 ExpectParsedUrlsEqual(GURL(serialized), origin.GetURL());
233
234 EXPECT_EQ(test_case.expected, serialized);
235
236 // The '<<' operator sometimes produces additional information.
237 std::stringstream out;
238 out << origin;
239 if (test_case.expected_log)
240 EXPECT_EQ(test_case.expected_log, out.str());
241 else
242 EXPECT_EQ(test_case.expected, out.str());
243 }
244 }
245
TEST_F(OriginTest,Comparison)246 TEST_F(OriginTest, Comparison) {
247 // These URLs are arranged in increasing order:
248 const char* const urls[] = {
249 "data:uniqueness", "http://a:80", "http://b:80",
250 "https://a:80", "https://b:80", "http://a:81",
251 "http://b:81", "https://a:81", "https://b:81",
252 };
253 // Validate the comparison logic still works when creating a canonical origin,
254 // when any created opaque origins contain a nonce.
255 {
256 // Pre-create the origins, as the internal nonce for unique origins changes
257 // with each freshly-constructed Origin (that's not copied).
258 std::vector<Origin> origins;
259 for (const auto* test_url : urls)
260 origins.push_back(Origin::Create(GURL(test_url)));
261 for (size_t i = 0; i < origins.size(); i++) {
262 const Origin& current = origins[i];
263 for (size_t j = i; j < origins.size(); j++) {
264 const Origin& to_compare = origins[j];
265 EXPECT_EQ(i < j, current < to_compare) << i << " < " << j;
266 EXPECT_EQ(j < i, to_compare < current) << j << " < " << i;
267 }
268 }
269 }
270 }
271
TEST_F(OriginTest,UnsafelyCreate)272 TEST_F(OriginTest, UnsafelyCreate) {
273 struct TestCase {
274 const char* scheme;
275 const char* host;
276 uint16_t port;
277 } cases[] = {
278 {"http", "example.com", 80},
279 {"http", "example.com", 123},
280 {"https", "example.com", 443},
281 {"https", "example.com", 123},
282 {"http", "example.com", 0}, // 0 is a valid port for http.
283 {"file", "", 0}, // 0 indicates "no port" for file: scheme.
284 {"file", "example.com", 0},
285 };
286
287 for (const auto& test : cases) {
288 SCOPED_TRACE(testing::Message()
289 << test.scheme << "://" << test.host << ":" << test.port);
290 absl::optional<url::Origin> origin =
291 url::Origin::UnsafelyCreateTupleOriginWithoutNormalization(
292 test.scheme, test.host, test.port);
293 ASSERT_TRUE(origin);
294 EXPECT_EQ(test.scheme, origin->scheme());
295 EXPECT_EQ(test.host, origin->host());
296 EXPECT_EQ(test.port, origin->port());
297 EXPECT_FALSE(origin->opaque());
298 EXPECT_TRUE(origin->IsSameOriginWith(*origin));
299
300 ExpectParsedUrlsEqual(GURL(origin->Serialize()), origin->GetURL());
301
302 base::UnguessableToken nonce = base::UnguessableToken::Create();
303 absl::optional<url::Origin> opaque_origin =
304 UnsafelyCreateOpaqueOriginWithoutNormalization(
305 test.scheme, test.host, test.port, CreateNonce(nonce));
306 ASSERT_TRUE(opaque_origin);
307 EXPECT_TRUE(opaque_origin->opaque());
308 EXPECT_FALSE(*opaque_origin == origin);
309 EXPECT_EQ(opaque_origin->GetTupleOrPrecursorTupleIfOpaque(),
310 origin->GetTupleOrPrecursorTupleIfOpaque());
311 EXPECT_EQ(opaque_origin,
312 UnsafelyCreateOpaqueOriginWithoutNormalization(
313 test.scheme, test.host, test.port, CreateNonce(nonce)));
314 EXPECT_FALSE(*opaque_origin == origin->DeriveNewOpaqueOrigin());
315 }
316 }
317
TEST_F(OriginTest,UnsafelyCreateUniqueOnInvalidInput)318 TEST_F(OriginTest, UnsafelyCreateUniqueOnInvalidInput) {
319 url::AddStandardScheme("host-only", url::SCHEME_WITH_HOST);
320 url::AddStandardScheme("host-port-only", url::SCHEME_WITH_HOST_AND_PORT);
321 struct TestCases {
322 const char* scheme;
323 const char* host;
324 uint16_t port = 80;
325 } cases[] = {{"", "", 33},
326 {"data", "", 0},
327 {"blob", "", 0},
328 {"filesystem", "", 0},
329 {"data", "example.com"},
330 {"http", "☃.net"},
331 {"http\nmore", "example.com"},
332 {"http\rmore", "example.com"},
333 {"http\n", "example.com"},
334 {"http\r", "example.com"},
335 {"http", "example.com\nnot-example.com"},
336 {"http", "example.com\rnot-example.com"},
337 {"http", "example.com\n"},
338 {"http", "example.com\r"},
339 {"unknown-scheme", "example.com"},
340 {"host-only", "\r", 0},
341 {"host-only", "example.com", 22},
342 {"file", "", 123}}; // file: shouldn't have a port.
343
344 for (const auto& test : cases) {
345 SCOPED_TRACE(testing::Message()
346 << test.scheme << "://" << test.host << ":" << test.port);
347 EXPECT_FALSE(UnsafelyCreateOpaqueOriginWithoutNormalization(
348 test.scheme, test.host, test.port, CreateNonce()));
349 EXPECT_FALSE(url::Origin::UnsafelyCreateTupleOriginWithoutNormalization(
350 test.scheme, test.host, test.port));
351 }
352
353 // An empty scheme/host/port tuple is not a valid tuple origin.
354 EXPECT_FALSE(
355 url::Origin::UnsafelyCreateTupleOriginWithoutNormalization("", "", 0));
356
357 // Opaque origins with unknown precursors are allowed.
358 base::UnguessableToken token = base::UnguessableToken::Create();
359 absl::optional<url::Origin> anonymous_opaque =
360 UnsafelyCreateOpaqueOriginWithoutNormalization("", "", 0,
361 CreateNonce(token));
362 ASSERT_TRUE(anonymous_opaque)
363 << "An invalid tuple is a valid input to "
364 << "UnsafelyCreateOpaqueOriginWithoutNormalization, so long as it is "
365 << "the canonical form of the invalid tuple.";
366 EXPECT_TRUE(anonymous_opaque->opaque());
367 EXPECT_EQ(*GetNonce(anonymous_opaque.value()), token);
368 EXPECT_EQ(anonymous_opaque->GetTupleOrPrecursorTupleIfOpaque(),
369 url::SchemeHostPort());
370 }
371
TEST_F(OriginTest,UnsafelyCreateUniqueViaEmbeddedNulls)372 TEST_F(OriginTest, UnsafelyCreateUniqueViaEmbeddedNulls) {
373 struct TestCases {
374 base::StringPiece scheme;
375 base::StringPiece host;
376 uint16_t port = 80;
377 } cases[] = {{{"http\0more", 9}, {"example.com", 11}},
378 {{"http\0", 5}, {"example.com", 11}},
379 {{"\0http", 5}, {"example.com", 11}},
380 {{"http"}, {"example.com\0not-example.com", 27}},
381 {{"http"}, {"example.com\0", 12}},
382 {{"http"}, {"\0example.com", 12}},
383 {{""}, {"\0", 1}, 0},
384 {{"\0", 1}, {""}, 0}};
385
386 for (const auto& test : cases) {
387 SCOPED_TRACE(testing::Message()
388 << test.scheme << "://" << test.host << ":" << test.port);
389 EXPECT_FALSE(url::Origin::UnsafelyCreateTupleOriginWithoutNormalization(
390 test.scheme, test.host, test.port));
391 EXPECT_FALSE(UnsafelyCreateOpaqueOriginWithoutNormalization(
392 test.scheme, test.host, test.port, CreateNonce()));
393 }
394 }
395
TEST_F(OriginTest,DomainIs)396 TEST_F(OriginTest, DomainIs) {
397 const struct {
398 const char* url;
399 const char* lower_ascii_domain;
400 bool expected_domain_is;
401 } kTestCases[] = {
402 {"http://google.com/foo", "google.com", true},
403 {"http://www.google.com:99/foo", "google.com", true},
404 {"http://www.google.com.cn/foo", "google.com", false},
405 {"http://www.google.comm", "google.com", false},
406 {"http://www.iamnotgoogle.com/foo", "google.com", false},
407 {"http://www.google.com/foo", "Google.com", false},
408
409 // If the host ends with a dot, it matches domains with or without a dot.
410 {"http://www.google.com./foo", "google.com", true},
411 {"http://www.google.com./foo", "google.com.", true},
412 {"http://www.google.com./foo", ".com", true},
413 {"http://www.google.com./foo", ".com.", true},
414
415 // But, if the host doesn't end with a dot and the input domain does, then
416 // it's considered to not match.
417 {"http://google.com/foo", "google.com.", false},
418
419 // If the host ends with two dots, it doesn't match.
420 {"http://www.google.com../foo", "google.com", false},
421
422 // Filesystem scheme.
423 {"filesystem:http://www.google.com:99/foo/", "google.com", true},
424 {"filesystem:http://www.iamnotgoogle.com/foo/", "google.com", false},
425
426 // File scheme.
427 {"file:///home/user/text.txt", "", false},
428 {"file:///home/user/text.txt", "txt", false},
429 };
430
431 for (const auto& test_case : kTestCases) {
432 SCOPED_TRACE(testing::Message()
433 << "(url, domain): (" << test_case.url << ", "
434 << test_case.lower_ascii_domain << ")");
435 GURL url(test_case.url);
436 ASSERT_TRUE(url.is_valid());
437 Origin origin = Origin::Create(url);
438
439 EXPECT_EQ(test_case.expected_domain_is,
440 origin.DomainIs(test_case.lower_ascii_domain));
441 EXPECT_FALSE(
442 origin.DeriveNewOpaqueOrigin().DomainIs(test_case.lower_ascii_domain));
443 }
444
445 // If the URL is invalid, DomainIs returns false.
446 GURL invalid_url("google.com");
447 ASSERT_FALSE(invalid_url.is_valid());
448 EXPECT_FALSE(Origin::Create(invalid_url).DomainIs("google.com"));
449
450 // Unique origins.
451 EXPECT_FALSE(Origin().DomainIs(""));
452 EXPECT_FALSE(Origin().DomainIs("com"));
453 }
454
TEST_F(OriginTest,DebugAlias)455 TEST_F(OriginTest, DebugAlias) {
456 Origin origin1 = Origin::Create(GURL("https://foo.com/bar"));
457 DEBUG_ALIAS_FOR_ORIGIN(origin1_debug_alias, origin1);
458 EXPECT_STREQ("https://foo.com", origin1_debug_alias);
459 }
460
TEST_F(OriginTest,CanBeDerivedFrom)461 TEST_F(OriginTest, CanBeDerivedFrom) {
462 AddStandardScheme("new-standard", SchemeType::SCHEME_WITH_HOST);
463 Origin opaque_unique_origin = Origin();
464
465 Origin regular_origin = Origin::Create(GURL("https://a.com/"));
466 Origin opaque_precursor_origin = regular_origin.DeriveNewOpaqueOrigin();
467
468 Origin file_origin = Origin::Create(GURL("file:///foo/bar"));
469 Origin file_opaque_precursor_origin = file_origin.DeriveNewOpaqueOrigin();
470 Origin file_host_origin = Origin::Create(GURL("file://a.com/foo/bar"));
471 Origin file_host_opaque_precursor_origin =
472 file_host_origin.DeriveNewOpaqueOrigin();
473
474 Origin non_standard_scheme_origin =
475 Origin::Create(GURL("non-standard-scheme:foo"));
476 Origin non_standard_opaque_precursor_origin =
477 non_standard_scheme_origin.DeriveNewOpaqueOrigin();
478
479 // Also, add new standard scheme that is local to the test.
480 Origin new_standard_origin = Origin::Create(GURL("new-standard://host/"));
481 Origin new_standard_opaque_precursor_origin =
482 new_standard_origin.DeriveNewOpaqueOrigin();
483
484 // No access schemes always get unique opaque origins.
485 Origin no_access_origin =
486 Origin::Create(GURL("standard-but-noaccess://b.com"));
487 Origin no_access_opaque_precursor_origin =
488 no_access_origin.DeriveNewOpaqueOrigin();
489
490 Origin local_non_standard_origin =
491 Origin::Create(GURL("local-but-nonstandard://a.com"));
492 Origin local_non_standard_opaque_precursor_origin =
493 local_non_standard_origin.DeriveNewOpaqueOrigin();
494
495 // Call origin.CanBeDerivedFrom(url) for each of the following test cases
496 // and ensure that it returns |expected_value|
497 const struct {
498 const char* url;
499 raw_ptr<Origin> origin;
500 bool expected_value;
501 } kTestCases[] = {
502 {"https://a.com", ®ular_origin, true},
503 // Web URL can commit in an opaque origin with precursor information.
504 // Example: iframe sandbox navigated to a.com.
505 {"https://a.com", &opaque_precursor_origin, true},
506 // URL that comes from the web can never commit in an opaque unique
507 // origin. It must have precursor information.
508 {"https://a.com", &opaque_unique_origin, false},
509
510 // Cross-origin URLs should never work.
511 {"https://b.com", ®ular_origin, false},
512 {"https://b.com", &opaque_precursor_origin, false},
513
514 // data: URL can never commit in a regular, non-opaque origin.
515 {"data:text/html,foo", ®ular_origin, false},
516 // This is the default case: data: URLs commit in opaque origin carrying
517 // precursor information for the origin that created them.
518 {"data:text/html,foo", &opaque_precursor_origin, true},
519 // Browser-initiated navigations can result in data: URL committing in
520 // opaque unique origin.
521 {"data:text/html,foo", &opaque_unique_origin, true},
522
523 // about:blank can commit in regular origin (default case for iframes).
524 {"about:blank", ®ular_origin, true},
525 // This can happen if data: URL that originated at a.com creates an
526 // about:blank iframe.
527 {"about:blank", &opaque_precursor_origin, true},
528 // Browser-initiated navigations can result in about:blank URL committing
529 // in opaque unique origin.
530 {"about:blank", &opaque_unique_origin, true},
531
532 // Default behavior of srcdoc is to inherit the origin of the parent
533 // document.
534 {"about:srcdoc", ®ular_origin, true},
535 // This happens for sandboxed srcdoc iframe.
536 {"about:srcdoc", &opaque_precursor_origin, true},
537 // This can happen with browser-initiated navigation to about:blank or
538 // data: URL, which in turn add srcdoc iframe.
539 {"about:srcdoc", &opaque_unique_origin, true},
540
541 // Just like srcdoc, blob: URLs can be created in all the cases.
542 {"blob:https://a.com/foo", ®ular_origin, true},
543 {"blob:https://a.com/foo", &opaque_precursor_origin, true},
544 {"blob:https://a.com/foo", &opaque_unique_origin, true},
545
546 {"filesystem:https://a.com/foo", ®ular_origin, true},
547 {"filesystem:https://a.com/foo", &opaque_precursor_origin, true},
548 // Unlike blob: URLs, filesystem: ones cannot be created in an unique
549 // opaque origin.
550 {"filesystem:https://a.com/foo", &opaque_unique_origin, false},
551
552 // file: URLs cannot result in regular web origins, regardless of
553 // opaqueness.
554 {"file:///etc/passwd", ®ular_origin, false},
555 {"file:///etc/passwd", &opaque_precursor_origin, false},
556 // However, they can result in regular file: origin and an opaque one
557 // containing another file: origin as precursor.
558 {"file:///etc/passwd", &file_origin, true},
559 {"file:///etc/passwd", &file_opaque_precursor_origin, true},
560 // It should not be possible to get an opaque unique origin for file:
561 // as it is a standard scheme and will always result in a tuple origin
562 // or will always be derived by other origin.
563 // Note: file:// URLs should become unique opaque origins at some point.
564 {"file:///etc/passwd", &opaque_unique_origin, false},
565
566 // The same set as above, but including a host.
567 {"file://a.com/etc/passwd", ®ular_origin, false},
568 {"file://a.com/etc/passwd", &opaque_precursor_origin, false},
569 {"file://a.com/etc/passwd", &file_host_origin, true},
570 {"file://a.com/etc/passwd", &file_host_opaque_precursor_origin, true},
571 {"file://a.com/etc/passwd", &opaque_unique_origin, false},
572
573 // Locally registered standard scheme should behave the same way
574 // as built-in standard schemes.
575 {"new-standard://host/foo", &new_standard_origin, true},
576 {"new-standard://host/foo", &new_standard_opaque_precursor_origin, true},
577 {"new-standard://host/foo", &opaque_unique_origin, false},
578 {"new-standard://host2/foo", &new_standard_origin, false},
579 {"new-standard://host2/foo", &new_standard_opaque_precursor_origin,
580 false},
581
582 // A non-standard scheme should never commit in an standard origin or
583 // opaque origin with standard precursor information.
584 {"non-standard-scheme://a.com/foo", ®ular_origin, false},
585 {"non-standard-scheme://a.com/foo", &opaque_precursor_origin, false},
586 // However, it should be fine to commit in unique opaque origins or in its
587 // own origin.
588 // Note: since non-standard scheme URLs don't parse out anything
589 // but the scheme, using a random different hostname here would work.
590 {"non-standard-scheme://b.com/foo2", &opaque_unique_origin, true},
591 {"non-standard-scheme://b.com/foo3", &non_standard_scheme_origin, true},
592 {"non-standard-scheme://b.com/foo4",
593 &non_standard_opaque_precursor_origin, true},
594
595 // No access scheme can only commit in opaque origin.
596 {"standard-but-noaccess://a.com/foo", ®ular_origin, false},
597 {"standard-but-noaccess://a.com/foo", &opaque_precursor_origin, false},
598 {"standard-but-noaccess://a.com/foo", &opaque_unique_origin, true},
599 {"standard-but-noaccess://a.com/foo", &no_access_origin, true},
600 {"standard-but-noaccess://a.com/foo", &no_access_opaque_precursor_origin,
601 true},
602 {"standard-but-noaccess://b.com/foo", &no_access_origin, true},
603 {"standard-but-noaccess://b.com/foo", &no_access_opaque_precursor_origin,
604 true},
605
606 // Local schemes can be non-standard, verify they also work as expected.
607 {"local-but-nonstandard://a.com", ®ular_origin, false},
608 {"local-but-nonstandard://a.com", &opaque_precursor_origin, false},
609 {"local-but-nonstandard://a.com", &opaque_unique_origin, true},
610 {"local-but-nonstandard://a.com", &local_non_standard_origin, true},
611 {"local-but-nonstandard://a.com",
612 &local_non_standard_opaque_precursor_origin, true},
613 };
614
615 for (const auto& test_case : kTestCases) {
616 SCOPED_TRACE(testing::Message() << "(origin, url): (" << *test_case.origin
617 << ", " << test_case.url << ")");
618 EXPECT_EQ(test_case.expected_value,
619 test_case.origin->CanBeDerivedFrom(GURL(test_case.url)));
620 }
621 }
622
TEST_F(OriginTest,GetDebugString)623 TEST_F(OriginTest, GetDebugString) {
624 Origin http_origin = Origin::Create(GURL("http://192.168.9.1"));
625 EXPECT_STREQ(http_origin.GetDebugString().c_str(), "http://192.168.9.1");
626
627 Origin http_opaque_origin = http_origin.DeriveNewOpaqueOrigin();
628 EXPECT_THAT(
629 http_opaque_origin.GetDebugString().c_str(),
630 ::testing::MatchesRegex(
631 "null \\[internally: \\(\\w*\\) derived from http://192.168.9.1\\]"));
632 EXPECT_THAT(
633 http_opaque_origin.GetDebugString(false /* include_nonce */).c_str(),
634 ::testing::MatchesRegex(
635 "null \\[internally: derived from http://192.168.9.1\\]"));
636
637 Origin data_origin = Origin::Create(GURL("data:"));
638 EXPECT_STREQ(data_origin.GetDebugString().c_str(),
639 "null [internally: (nonce TBD) anonymous]");
640
641 // The nonce of the origin will be initialized if a new opaque origin is
642 // derived.
643 Origin data_derived_origin = data_origin.DeriveNewOpaqueOrigin();
644 EXPECT_THAT(
645 data_derived_origin.GetDebugString().c_str(),
646 ::testing::MatchesRegex("null \\[internally: \\(\\w*\\) anonymous\\]"));
647 EXPECT_THAT(
648 data_derived_origin.GetDebugString(false /* include_nonce */).c_str(),
649 ::testing::MatchesRegex("null \\[internally: anonymous\\]"));
650
651 Origin file_origin = Origin::Create(GURL("file:///etc/passwd"));
652 EXPECT_STREQ(file_origin.GetDebugString().c_str(),
653 "file:// [internally: file://]");
654
655 Origin file_server_origin =
656 Origin::Create(GURL("file://example.com/etc/passwd"));
657 EXPECT_STREQ(file_server_origin.GetDebugString().c_str(),
658 "file:// [internally: file://example.com]");
659 }
660
TEST_F(OriginTest,Deserialize)661 TEST_F(OriginTest, Deserialize) {
662 std::vector<GURL> valid_urls = {
663 GURL("https://a.com"), GURL("http://a"),
664 GURL("http://a:80"), GURL("file://a.com/etc/passwd"),
665 GURL("file:///etc/passwd"), GURL("http://192.168.1.1"),
666 GURL("http://[2001:db8::1]/"),
667 };
668 for (const GURL& url : valid_urls) {
669 SCOPED_TRACE(url.spec());
670 Origin origin = Origin::Create(url);
671 absl::optional<std::string> serialized = SerializeWithNonce(origin);
672 ASSERT_TRUE(serialized);
673
674 absl::optional<Origin> deserialized = Deserialize(std::move(*serialized));
675 ASSERT_TRUE(deserialized.has_value());
676
677 EXPECT_TRUE(DoEqualityComparisons(origin, deserialized.value(), true));
678 EXPECT_EQ(origin.GetDebugString(), deserialized.value().GetDebugString());
679 }
680 }
681
TEST_F(OriginTest,DeserializeInvalid)682 TEST_F(OriginTest, DeserializeInvalid) {
683 EXPECT_EQ(absl::nullopt, Deserialize(std::string()));
684 EXPECT_EQ(absl::nullopt, Deserialize("deadbeef"));
685 EXPECT_EQ(absl::nullopt, Deserialize("0123456789"));
686 EXPECT_EQ(absl::nullopt, Deserialize("https://a.com"));
687 EXPECT_EQ(absl::nullopt, Deserialize("https://192.168.1.1"));
688 }
689
TEST_F(OriginTest,SerializeTBDNonce)690 TEST_F(OriginTest, SerializeTBDNonce) {
691 std::vector<GURL> invalid_urls = {
692 GURL("data:uniqueness"), GURL("data:,"),
693 GURL("data:text/html,Hello!"), GURL("javascript:alert(1)"),
694 GURL("about:blank"), GURL("google.com"),
695 };
696 for (const GURL& url : invalid_urls) {
697 SCOPED_TRACE(url.spec());
698 Origin origin = Origin::Create(url);
699 absl::optional<std::string> serialized = SerializeWithNonce(origin);
700 absl::optional<Origin> deserialized = Deserialize(std::move(*serialized));
701 ASSERT_TRUE(deserialized.has_value());
702
703 // Can't use DoEqualityComparisons here since empty nonces are never ==
704 // unless they are the same object.
705 EXPECT_EQ(origin.GetDebugString(), deserialized.value().GetDebugString());
706 }
707
708 {
709 // Same basic test as above, but without a GURL to create tuple_.
710 Origin opaque;
711 absl::optional<std::string> serialized = SerializeWithNonce(opaque);
712 ASSERT_TRUE(serialized);
713
714 absl::optional<Origin> deserialized = Deserialize(std::move(*serialized));
715 ASSERT_TRUE(deserialized.has_value());
716
717 // Can't use DoEqualityComparisons here since empty nonces are never ==
718 // unless they are the same object.
719 EXPECT_EQ(opaque.GetDebugString(), deserialized.value().GetDebugString());
720 }
721
722 // Now force initialization of the nonce prior to serialization.
723 for (const GURL& url : invalid_urls) {
724 SCOPED_TRACE(url.spec());
725 Origin origin = Origin::Create(url);
726 absl::optional<std::string> serialized =
727 SerializeWithNonceAndInitIfNeeded(origin);
728 absl::optional<Origin> deserialized = Deserialize(std::move(*serialized));
729 ASSERT_TRUE(deserialized.has_value());
730
731 // The nonce should have been initialized prior to Serialization().
732 EXPECT_EQ(origin, deserialized.value());
733 }
734 }
735
TEST_F(OriginTest,DeserializeValidNonce)736 TEST_F(OriginTest, DeserializeValidNonce) {
737 Origin opaque;
738 GetNonce(opaque);
739
740 absl::optional<std::string> serialized = SerializeWithNonce(opaque);
741 ASSERT_TRUE(serialized);
742
743 absl::optional<Origin> deserialized = Deserialize(std::move(*serialized));
744 ASSERT_TRUE(deserialized.has_value());
745
746 EXPECT_TRUE(DoEqualityComparisons(opaque, deserialized.value(), true));
747 EXPECT_EQ(opaque.GetDebugString(), deserialized.value().GetDebugString());
748 }
749
TEST_F(OriginTest,IsSameOriginWith)750 TEST_F(OriginTest, IsSameOriginWith) {
751 url::Origin opaque_origin;
752 GURL foo_url = GURL("https://foo.com/path");
753 url::Origin foo_origin = url::Origin::Create(foo_url);
754 GURL bar_url = GURL("https://bar.com/path");
755 url::Origin bar_origin = url::Origin::Create(bar_url);
756
757 EXPECT_FALSE(opaque_origin.IsSameOriginWith(foo_origin));
758 EXPECT_FALSE(opaque_origin.IsSameOriginWith(foo_url));
759
760 EXPECT_TRUE(foo_origin.IsSameOriginWith(foo_origin));
761 EXPECT_TRUE(foo_origin.IsSameOriginWith(foo_url));
762
763 EXPECT_FALSE(foo_origin.IsSameOriginWith(bar_origin));
764 EXPECT_FALSE(foo_origin.IsSameOriginWith(bar_url));
765
766 // Documenting legacy behavior. This doesn't necessarily mean that the legacy
767 // behavior is correct (or desirable in the long-term).
768 EXPECT_FALSE(foo_origin.IsSameOriginWith(GURL("about:blank")));
769 EXPECT_FALSE(foo_origin.IsSameOriginWith(GURL())); // Invalid GURL.
770 EXPECT_TRUE(foo_origin.IsSameOriginWith(GURL("blob:https://foo.com/guid")));
771 }
772
773 INSTANTIATE_TYPED_TEST_SUITE_P(UrlOrigin,
774 AbstractOriginTest,
775 UrlOriginTestTraits);
776
777 } // namespace url
778