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 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 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 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 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 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 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 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 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 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:// 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 // *:// 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> 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. 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. 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 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 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 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 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