• 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(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", &regular_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", &regular_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", &regular_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", &regular_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", &regular_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", &regular_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", &regular_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", &regular_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", &regular_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", &regular_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", &regular_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", &regular_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