1 #include "gtest/gtest.h"
2 #include "gtest/MozGTestBench.h"  // For MOZ_GTEST_BENCH
3 
4 #include <regex>
5 #include "json/json.h"
6 #include "json/reader.h"
7 #include "mozilla/TextUtils.h"
8 #include "mozilla/net/MozURL.h"
9 #include "nsCOMPtr.h"
10 #include "nsDirectoryServiceDefs.h"
11 #include "nsNetUtil.h"
12 #include "nsIFile.h"
13 #include "nsIURI.h"
14 #include "nsStreamUtils.h"
15 
16 using namespace mozilla;
17 using namespace mozilla::net;
18 
TEST(TestMozURL,Getters)19 TEST(TestMozURL, Getters)
20 {
21   nsAutoCString href("http://user:pass@example.com/path?query#ref");
22   RefPtr<MozURL> url;
23   ASSERT_EQ(MozURL::Init(getter_AddRefs(url), href), NS_OK);
24 
25   ASSERT_TRUE(url->Scheme().EqualsLiteral("http"));
26 
27   ASSERT_TRUE(url->Spec() == href);
28 
29   ASSERT_TRUE(url->Username().EqualsLiteral("user"));
30 
31   ASSERT_TRUE(url->Password().EqualsLiteral("pass"));
32 
33   ASSERT_TRUE(url->Host().EqualsLiteral("example.com"));
34 
35   ASSERT_TRUE(url->FilePath().EqualsLiteral("/path"));
36 
37   ASSERT_TRUE(url->Query().EqualsLiteral("query"));
38 
39   ASSERT_TRUE(url->Ref().EqualsLiteral("ref"));
40 
41   url = nullptr;
42   ASSERT_EQ(MozURL::Init(getter_AddRefs(url), ""_ns), NS_ERROR_MALFORMED_URI);
43   ASSERT_EQ(url, nullptr);
44 }
45 
TEST(TestMozURL,MutatorChain)46 TEST(TestMozURL, MutatorChain)
47 {
48   nsAutoCString href("http://user:pass@example.com/path?query#ref");
49   RefPtr<MozURL> url;
50   ASSERT_EQ(MozURL::Init(getter_AddRefs(url), href), NS_OK);
51   nsAutoCString out;
52 
53   RefPtr<MozURL> url2;
54   ASSERT_EQ(url->Mutate()
55                 .SetScheme("https"_ns)
56                 .SetUsername("newuser"_ns)
57                 .SetPassword("newpass"_ns)
58                 .SetHostname("test"_ns)
59                 .SetFilePath("new/file/path"_ns)
60                 .SetQuery("bla"_ns)
61                 .SetRef("huh"_ns)
62                 .Finalize(getter_AddRefs(url2)),
63             NS_OK);
64 
65   ASSERT_TRUE(url2->Spec().EqualsLiteral(
66       "https://newuser:newpass@test/new/file/path?bla#huh"));
67 }
68 
TEST(TestMozURL,MutatorFinalizeTwice)69 TEST(TestMozURL, MutatorFinalizeTwice)
70 {
71   nsAutoCString href("http://user:pass@example.com/path?query#ref");
72   RefPtr<MozURL> url;
73   ASSERT_EQ(MozURL::Init(getter_AddRefs(url), href), NS_OK);
74   nsAutoCString out;
75 
76   RefPtr<MozURL> url2;
77   MozURL::Mutator mut = url->Mutate();
78   mut.SetScheme("https"_ns);  // Change the scheme to https
79   ASSERT_EQ(mut.Finalize(getter_AddRefs(url2)), NS_OK);
80   ASSERT_TRUE(url2->Spec().EqualsLiteral(
81       "https://user:pass@example.com/path?query#ref"));
82 
83   // Test that a second call to Finalize will result in an error code
84   url2 = nullptr;
85   ASSERT_EQ(mut.Finalize(getter_AddRefs(url2)), NS_ERROR_NOT_AVAILABLE);
86   ASSERT_EQ(url2, nullptr);
87 }
88 
TEST(TestMozURL,MutatorErrorStatus)89 TEST(TestMozURL, MutatorErrorStatus)
90 {
91   nsAutoCString href("http://user:pass@example.com/path?query#ref");
92   RefPtr<MozURL> url;
93   ASSERT_EQ(MozURL::Init(getter_AddRefs(url), href), NS_OK);
94   nsAutoCString out;
95 
96   // Test that trying to set the scheme to a bad value will get you an error
97   MozURL::Mutator mut = url->Mutate();
98   mut.SetScheme("!@#$%^&*("_ns);
99   ASSERT_EQ(mut.GetStatus(), NS_ERROR_MALFORMED_URI);
100 
101   // Test that the mutator will not work after one faulty operation
102   mut.SetScheme("test"_ns);
103   ASSERT_EQ(mut.GetStatus(), NS_ERROR_MALFORMED_URI);
104 }
105 
TEST(TestMozURL,InitWithBase)106 TEST(TestMozURL, InitWithBase)
107 {
108   nsAutoCString href("https://example.net/a/b.html");
109   RefPtr<MozURL> url;
110   ASSERT_EQ(MozURL::Init(getter_AddRefs(url), href), NS_OK);
111 
112   ASSERT_TRUE(url->Spec().EqualsLiteral("https://example.net/a/b.html"));
113 
114   RefPtr<MozURL> url2;
115   ASSERT_EQ(MozURL::Init(getter_AddRefs(url2), "c.png"_ns, url), NS_OK);
116 
117   ASSERT_TRUE(url2->Spec().EqualsLiteral("https://example.net/a/c.png"));
118 }
119 
TEST(TestMozURL,Path)120 TEST(TestMozURL, Path)
121 {
122   nsAutoCString href("about:blank");
123   RefPtr<MozURL> url;
124   ASSERT_EQ(MozURL::Init(getter_AddRefs(url), href), NS_OK);
125 
126   ASSERT_TRUE(url->Spec().EqualsLiteral("about:blank"));
127 
128   ASSERT_TRUE(url->Scheme().EqualsLiteral("about"));
129 
130   ASSERT_TRUE(url->FilePath().EqualsLiteral("blank"));
131 }
132 
TEST(TestMozURL,HostPort)133 TEST(TestMozURL, HostPort)
134 {
135   nsAutoCString href("https://user:pass@example.net:1234/path?query#ref");
136   RefPtr<MozURL> url;
137   ASSERT_EQ(MozURL::Init(getter_AddRefs(url), href), NS_OK);
138 
139   ASSERT_TRUE(url->HostPort().EqualsLiteral("example.net:1234"));
140 
141   RefPtr<MozURL> url2;
142   url->Mutate().SetHostPort("test:321"_ns).Finalize(getter_AddRefs(url2));
143 
144   ASSERT_TRUE(url2->HostPort().EqualsLiteral("test:321"));
145   ASSERT_TRUE(
146       url2->Spec().EqualsLiteral("https://user:pass@test:321/path?query#ref"));
147 
148   href.Assign("https://user:pass@example.net:443/path?query#ref");
149   ASSERT_EQ(MozURL::Init(getter_AddRefs(url), href), NS_OK);
150   ASSERT_TRUE(url->HostPort().EqualsLiteral("example.net"));
151   ASSERT_EQ(url->Port(), -1);
152 }
153 
TEST(TestMozURL,Origin)154 TEST(TestMozURL, Origin)
155 {
156   nsAutoCString href("https://user:pass@example.net:1234/path?query#ref");
157   RefPtr<MozURL> url;
158   ASSERT_EQ(MozURL::Init(getter_AddRefs(url), href), NS_OK);
159 
160   nsAutoCString out;
161   url->Origin(out);
162   ASSERT_TRUE(out.EqualsLiteral("https://example.net:1234"));
163 
164   RefPtr<MozURL> url2;
165   ASSERT_EQ(MozURL::Init(getter_AddRefs(url2), "file:///tmp/foo"_ns), NS_OK);
166   url2->Origin(out);
167   ASSERT_TRUE(out.EqualsLiteral("file:///tmp/foo"));
168 
169   RefPtr<MozURL> url3;
170   ASSERT_EQ(
171       MozURL::Init(getter_AddRefs(url3),
172                    nsLiteralCString(
173                        "moz-extension://53711a8f-65ed-e742-9671-1f02e267c0bc/"
174                        "foo/bar.html")),
175       NS_OK);
176   url3->Origin(out);
177   ASSERT_TRUE(out.EqualsLiteral(
178       "moz-extension://53711a8f-65ed-e742-9671-1f02e267c0bc"));
179 
180   RefPtr<MozURL> url4;
181   ASSERT_EQ(MozURL::Init(getter_AddRefs(url4), "resource://foo/bar.html"_ns),
182             NS_OK);
183   url4->Origin(out);
184   ASSERT_TRUE(out.EqualsLiteral("resource://foo"));
185 
186   RefPtr<MozURL> url5;
187   ASSERT_EQ(MozURL::Init(getter_AddRefs(url5), "about:home"_ns), NS_OK);
188   url5->Origin(out);
189   ASSERT_TRUE(out.EqualsLiteral("about:home"));
190 }
191 
TEST(TestMozURL,BaseDomain)192 TEST(TestMozURL, BaseDomain)
193 {
194   nsAutoCString href("https://user:pass@example.net:1234/path?query#ref");
195   RefPtr<MozURL> url;
196   ASSERT_EQ(MozURL::Init(getter_AddRefs(url), href), NS_OK);
197 
198   nsAutoCString out;
199   ASSERT_EQ(url->BaseDomain(out), NS_OK);
200   ASSERT_TRUE(out.EqualsLiteral("example.net"));
201 
202   RefPtr<MozURL> url2;
203   ASSERT_EQ(MozURL::Init(getter_AddRefs(url2), "file:///tmp/foo"_ns), NS_OK);
204   ASSERT_EQ(url2->BaseDomain(out), NS_OK);
205   ASSERT_TRUE(out.EqualsLiteral("/tmp/foo"));
206 
207   RefPtr<MozURL> url3;
208   ASSERT_EQ(
209       MozURL::Init(getter_AddRefs(url3),
210                    nsLiteralCString(
211                        "moz-extension://53711a8f-65ed-e742-9671-1f02e267c0bc/"
212                        "foo/bar.html")),
213       NS_OK);
214   ASSERT_EQ(url3->BaseDomain(out), NS_OK);
215   ASSERT_TRUE(out.EqualsLiteral("53711a8f-65ed-e742-9671-1f02e267c0bc"));
216 
217   RefPtr<MozURL> url4;
218   ASSERT_EQ(MozURL::Init(getter_AddRefs(url4), "resource://foo/bar.html"_ns),
219             NS_OK);
220   ASSERT_EQ(url4->BaseDomain(out), NS_OK);
221   ASSERT_TRUE(out.EqualsLiteral("foo"));
222 
223   RefPtr<MozURL> url5;
224   ASSERT_EQ(MozURL::Init(getter_AddRefs(url5), "about:home"_ns), NS_OK);
225   ASSERT_EQ(url5->BaseDomain(out), NS_OK);
226   ASSERT_TRUE(out.EqualsLiteral("about:home"));
227 }
228 
229 namespace {
230 
OriginMatchesExpectedOrigin(const nsACString & aOrigin,const nsACString & aExpectedOrigin)231 bool OriginMatchesExpectedOrigin(const nsACString& aOrigin,
232                                  const nsACString& aExpectedOrigin) {
233   if (aExpectedOrigin.Equals("null") &&
234       StringBeginsWith(aOrigin, "moz-nullprincipal"_ns)) {
235     return true;
236   }
237   return aOrigin == aExpectedOrigin;
238 }
239 
IsUUID(const nsACString & aString)240 bool IsUUID(const nsACString& aString) {
241   if (!IsAscii(aString)) {
242     return false;
243   }
244 
245   std::regex pattern(
246       "^\\{[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-4[0-9A-Fa-f]{3}-[89ABab"
247       "][0-9A-Fa-f]{3}-[0-9A-Fa-f]{12}\\}$");
248   return regex_match(nsCString(aString).get(), pattern);
249 }
250 
BaseDomainsEqual(const nsACString & aBaseDomain1,const nsACString & aBaseDomain2)251 bool BaseDomainsEqual(const nsACString& aBaseDomain1,
252                       const nsACString& aBaseDomain2) {
253   if (IsUUID(aBaseDomain1) && IsUUID(aBaseDomain2)) {
254     return true;
255   }
256   return aBaseDomain1 == aBaseDomain2;
257 }
258 
CheckOrigin(const nsACString & aSpec,const nsACString & aBase,const nsACString & aOrigin)259 void CheckOrigin(const nsACString& aSpec, const nsACString& aBase,
260                  const nsACString& aOrigin) {
261   nsCOMPtr<nsIURI> baseUri;
262   nsresult rv = NS_NewURI(getter_AddRefs(baseUri), aBase);
263   ASSERT_EQ(rv, NS_OK);
264 
265   nsCOMPtr<nsIURI> uri;
266   rv = NS_NewURI(getter_AddRefs(uri), aSpec, nullptr, baseUri);
267   ASSERT_EQ(rv, NS_OK);
268 
269   OriginAttributes attrs;
270 
271   nsCOMPtr<nsIPrincipal> principal =
272       BasePrincipal::CreateContentPrincipal(uri, attrs);
273   ASSERT_TRUE(principal);
274 
275   nsCString origin;
276   rv = principal->GetOriginNoSuffix(origin);
277   ASSERT_EQ(rv, NS_OK);
278 
279   EXPECT_TRUE(OriginMatchesExpectedOrigin(origin, aOrigin));
280 
281   nsCString baseDomain;
282   rv = principal->GetBaseDomain(baseDomain);
283 
284   bool baseDomainSucceeded = NS_SUCCEEDED(rv);
285 
286   RefPtr<MozURL> baseUrl;
287   ASSERT_EQ(MozURL::Init(getter_AddRefs(baseUrl), aBase), NS_OK);
288 
289   RefPtr<MozURL> url;
290   ASSERT_EQ(MozURL::Init(getter_AddRefs(url), aSpec, baseUrl), NS_OK);
291 
292   url->Origin(origin);
293 
294   EXPECT_TRUE(OriginMatchesExpectedOrigin(origin, aOrigin));
295 
296   nsCString baseDomain2;
297   rv = url->BaseDomain(baseDomain2);
298 
299   bool baseDomain2Succeeded = NS_SUCCEEDED(rv);
300 
301   EXPECT_TRUE(baseDomainSucceeded == baseDomain2Succeeded);
302 
303   if (baseDomainSucceeded) {
304     EXPECT_TRUE(BaseDomainsEqual(baseDomain, baseDomain2));
305   }
306 }
307 
308 }  // namespace
309 
TEST(TestMozURL,UrlTestData)310 TEST(TestMozURL, UrlTestData)
311 {
312   nsCOMPtr<nsIFile> file;
313   nsresult rv =
314       NS_GetSpecialDirectory(NS_OS_CURRENT_WORKING_DIR, getter_AddRefs(file));
315   ASSERT_EQ(rv, NS_OK);
316 
317   rv = file->Append(u"urltestdata.json"_ns);
318   ASSERT_EQ(rv, NS_OK);
319 
320   bool exists;
321   rv = file->Exists(&exists);
322   ASSERT_EQ(rv, NS_OK);
323 
324   ASSERT_TRUE(exists);
325 
326   nsCOMPtr<nsIInputStream> stream;
327   rv = NS_NewLocalFileInputStream(getter_AddRefs(stream), file);
328   ASSERT_EQ(rv, NS_OK);
329 
330   nsCOMPtr<nsIInputStream> bufferedStream;
331   rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream),
332                                  stream.forget(), 4096);
333   ASSERT_EQ(rv, NS_OK);
334 
335   nsCString data;
336   rv = NS_ConsumeStream(bufferedStream, UINT32_MAX, data);
337   ASSERT_EQ(rv, NS_OK);
338 
339   Json::Value root;
340   Json::CharReaderBuilder builder;
341   std::unique_ptr<Json::CharReader> const reader(builder.newCharReader());
342   ASSERT_TRUE(
343       reader->parse(data.BeginReading(), data.EndReading(), &root, nullptr));
344   ASSERT_TRUE(root.isArray());
345 
346   for (auto& item : root) {
347     if (!item.isObject()) {
348       continue;
349     }
350 
351     const Json::Value& skip = item["skip"];
352     ASSERT_TRUE(skip.isNull() || skip.isBool());
353     if (skip.isBool() && skip.asBool()) {
354       continue;
355     }
356 
357     const Json::Value& failure = item["failure"];
358     ASSERT_TRUE(failure.isNull() || failure.isBool());
359     if (failure.isBool() && failure.asBool()) {
360       continue;
361     }
362 
363     const Json::Value& origin = item["origin"];
364     ASSERT_TRUE(origin.isNull() || origin.isString());
365     if (origin.isNull()) {
366       continue;
367     }
368     const char* originBegin;
369     const char* originEnd;
370     origin.getString(&originBegin, &originEnd);
371 
372     const Json::Value& base = item["base"];
373     ASSERT_TRUE(base.isString());
374     const char* baseBegin;
375     const char* baseEnd;
376     base.getString(&baseBegin, &baseEnd);
377 
378     const Json::Value& input = item["input"];
379     ASSERT_TRUE(input.isString());
380     const char* inputBegin;
381     const char* inputEnd;
382     input.getString(&inputBegin, &inputEnd);
383 
384     CheckOrigin(nsDependentCString(inputBegin, inputEnd),
385                 nsDependentCString(baseBegin, baseEnd),
386                 nsDependentCString(originBegin, originEnd));
387   }
388 }
389