1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
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 "base/memory/scoped_ptr.h"
6 #include "chrome/common/extensions/url_pattern.h"
7 #include "testing/gtest/include/gtest/gtest.h"
8 #include "googleurl/src/gurl.h"
9
10 // See url_pattern.h for examples of valid and invalid patterns.
11
12 static const int kAllSchemes =
13 URLPattern::SCHEME_HTTP |
14 URLPattern::SCHEME_HTTPS |
15 URLPattern::SCHEME_FILE |
16 URLPattern::SCHEME_FTP |
17 URLPattern::SCHEME_CHROMEUI;
18
TEST(ExtensionURLPatternTest,ParseInvalid)19 TEST(ExtensionURLPatternTest, ParseInvalid) {
20 const struct {
21 const char* pattern;
22 URLPattern::ParseResult expected_result;
23 } kInvalidPatterns[] = {
24 { "http", URLPattern::PARSE_ERROR_MISSING_SCHEME_SEPARATOR },
25 { "http:", URLPattern::PARSE_ERROR_WRONG_SCHEME_SEPARATOR },
26 { "http:/", URLPattern::PARSE_ERROR_WRONG_SCHEME_SEPARATOR },
27 { "about://", URLPattern::PARSE_ERROR_WRONG_SCHEME_SEPARATOR },
28 { "http://", URLPattern::PARSE_ERROR_EMPTY_HOST },
29 { "http:///", URLPattern::PARSE_ERROR_EMPTY_HOST },
30 { "http://*foo/bar", URLPattern::PARSE_ERROR_INVALID_HOST_WILDCARD },
31 { "http://foo.*.bar/baz", URLPattern::PARSE_ERROR_INVALID_HOST_WILDCARD },
32 { "http://fo.*.ba:123/baz", URLPattern::PARSE_ERROR_INVALID_HOST_WILDCARD },
33 { "http:/bar", URLPattern::PARSE_ERROR_WRONG_SCHEME_SEPARATOR },
34 { "http://bar", URLPattern::PARSE_ERROR_EMPTY_PATH },
35 };
36
37 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kInvalidPatterns); ++i) {
38 URLPattern pattern(URLPattern::SCHEME_ALL);
39 EXPECT_EQ(kInvalidPatterns[i].expected_result,
40 pattern.Parse(kInvalidPatterns[i].pattern,
41 URLPattern::PARSE_LENIENT))
42 << kInvalidPatterns[i].pattern;
43 }
44 };
45
TEST(ExtensionURLPatternTest,Colons)46 TEST(ExtensionURLPatternTest, Colons) {
47 const struct {
48 const char* pattern;
49 URLPattern::ParseResult expected_result;
50 } kTestPatterns[] = {
51 { "http://foo:1234/", URLPattern::PARSE_ERROR_HAS_COLON },
52 { "http://foo:1234/bar", URLPattern::PARSE_ERROR_HAS_COLON },
53 { "http://*.foo:1234/", URLPattern::PARSE_ERROR_HAS_COLON },
54 { "http://*.foo:1234/bar", URLPattern::PARSE_ERROR_HAS_COLON },
55 { "http://:1234/", URLPattern::PARSE_ERROR_HAS_COLON },
56 { "http://foo:/", URLPattern::PARSE_ERROR_HAS_COLON },
57 { "http://*.foo:/", URLPattern::PARSE_ERROR_HAS_COLON },
58 { "http://foo:com/", URLPattern::PARSE_ERROR_HAS_COLON },
59
60 // Port-like strings in the path should not trigger a warning.
61 { "http://*/:1234", URLPattern::PARSE_SUCCESS },
62 { "http://*.foo/bar:1234", URLPattern::PARSE_SUCCESS },
63 { "http://foo/bar:1234/path", URLPattern::PARSE_SUCCESS },
64 };
65
66 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTestPatterns); ++i) {
67 URLPattern pattern(URLPattern::SCHEME_ALL);
68
69 // Without |strict_error_checks|, expect success.
70 EXPECT_EQ(URLPattern::PARSE_SUCCESS,
71 pattern.Parse(kTestPatterns[i].pattern,
72 URLPattern::PARSE_LENIENT))
73 << "Got unexpected error for URL pattern: "
74 << kTestPatterns[i].pattern;
75
76 EXPECT_EQ(kTestPatterns[i].expected_result,
77 pattern.Parse(kTestPatterns[i].pattern,
78 URLPattern::PARSE_STRICT))
79 << "Got unexpected result for URL pattern: "
80 << kTestPatterns[i].pattern;
81 }
82 };
83
84 // all pages for a given scheme
TEST(ExtensionURLPatternTest,Match1)85 TEST(ExtensionURLPatternTest, Match1) {
86 URLPattern pattern(kAllSchemes);
87 EXPECT_EQ(URLPattern::PARSE_SUCCESS,
88 pattern.Parse("http://*/*", URLPattern::PARSE_STRICT));
89 EXPECT_EQ("http", pattern.scheme());
90 EXPECT_EQ("", pattern.host());
91 EXPECT_TRUE(pattern.match_subdomains());
92 EXPECT_FALSE(pattern.match_all_urls());
93 EXPECT_EQ("/*", pattern.path());
94 EXPECT_TRUE(pattern.MatchesUrl(GURL("http://google.com")));
95 EXPECT_TRUE(pattern.MatchesUrl(GURL("http://yahoo.com")));
96 EXPECT_TRUE(pattern.MatchesUrl(GURL("http://google.com/foo")));
97 EXPECT_FALSE(pattern.MatchesUrl(GURL("https://google.com")));
98 EXPECT_TRUE(pattern.MatchesUrl(GURL("http://74.125.127.100/search")));
99 }
100
101 // all domains
TEST(ExtensionURLPatternTest,Match2)102 TEST(ExtensionURLPatternTest, Match2) {
103 URLPattern pattern(kAllSchemes);
104 EXPECT_EQ(URLPattern::PARSE_SUCCESS,
105 pattern.Parse("https://*/foo*", URLPattern::PARSE_STRICT));
106 EXPECT_EQ("https", pattern.scheme());
107 EXPECT_EQ("", pattern.host());
108 EXPECT_TRUE(pattern.match_subdomains());
109 EXPECT_FALSE(pattern.match_all_urls());
110 EXPECT_EQ("/foo*", pattern.path());
111 EXPECT_TRUE(pattern.MatchesUrl(GURL("https://www.google.com/foo")));
112 EXPECT_TRUE(pattern.MatchesUrl(GURL("https://www.google.com/foobar")));
113 EXPECT_FALSE(pattern.MatchesUrl(GURL("http://www.google.com/foo")));
114 EXPECT_FALSE(pattern.MatchesUrl(GURL("https://www.google.com/")));
115 }
116
117 // subdomains
TEST(URLPatternTest,Match3)118 TEST(URLPatternTest, Match3) {
119 URLPattern pattern(kAllSchemes);
120 EXPECT_EQ(URLPattern::PARSE_SUCCESS,
121 pattern.Parse("http://*.google.com/foo*bar",
122 URLPattern::PARSE_STRICT));
123 EXPECT_EQ("http", pattern.scheme());
124 EXPECT_EQ("google.com", pattern.host());
125 EXPECT_TRUE(pattern.match_subdomains());
126 EXPECT_FALSE(pattern.match_all_urls());
127 EXPECT_EQ("/foo*bar", pattern.path());
128 EXPECT_TRUE(pattern.MatchesUrl(GURL("http://google.com/foobar")));
129 EXPECT_TRUE(pattern.MatchesUrl(GURL("http://www.google.com/foo?bar")));
130 EXPECT_TRUE(pattern.MatchesUrl(
131 GURL("http://monkey.images.google.com/foooobar")));
132 EXPECT_FALSE(pattern.MatchesUrl(GURL("http://yahoo.com/foobar")));
133 }
134
135 // glob escaping
TEST(ExtensionURLPatternTest,Match5)136 TEST(ExtensionURLPatternTest, Match5) {
137 URLPattern pattern(kAllSchemes);
138 EXPECT_EQ(URLPattern::PARSE_SUCCESS,
139 pattern.Parse("file:///foo?bar\\*baz", URLPattern::PARSE_STRICT));
140 EXPECT_EQ("file", pattern.scheme());
141 EXPECT_EQ("", pattern.host());
142 EXPECT_FALSE(pattern.match_subdomains());
143 EXPECT_FALSE(pattern.match_all_urls());
144 EXPECT_EQ("/foo?bar\\*baz", pattern.path());
145 EXPECT_TRUE(pattern.MatchesUrl(GURL("file:///foo?bar\\hellobaz")));
146 EXPECT_FALSE(pattern.MatchesUrl(GURL("file:///fooXbar\\hellobaz")));
147 }
148
149 // ip addresses
TEST(ExtensionURLPatternTest,Match6)150 TEST(ExtensionURLPatternTest, Match6) {
151 URLPattern pattern(kAllSchemes);
152 EXPECT_EQ(URLPattern::PARSE_SUCCESS,
153 pattern.Parse("http://127.0.0.1/*", URLPattern::PARSE_STRICT));
154 EXPECT_EQ("http", pattern.scheme());
155 EXPECT_EQ("127.0.0.1", pattern.host());
156 EXPECT_FALSE(pattern.match_subdomains());
157 EXPECT_FALSE(pattern.match_all_urls());
158 EXPECT_EQ("/*", pattern.path());
159 EXPECT_TRUE(pattern.MatchesUrl(GURL("http://127.0.0.1")));
160 }
161
162 // subdomain matching with ip addresses
TEST(ExtensionURLPatternTest,Match7)163 TEST(ExtensionURLPatternTest, Match7) {
164 URLPattern pattern(kAllSchemes);
165 EXPECT_EQ(URLPattern::PARSE_SUCCESS,
166 pattern.Parse("http://*.0.0.1/*",
167 URLPattern::PARSE_STRICT)); // allowed, but useless
168 EXPECT_EQ("http", pattern.scheme());
169 EXPECT_EQ("0.0.1", pattern.host());
170 EXPECT_TRUE(pattern.match_subdomains());
171 EXPECT_FALSE(pattern.match_all_urls());
172 EXPECT_EQ("/*", pattern.path());
173 // Subdomain matching is never done if the argument has an IP address host.
174 EXPECT_FALSE(pattern.MatchesUrl(GURL("http://127.0.0.1")));
175 };
176
177 // unicode
TEST(ExtensionURLPatternTest,Match8)178 TEST(ExtensionURLPatternTest, Match8) {
179 URLPattern pattern(kAllSchemes);
180 // The below is the ASCII encoding of the following URL:
181 // http://*.\xe1\x80\xbf/a\xc2\x81\xe1*
182 EXPECT_EQ(URLPattern::PARSE_SUCCESS,
183 pattern.Parse("http://*.xn--gkd/a%C2%81%E1*",
184 URLPattern::PARSE_STRICT));
185 EXPECT_EQ("http", pattern.scheme());
186 EXPECT_EQ("xn--gkd", pattern.host());
187 EXPECT_TRUE(pattern.match_subdomains());
188 EXPECT_FALSE(pattern.match_all_urls());
189 EXPECT_EQ("/a%C2%81%E1*", pattern.path());
190 EXPECT_TRUE(pattern.MatchesUrl(
191 GURL("http://abc.\xe1\x80\xbf/a\xc2\x81\xe1xyz")));
192 EXPECT_TRUE(pattern.MatchesUrl(
193 GURL("http://\xe1\x80\xbf/a\xc2\x81\xe1\xe1")));
194 };
195
196 // chrome://
TEST(ExtensionURLPatternTest,Match9)197 TEST(ExtensionURLPatternTest, Match9) {
198 URLPattern pattern(kAllSchemes);
199 EXPECT_EQ(URLPattern::PARSE_SUCCESS,
200 pattern.Parse("chrome://favicon/*", URLPattern::PARSE_STRICT));
201 EXPECT_EQ("chrome", pattern.scheme());
202 EXPECT_EQ("favicon", pattern.host());
203 EXPECT_FALSE(pattern.match_subdomains());
204 EXPECT_FALSE(pattern.match_all_urls());
205 EXPECT_EQ("/*", pattern.path());
206 EXPECT_TRUE(pattern.MatchesUrl(GURL("chrome://favicon/http://google.com")));
207 EXPECT_TRUE(pattern.MatchesUrl(GURL("chrome://favicon/https://google.com")));
208 EXPECT_FALSE(pattern.MatchesUrl(GURL("chrome://history")));
209 };
210
211 // *://
TEST(ExtensionURLPatternTest,Match10)212 TEST(ExtensionURLPatternTest, Match10) {
213 URLPattern pattern(kAllSchemes);
214 EXPECT_EQ(URLPattern::PARSE_SUCCESS,
215 pattern.Parse("*://*/*", URLPattern::PARSE_STRICT));
216 EXPECT_TRUE(pattern.MatchesScheme("http"));
217 EXPECT_TRUE(pattern.MatchesScheme("https"));
218 EXPECT_FALSE(pattern.MatchesScheme("chrome"));
219 EXPECT_FALSE(pattern.MatchesScheme("file"));
220 EXPECT_FALSE(pattern.MatchesScheme("ftp"));
221 EXPECT_TRUE(pattern.match_subdomains());
222 EXPECT_FALSE(pattern.match_all_urls());
223 EXPECT_EQ("/*", pattern.path());
224 EXPECT_TRUE(pattern.MatchesUrl(GURL("http://127.0.0.1")));
225 EXPECT_FALSE(pattern.MatchesUrl(GURL("chrome://favicon/http://google.com")));
226 EXPECT_FALSE(pattern.MatchesUrl(GURL("file:///foo/bar")));
227 };
228
229 // <all_urls>
TEST(ExtensionURLPatternTest,Match11)230 TEST(ExtensionURLPatternTest, Match11) {
231 URLPattern pattern(kAllSchemes);
232 EXPECT_EQ(URLPattern::PARSE_SUCCESS,
233 pattern.Parse("<all_urls>", URLPattern::PARSE_STRICT));
234 EXPECT_TRUE(pattern.MatchesScheme("chrome"));
235 EXPECT_TRUE(pattern.MatchesScheme("http"));
236 EXPECT_TRUE(pattern.MatchesScheme("https"));
237 EXPECT_TRUE(pattern.MatchesScheme("file"));
238 EXPECT_TRUE(pattern.match_subdomains());
239 EXPECT_TRUE(pattern.match_all_urls());
240 EXPECT_EQ("/*", pattern.path());
241 EXPECT_TRUE(pattern.MatchesUrl(GURL("chrome://favicon/http://google.com")));
242 EXPECT_TRUE(pattern.MatchesUrl(GURL("http://127.0.0.1")));
243 EXPECT_TRUE(pattern.MatchesUrl(GURL("file:///foo/bar")));
244 };
245
246 // SCHEME_ALL matches all schemes.
TEST(ExtensionURLPatternTest,Match12)247 TEST(ExtensionURLPatternTest, Match12) {
248 URLPattern pattern(URLPattern::SCHEME_ALL);
249 EXPECT_EQ(URLPattern::PARSE_SUCCESS,
250 pattern.Parse("<all_urls>", URLPattern::PARSE_STRICT));
251 EXPECT_TRUE(pattern.MatchesScheme("chrome"));
252 EXPECT_TRUE(pattern.MatchesScheme("http"));
253 EXPECT_TRUE(pattern.MatchesScheme("https"));
254 EXPECT_TRUE(pattern.MatchesScheme("file"));
255 EXPECT_TRUE(pattern.MatchesScheme("javascript"));
256 EXPECT_TRUE(pattern.MatchesScheme("data"));
257 EXPECT_TRUE(pattern.MatchesScheme("about"));
258 EXPECT_TRUE(pattern.MatchesScheme("chrome-extension"));
259 EXPECT_TRUE(pattern.match_subdomains());
260 EXPECT_TRUE(pattern.match_all_urls());
261 EXPECT_EQ("/*", pattern.path());
262 EXPECT_TRUE(pattern.MatchesUrl(GURL("chrome://favicon/http://google.com")));
263 EXPECT_TRUE(pattern.MatchesUrl(GURL("http://127.0.0.1")));
264 EXPECT_TRUE(pattern.MatchesUrl(GURL("file:///foo/bar")));
265 EXPECT_TRUE(pattern.MatchesUrl(GURL("chrome://newtab")));
266 EXPECT_TRUE(pattern.MatchesUrl(GURL("about:blank")));
267 EXPECT_TRUE(pattern.MatchesUrl(GURL("about:version")));
268 EXPECT_TRUE(pattern.MatchesUrl(
269 GURL("data:text/html;charset=utf-8,<html>asdf</html>")));
270 };
271
272 static const struct MatchPatterns {
273 const char* pattern;
274 const char* matches;
275 } kMatch13UrlPatternTestCases[] = {
276 {"about:*", "about:blank"},
277 {"about:blank", "about:blank"},
278 {"about:*", "about:version"},
279 {"chrome-extension://*/*", "chrome-extension://FTW"},
280 {"data:*", "data:monkey"},
281 {"javascript:*", "javascript:atemyhomework"},
282 };
283
284 // SCHEME_ALL and specific schemes.
TEST(ExtensionURLPatternTest,Match13)285 TEST(ExtensionURLPatternTest, Match13) {
286 for (size_t i = 0; i < arraysize(kMatch13UrlPatternTestCases); ++i) {
287 URLPattern pattern(URLPattern::SCHEME_ALL);
288 EXPECT_EQ(URLPattern::PARSE_SUCCESS,
289 pattern.Parse(kMatch13UrlPatternTestCases[i].pattern,
290 URLPattern::PARSE_STRICT))
291 << " while parsing " << kMatch13UrlPatternTestCases[i].pattern;
292 EXPECT_TRUE(pattern.MatchesUrl(
293 GURL(kMatch13UrlPatternTestCases[i].matches)))
294 << " while matching " << kMatch13UrlPatternTestCases[i].matches;
295 }
296
297 // Negative test.
298 URLPattern pattern(URLPattern::SCHEME_ALL);
299 EXPECT_EQ(URLPattern::PARSE_SUCCESS,
300 pattern.Parse("data:*", URLPattern::PARSE_STRICT));
301 EXPECT_FALSE(pattern.MatchesUrl(GURL("about:blank")));
302 };
303
304 static const struct GetAsStringPatterns {
305 const char* pattern;
306 } kGetAsStringTestCases[] = {
307 { "http://www/" },
308 { "http://*/*" },
309 { "chrome://*/*" },
310 { "chrome://newtab/" },
311 { "about:*" },
312 { "about:blank" },
313 { "chrome-extension://*/*" },
314 { "chrome-extension://FTW/" },
315 { "data:*" },
316 { "data:monkey" },
317 { "javascript:*" },
318 { "javascript:atemyhomework" },
319 };
320
TEST(ExtensionURLPatternTest,GetAsString)321 TEST(ExtensionURLPatternTest, GetAsString) {
322 for (size_t i = 0; i < arraysize(kGetAsStringTestCases); ++i) {
323 URLPattern pattern(URLPattern::SCHEME_ALL);
324 EXPECT_EQ(URLPattern::PARSE_SUCCESS,
325 pattern.Parse(kGetAsStringTestCases[i].pattern,
326 URLPattern::PARSE_STRICT));
327 EXPECT_STREQ(kGetAsStringTestCases[i].pattern,
328 pattern.GetAsString().c_str());
329 }
330 }
331
TestPatternOverlap(const URLPattern & pattern1,const URLPattern & pattern2,bool expect_overlap)332 void TestPatternOverlap(const URLPattern& pattern1, const URLPattern& pattern2,
333 bool expect_overlap) {
334 EXPECT_EQ(expect_overlap, pattern1.OverlapsWith(pattern2))
335 << pattern1.GetAsString() << ", " << pattern2.GetAsString();
336 EXPECT_EQ(expect_overlap, pattern2.OverlapsWith(pattern1))
337 << pattern2.GetAsString() << ", " << pattern1.GetAsString();
338 }
339
TEST(ExtensionURLPatternTest,OverlapsWith)340 TEST(ExtensionURLPatternTest, OverlapsWith) {
341 URLPattern pattern1(kAllSchemes, "http://www.google.com/foo/*");
342 URLPattern pattern2(kAllSchemes, "https://www.google.com/foo/*");
343 URLPattern pattern3(kAllSchemes, "http://*.google.com/foo/*");
344 URLPattern pattern4(kAllSchemes, "http://*.yahooo.com/foo/*");
345 URLPattern pattern5(kAllSchemes, "http://www.yahooo.com/bar/*");
346 URLPattern pattern6(kAllSchemes,
347 "http://www.yahooo.com/bar/baz/*");
348 URLPattern pattern7(kAllSchemes, "file:///*");
349 URLPattern pattern8(kAllSchemes, "*://*/*");
350 URLPattern pattern9(URLPattern::SCHEME_HTTPS, "*://*/*");
351 URLPattern pattern10(kAllSchemes, "<all_urls>");
352
353 TestPatternOverlap(pattern1, pattern1, true);
354 TestPatternOverlap(pattern1, pattern2, false);
355 TestPatternOverlap(pattern1, pattern3, true);
356 TestPatternOverlap(pattern1, pattern4, false);
357 TestPatternOverlap(pattern3, pattern4, false);
358 TestPatternOverlap(pattern4, pattern5, false);
359 TestPatternOverlap(pattern5, pattern6, true);
360
361 // Test that scheme restrictions work.
362 TestPatternOverlap(pattern1, pattern8, true);
363 TestPatternOverlap(pattern1, pattern9, false);
364 TestPatternOverlap(pattern1, pattern10, true);
365
366 // Test that '<all_urls>' includes file URLs, while scheme '*' does not.
367 TestPatternOverlap(pattern7, pattern8, false);
368 TestPatternOverlap(pattern7, pattern10, true);
369 }
370
TEST(ExtensionURLPatternTest,ConvertToExplicitSchemes)371 TEST(ExtensionURLPatternTest, ConvertToExplicitSchemes) {
372 std::vector<URLPattern> all_urls(URLPattern(
373 kAllSchemes,
374 "<all_urls>").ConvertToExplicitSchemes());
375
376 std::vector<URLPattern> all_schemes(URLPattern(
377 kAllSchemes,
378 "*://google.com/foo").ConvertToExplicitSchemes());
379
380 std::vector<URLPattern> monkey(URLPattern(
381 URLPattern::SCHEME_HTTP | URLPattern::SCHEME_HTTPS |
382 URLPattern::SCHEME_FTP,
383 "http://google.com/monkey").ConvertToExplicitSchemes());
384
385 ASSERT_EQ(5u, all_urls.size());
386 ASSERT_EQ(2u, all_schemes.size());
387 ASSERT_EQ(1u, monkey.size());
388
389 EXPECT_EQ("http://*/*", all_urls[0].GetAsString());
390 EXPECT_EQ("https://*/*", all_urls[1].GetAsString());
391 EXPECT_EQ("file:///*", all_urls[2].GetAsString());
392 EXPECT_EQ("ftp://*/*", all_urls[3].GetAsString());
393 EXPECT_EQ("chrome://*/*", all_urls[4].GetAsString());
394
395 EXPECT_EQ("http://google.com/foo", all_schemes[0].GetAsString());
396 EXPECT_EQ("https://google.com/foo", all_schemes[1].GetAsString());
397
398 EXPECT_EQ("http://google.com/monkey", monkey[0].GetAsString());
399 }
400