1 // Copyright (c) 2012 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 "extensions/browser/api/declarative_webrequest/webrequest_condition.h" 6 7 #include <set> 8 9 #include "base/memory/scoped_ptr.h" 10 #include "base/message_loop/message_loop.h" 11 #include "base/test/values_test_util.h" 12 #include "base/values.h" 13 #include "components/url_matcher/url_matcher_constants.h" 14 #include "content/public/browser/resource_request_info.h" 15 #include "extensions/browser/api/declarative_webrequest/webrequest_constants.h" 16 #include "net/base/request_priority.h" 17 #include "net/url_request/url_request.h" 18 #include "net/url_request/url_request_test_util.h" 19 #include "testing/gtest/include/gtest/gtest.h" 20 21 using content::ResourceType; 22 using url_matcher::URLMatcher; 23 using url_matcher::URLMatcherConditionSet; 24 25 namespace extensions { 26 27 TEST(WebRequestConditionTest, CreateCondition) { 28 // Necessary for TestURLRequest. 29 base::MessageLoopForIO message_loop; 30 URLMatcher matcher; 31 32 std::string error; 33 scoped_ptr<WebRequestCondition> result; 34 35 // Test wrong condition name passed. 36 error.clear(); 37 result = WebRequestCondition::Create( 38 NULL, 39 matcher.condition_factory(), 40 *base::test::ParseJson( 41 "{ \"invalid\": \"foobar\", \n" 42 " \"instanceType\": \"declarativeWebRequest.RequestMatcher\", \n" 43 "}"), 44 &error); 45 EXPECT_FALSE(error.empty()); 46 EXPECT_FALSE(result.get()); 47 48 // Test wrong datatype in host_suffix. 49 error.clear(); 50 result = WebRequestCondition::Create( 51 NULL, 52 matcher.condition_factory(), 53 *base::test::ParseJson( 54 "{ \n" 55 " \"url\": [], \n" 56 " \"instanceType\": \"declarativeWebRequest.RequestMatcher\", \n" 57 "}"), 58 &error); 59 EXPECT_FALSE(error.empty()); 60 EXPECT_FALSE(result.get()); 61 62 // Test success (can we support multiple criteria?) 63 error.clear(); 64 result = WebRequestCondition::Create( 65 NULL, 66 matcher.condition_factory(), 67 *base::test::ParseJson( 68 "{ \n" 69 " \"resourceType\": [\"main_frame\"], \n" 70 " \"url\": { \"hostSuffix\": \"example.com\" }, \n" 71 " \"instanceType\": \"declarativeWebRequest.RequestMatcher\", \n" 72 "}"), 73 &error); 74 EXPECT_EQ("", error); 75 ASSERT_TRUE(result.get()); 76 77 URLMatcherConditionSet::Vector url_matcher_condition_set; 78 result->GetURLMatcherConditionSets(&url_matcher_condition_set); 79 matcher.AddConditionSets(url_matcher_condition_set); 80 81 net::TestURLRequestContext context; 82 const GURL http_url("http://www.example.com"); 83 scoped_ptr<net::URLRequest> match_request(context.CreateRequest( 84 http_url, net::DEFAULT_PRIORITY, NULL, NULL)); 85 WebRequestData data(match_request.get(), ON_BEFORE_REQUEST); 86 WebRequestDataWithMatchIds request_data(&data); 87 request_data.url_match_ids = matcher.MatchURL(http_url); 88 EXPECT_EQ(1u, request_data.url_match_ids.size()); 89 content::ResourceRequestInfo::AllocateForTesting( 90 match_request.get(), 91 content::RESOURCE_TYPE_MAIN_FRAME, 92 NULL, 93 -1, 94 -1, 95 -1, 96 false); 97 EXPECT_TRUE(result->IsFulfilled(request_data)); 98 99 const GURL https_url("https://www.example.com"); 100 scoped_ptr<net::URLRequest> wrong_resource_type(context.CreateRequest( 101 https_url, net::DEFAULT_PRIORITY, NULL, NULL)); 102 data.request = wrong_resource_type.get(); 103 request_data.url_match_ids = matcher.MatchURL(http_url); 104 // Make sure IsFulfilled does not fail because of URL matching. 105 EXPECT_EQ(1u, request_data.url_match_ids.size()); 106 content::ResourceRequestInfo::AllocateForTesting( 107 wrong_resource_type.get(), 108 content::RESOURCE_TYPE_SUB_FRAME, 109 NULL, 110 -1, 111 -1, 112 -1, 113 false); 114 EXPECT_FALSE(result->IsFulfilled(request_data)); 115 } 116 117 TEST(WebRequestConditionTest, CreateConditionFirstPartyForCookies) { 118 // Necessary for TestURLRequest. 119 base::MessageLoopForIO message_loop; 120 URLMatcher matcher; 121 122 std::string error; 123 scoped_ptr<WebRequestCondition> result; 124 125 result = WebRequestCondition::Create( 126 NULL, 127 matcher.condition_factory(), 128 *base::test::ParseJson( 129 "{ \n" 130 " \"firstPartyForCookiesUrl\": { \"hostPrefix\": \"fpfc\"}, \n" 131 " \"instanceType\": \"declarativeWebRequest.RequestMatcher\", \n" 132 "}"), 133 &error); 134 EXPECT_EQ("", error); 135 ASSERT_TRUE(result.get()); 136 137 URLMatcherConditionSet::Vector url_matcher_condition_set; 138 result->GetURLMatcherConditionSets(&url_matcher_condition_set); 139 matcher.AddConditionSets(url_matcher_condition_set); 140 141 net::TestURLRequestContext context; 142 const GURL http_url("http://www.example.com"); 143 const GURL first_party_url("http://fpfc.example.com"); 144 scoped_ptr<net::URLRequest> match_request(context.CreateRequest( 145 http_url, net::DEFAULT_PRIORITY, NULL, NULL)); 146 WebRequestData data(match_request.get(), ON_BEFORE_REQUEST); 147 WebRequestDataWithMatchIds request_data(&data); 148 request_data.url_match_ids = matcher.MatchURL(http_url); 149 EXPECT_EQ(0u, request_data.url_match_ids.size()); 150 request_data.first_party_url_match_ids = matcher.MatchURL(first_party_url); 151 EXPECT_EQ(1u, request_data.first_party_url_match_ids.size()); 152 content::ResourceRequestInfo::AllocateForTesting( 153 match_request.get(), 154 content::RESOURCE_TYPE_MAIN_FRAME, 155 NULL, 156 -1, 157 -1, 158 -1, 159 false); 160 EXPECT_TRUE(result->IsFulfilled(request_data)); 161 } 162 163 // Conditions without UrlFilter attributes need to be independent of URL 164 // matching results. We test here that: 165 // 1. A non-empty condition without UrlFilter attributes is fulfilled iff its 166 // attributes are fulfilled. 167 // 2. An empty condition (in particular, without UrlFilter attributes) is 168 // always fulfilled. 169 TEST(WebRequestConditionTest, NoUrlAttributes) { 170 // Necessary for TestURLRequest. 171 base::MessageLoopForIO message_loop; 172 URLMatcher matcher; 173 std::string error; 174 175 // The empty condition. 176 error.clear(); 177 scoped_ptr<WebRequestCondition> condition_empty = WebRequestCondition::Create( 178 NULL, 179 matcher.condition_factory(), 180 *base::test::ParseJson( 181 "{ \n" 182 " \"instanceType\": \"declarativeWebRequest.RequestMatcher\", \n" 183 "}"), 184 &error); 185 EXPECT_EQ("", error); 186 ASSERT_TRUE(condition_empty.get()); 187 188 // A condition without a UrlFilter attribute, which is always true. 189 error.clear(); 190 scoped_ptr<WebRequestCondition> condition_no_url_true = 191 WebRequestCondition::Create( 192 NULL, 193 matcher.condition_factory(), 194 *base::test::ParseJson( 195 "{ \n" 196 " \"instanceType\": \"declarativeWebRequest.RequestMatcher\", " 197 "\n" 198 // There is no "1st party for cookies" URL in the requests below, 199 // therefore all requests are considered first party for cookies. 200 " \"thirdPartyForCookies\": false, \n" 201 "}"), 202 &error); 203 EXPECT_EQ("", error); 204 ASSERT_TRUE(condition_no_url_true.get()); 205 206 // A condition without a UrlFilter attribute, which is always false. 207 error.clear(); 208 scoped_ptr<WebRequestCondition> condition_no_url_false = 209 WebRequestCondition::Create( 210 NULL, 211 matcher.condition_factory(), 212 *base::test::ParseJson( 213 "{ \n" 214 " \"instanceType\": \"declarativeWebRequest.RequestMatcher\", " 215 "\n" 216 " \"thirdPartyForCookies\": true, \n" 217 "}"), 218 &error); 219 EXPECT_EQ("", error); 220 ASSERT_TRUE(condition_no_url_false.get()); 221 222 net::TestURLRequestContext context; 223 scoped_ptr<net::URLRequest> https_request(context.CreateRequest( 224 GURL("https://www.example.com"), net::DEFAULT_PRIORITY, NULL, NULL)); 225 226 // 1. A non-empty condition without UrlFilter attributes is fulfilled iff its 227 // attributes are fulfilled. 228 WebRequestData data(https_request.get(), ON_BEFORE_REQUEST); 229 EXPECT_FALSE( 230 condition_no_url_false->IsFulfilled(WebRequestDataWithMatchIds(&data))); 231 232 data = WebRequestData(https_request.get(), ON_BEFORE_REQUEST); 233 EXPECT_TRUE( 234 condition_no_url_true->IsFulfilled(WebRequestDataWithMatchIds(&data))); 235 236 // 2. An empty condition (in particular, without UrlFilter attributes) is 237 // always fulfilled. 238 data = WebRequestData(https_request.get(), ON_BEFORE_REQUEST); 239 EXPECT_TRUE(condition_empty->IsFulfilled(WebRequestDataWithMatchIds(&data))); 240 } 241 242 TEST(WebRequestConditionTest, CreateConditionSet) { 243 // Necessary for TestURLRequest. 244 base::MessageLoopForIO message_loop; 245 URLMatcher matcher; 246 247 WebRequestConditionSet::AnyVector conditions; 248 conditions.push_back(linked_ptr<base::Value>(base::test::ParseJson( 249 "{ \n" 250 " \"instanceType\": \"declarativeWebRequest.RequestMatcher\", \n" 251 " \"url\": { \n" 252 " \"hostSuffix\": \"example.com\", \n" 253 " \"schemes\": [\"http\"], \n" 254 " }, \n" 255 "}").release())); 256 conditions.push_back(linked_ptr<base::Value>(base::test::ParseJson( 257 "{ \n" 258 " \"instanceType\": \"declarativeWebRequest.RequestMatcher\", \n" 259 " \"url\": { \n" 260 " \"hostSuffix\": \"example.com\", \n" 261 " \"hostPrefix\": \"www\", \n" 262 " \"schemes\": [\"https\"], \n" 263 " }, \n" 264 "}").release())); 265 266 // Test insertion 267 std::string error; 268 scoped_ptr<WebRequestConditionSet> result = WebRequestConditionSet::Create( 269 NULL, matcher.condition_factory(), conditions, &error); 270 EXPECT_EQ("", error); 271 ASSERT_TRUE(result.get()); 272 EXPECT_EQ(2u, result->conditions().size()); 273 274 // Tell the URLMatcher about our shiny new patterns. 275 URLMatcherConditionSet::Vector url_matcher_condition_set; 276 result->GetURLMatcherConditionSets(&url_matcher_condition_set); 277 matcher.AddConditionSets(url_matcher_condition_set); 278 279 // Test that the result is correct and matches http://www.example.com and 280 // https://www.example.com 281 GURL http_url("http://www.example.com"); 282 net::TestURLRequestContext context; 283 scoped_ptr<net::URLRequest> http_request(context.CreateRequest( 284 http_url, net::DEFAULT_PRIORITY, NULL, NULL)); 285 WebRequestData data(http_request.get(), ON_BEFORE_REQUEST); 286 WebRequestDataWithMatchIds request_data(&data); 287 request_data.url_match_ids = matcher.MatchURL(http_url); 288 EXPECT_EQ(1u, request_data.url_match_ids.size()); 289 EXPECT_TRUE(result->IsFulfilled(*(request_data.url_match_ids.begin()), 290 request_data)); 291 292 GURL https_url("https://www.example.com"); 293 request_data.url_match_ids = matcher.MatchURL(https_url); 294 EXPECT_EQ(1u, request_data.url_match_ids.size()); 295 scoped_ptr<net::URLRequest> https_request(context.CreateRequest( 296 https_url, net::DEFAULT_PRIORITY, NULL, NULL)); 297 data.request = https_request.get(); 298 EXPECT_TRUE(result->IsFulfilled(*(request_data.url_match_ids.begin()), 299 request_data)); 300 301 // Check that both, hostPrefix and hostSuffix are evaluated. 302 GURL https_foo_url("https://foo.example.com"); 303 request_data.url_match_ids = matcher.MatchURL(https_foo_url); 304 EXPECT_EQ(0u, request_data.url_match_ids.size()); 305 scoped_ptr<net::URLRequest> https_foo_request(context.CreateRequest( 306 https_foo_url, net::DEFAULT_PRIORITY, NULL, NULL)); 307 data.request = https_foo_request.get(); 308 EXPECT_FALSE(result->IsFulfilled(-1, request_data)); 309 } 310 311 TEST(WebRequestConditionTest, TestPortFilter) { 312 // Necessary for TestURLRequest. 313 base::MessageLoopForIO message_loop; 314 URLMatcher matcher; 315 316 WebRequestConditionSet::AnyVector conditions; 317 conditions.push_back(linked_ptr<base::Value>(base::test::ParseJson( 318 "{ \n" 319 " \"instanceType\": \"declarativeWebRequest.RequestMatcher\", \n" 320 " \"url\": { \n" 321 " \"ports\": [80, [1000, 1010]], \n" // Allow 80;1000-1010. 322 " \"hostSuffix\": \"example.com\", \n" 323 " }, \n" 324 "}").release())); 325 326 // Test insertion 327 std::string error; 328 scoped_ptr<WebRequestConditionSet> result = WebRequestConditionSet::Create( 329 NULL, matcher.condition_factory(), conditions, &error); 330 EXPECT_EQ("", error); 331 ASSERT_TRUE(result.get()); 332 EXPECT_EQ(1u, result->conditions().size()); 333 334 // Tell the URLMatcher about our shiny new patterns. 335 URLMatcherConditionSet::Vector url_matcher_condition_set; 336 result->GetURLMatcherConditionSets(&url_matcher_condition_set); 337 matcher.AddConditionSets(url_matcher_condition_set); 338 339 std::set<URLMatcherConditionSet::ID> url_match_ids; 340 341 // Test various URLs. 342 GURL http_url("http://www.example.com"); 343 net::TestURLRequestContext context; 344 scoped_ptr<net::URLRequest> http_request(context.CreateRequest( 345 http_url, net::DEFAULT_PRIORITY, NULL, NULL)); 346 url_match_ids = matcher.MatchURL(http_url); 347 ASSERT_EQ(1u, url_match_ids.size()); 348 349 GURL http_url_80("http://www.example.com:80"); 350 scoped_ptr<net::URLRequest> http_request_80(context.CreateRequest( 351 http_url_80, net::DEFAULT_PRIORITY, NULL, NULL)); 352 url_match_ids = matcher.MatchURL(http_url_80); 353 ASSERT_EQ(1u, url_match_ids.size()); 354 355 GURL http_url_1000("http://www.example.com:1000"); 356 scoped_ptr<net::URLRequest> http_request_1000(context.CreateRequest( 357 http_url_1000, net::DEFAULT_PRIORITY, NULL, NULL)); 358 url_match_ids = matcher.MatchURL(http_url_1000); 359 ASSERT_EQ(1u, url_match_ids.size()); 360 361 GURL http_url_2000("http://www.example.com:2000"); 362 scoped_ptr<net::URLRequest> http_request_2000(context.CreateRequest( 363 http_url_2000, net::DEFAULT_PRIORITY, NULL, NULL)); 364 url_match_ids = matcher.MatchURL(http_url_2000); 365 ASSERT_EQ(0u, url_match_ids.size()); 366 } 367 368 // Create a condition with two attributes: one on the request header and one on 369 // the response header. The Create() method should fail and complain that it is 370 // impossible that both conditions are fulfilled at the same time. 371 TEST(WebRequestConditionTest, ConditionsWithConflictingStages) { 372 // Necessary for TestURLRequest. 373 base::MessageLoopForIO message_loop; 374 URLMatcher matcher; 375 376 std::string error; 377 scoped_ptr<WebRequestCondition> result; 378 379 // Test error on incompatible application stages for involved attributes. 380 error.clear(); 381 result = WebRequestCondition::Create( 382 NULL, 383 matcher.condition_factory(), 384 *base::test::ParseJson( 385 "{ \n" 386 " \"instanceType\": \"declarativeWebRequest.RequestMatcher\", \n" 387 // Pass a JS array with one empty object to each of the header 388 // filters. 389 " \"requestHeaders\": [{}], \n" 390 " \"responseHeaders\": [{}], \n" 391 "}"), 392 &error); 393 EXPECT_FALSE(error.empty()); 394 EXPECT_FALSE(result.get()); 395 } 396 397 } // namespace extensions 398