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