| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/extensions/api/declarative_webrequest/webrequest_action
.h" | 5 #include "chrome/browser/extensions/api/declarative_webrequest/webrequest_action
.h" |
| 6 | 6 |
| 7 #include "base/files/file_path.h" |
| 8 #include "base/json/json_file_value_serializer.h" |
| 9 #include "base/memory/ref_counted.h" |
| 10 #include "base/memory/scoped_ptr.h" |
| 7 #include "base/message_loop.h" | 11 #include "base/message_loop.h" |
| 12 #include "base/path_service.h" |
| 13 #include "base/test/values_test_util.h" |
| 14 #include "base/time.h" |
| 8 #include "base/values.h" | 15 #include "base/values.h" |
| 16 #include "chrome/browser/extensions/api/declarative_webrequest/request_stage.h" |
| 9 #include "chrome/browser/extensions/api/declarative_webrequest/webrequest_condit
ion.h" | 17 #include "chrome/browser/extensions/api/declarative_webrequest/webrequest_condit
ion.h" |
| 10 #include "chrome/browser/extensions/api/declarative_webrequest/webrequest_consta
nts.h" | 18 #include "chrome/browser/extensions/api/declarative_webrequest/webrequest_consta
nts.h" |
| 11 #include "chrome/browser/extensions/api/web_request/web_request_api_helpers.h" | 19 #include "chrome/browser/extensions/api/web_request/web_request_api_helpers.h" |
| 20 #include "chrome/browser/extensions/extension_info_map.h" |
| 21 #include "chrome/common/chrome_paths.h" |
| 22 #include "chrome/common/extensions/extension.h" |
| 12 #include "chrome/common/extensions/extension_constants.h" | 23 #include "chrome/common/extensions/extension_constants.h" |
| 24 #include "chrome/common/extensions/extension_test_util.h" |
| 25 #include "content/public/test/test_browser_thread.h" |
| 26 #include "net/http/http_response_headers.h" |
| 13 #include "net/url_request/url_request_test_util.h" | 27 #include "net/url_request/url_request_test_util.h" |
| 14 #include "testing/gtest/include/gtest/gtest.h" | 28 #include "testing/gtest/include/gtest/gtest.h" |
| 15 | 29 |
| 16 namespace { | 30 using base::DictionaryValue; |
| 17 const char kUnknownActionType[] = "unknownType"; | 31 using base::ListValue; |
| 18 } // namespace | 32 using extension_test_util::LoadManifestUnchecked; |
| 19 | 33 |
| 20 namespace extensions { | 34 namespace extensions { |
| 21 | 35 |
| 36 namespace { |
| 37 |
| 38 const char kUnknownActionType[] = "unknownType"; |
| 39 |
| 40 scoped_ptr<WebRequestActionSet> CreateSetOfActions(const char* json) { |
| 41 scoped_ptr<Value> parsed_value(base::test::ParseJson(json)); |
| 42 const ListValue* parsed_list; |
| 43 CHECK(parsed_value->GetAsList(&parsed_list)); |
| 44 |
| 45 WebRequestActionSet::AnyVector actions; |
| 46 for (ListValue::const_iterator it = parsed_list->begin(); |
| 47 it != parsed_list->end(); |
| 48 ++it) { |
| 49 const DictionaryValue* dict; |
| 50 CHECK((*it)->GetAsDictionary(&dict)); |
| 51 actions.push_back(linked_ptr<base::Value>(dict->DeepCopy())); |
| 52 } |
| 53 |
| 54 std::string error; |
| 55 bool bad_message = false; |
| 56 |
| 57 scoped_ptr<WebRequestActionSet> action_set( |
| 58 WebRequestActionSet::Create(actions, &error, &bad_message)); |
| 59 EXPECT_EQ("", error); |
| 60 EXPECT_FALSE(bad_message); |
| 61 CHECK(action_set); |
| 62 return action_set.Pass(); |
| 63 } |
| 64 |
| 65 } // namespace |
| 66 |
| 22 namespace keys = declarative_webrequest_constants; | 67 namespace keys = declarative_webrequest_constants; |
| 23 | 68 |
| 69 class WebRequestActionWithThreadsTest : public testing::Test { |
| 70 public: |
| 71 WebRequestActionWithThreadsTest() |
| 72 : io_thread_(content::BrowserThread::IO, &message_loop_) {} |
| 73 |
| 74 protected: |
| 75 virtual void SetUp() OVERRIDE; |
| 76 |
| 77 // Creates a URL request for URL |url_string|, and applies the actions from |
| 78 // |action_set| as if they were triggered by the extension with |
| 79 // |extension_id| during |stage|. |
| 80 bool ActionWorksOnRequest(const char* url_string, |
| 81 const std::string& extension_id, |
| 82 const WebRequestActionSet* action_set, |
| 83 RequestStage stage); |
| 84 |
| 85 // Expects a JSON description of an |action| requiring <all_urls> host |
| 86 // permission, and checks that only an extensions with full host permissions |
| 87 // can execute that action at |stage|. Also checks that the action is not |
| 88 // executable for http://clients1.google.com. |
| 89 void CheckActionNeedsAllUrls(const char* action, RequestStage stage); |
| 90 |
| 91 net::TestURLRequestContext context_; |
| 92 |
| 93 // An extension with *.com host permissions and the DWR permission. |
| 94 scoped_refptr<Extension> extension_; |
| 95 // An extension with host permissions for all URLs and the DWR permission. |
| 96 scoped_refptr<Extension> extension_all_urls_; |
| 97 scoped_refptr<ExtensionInfoMap> extension_info_map_; |
| 98 |
| 99 private: |
| 100 MessageLoopForIO message_loop_; |
| 101 content::TestBrowserThread io_thread_; |
| 102 }; |
| 103 |
| 104 void WebRequestActionWithThreadsTest::SetUp() { |
| 105 testing::Test::SetUp(); |
| 106 |
| 107 std::string error; |
| 108 extension_ = LoadManifestUnchecked("permissions", |
| 109 "web_request_com_host_permissions.json", |
| 110 Manifest::INVALID_LOCATION, |
| 111 Extension::NO_FLAGS, |
| 112 "ext_id_1", |
| 113 &error); |
| 114 ASSERT_TRUE(extension_) << error; |
| 115 extension_all_urls_ = |
| 116 LoadManifestUnchecked("permissions", |
| 117 "web_request_all_host_permissions.json", |
| 118 Manifest::INVALID_LOCATION, |
| 119 Extension::NO_FLAGS, |
| 120 "ext_id_2", |
| 121 &error); |
| 122 ASSERT_TRUE(extension_all_urls_) << error; |
| 123 extension_info_map_ = new ExtensionInfoMap; |
| 124 ASSERT_TRUE(extension_info_map_); |
| 125 extension_info_map_->AddExtension( |
| 126 extension_.get(), base::Time::Now(), false /*incognito_enabled*/); |
| 127 extension_info_map_->AddExtension(extension_all_urls_.get(), |
| 128 base::Time::Now(), |
| 129 false /*incognito_enabled*/); |
| 130 } |
| 131 |
| 132 bool WebRequestActionWithThreadsTest::ActionWorksOnRequest( |
| 133 const char* url_string, |
| 134 const std::string& extension_id, |
| 135 const WebRequestActionSet* action_set, |
| 136 RequestStage stage) { |
| 137 net::TestURLRequest regular_request(GURL(url_string), NULL, &context_, NULL); |
| 138 std::list<LinkedPtrEventResponseDelta> deltas; |
| 139 scoped_refptr<net::HttpResponseHeaders> headers( |
| 140 new net::HttpResponseHeaders("")); |
| 141 WebRequestData request_data(®ular_request, stage, headers); |
| 142 std::set<std::string> ignored_tags; |
| 143 WebRequestAction::ApplyInfo apply_info = { |
| 144 extension_info_map_, request_data, false /*crosses_incognito*/, &deltas, |
| 145 &ignored_tags |
| 146 }; |
| 147 action_set->Apply(extension_id, base::Time(), &apply_info); |
| 148 return (1u == deltas.size() || 0u < ignored_tags.size()); |
| 149 } |
| 150 |
| 151 void WebRequestActionWithThreadsTest::CheckActionNeedsAllUrls( |
| 152 const char* action, |
| 153 RequestStage stage) { |
| 154 scoped_ptr<WebRequestActionSet> action_set(CreateSetOfActions(action)); |
| 155 |
| 156 // Although |extension_| has matching *.com host permission, |action| |
| 157 // is intentionally forbidden -- in Declarative WR, host permission |
| 158 // for less than all URLs are ignored (except in SendMessageToExtension). |
| 159 EXPECT_FALSE(ActionWorksOnRequest( |
| 160 "http://test.com", extension_->id(), action_set.get(), stage)); |
| 161 // With the "<all_urls>" host permission they are allowed. |
| 162 EXPECT_TRUE(ActionWorksOnRequest( |
| 163 "http://test.com", extension_all_urls_->id(), action_set.get(), stage)); |
| 164 |
| 165 // The protected URLs should not be touched at all. |
| 166 EXPECT_FALSE(ActionWorksOnRequest( |
| 167 "http://clients1.google.com", extension_->id(), action_set.get(), stage)); |
| 168 EXPECT_FALSE(ActionWorksOnRequest("http://clients1.google.com", |
| 169 extension_all_urls_->id(), |
| 170 action_set.get(), |
| 171 stage)); |
| 172 } |
| 173 |
| 24 TEST(WebRequestActionTest, CreateAction) { | 174 TEST(WebRequestActionTest, CreateAction) { |
| 25 std::string error; | 175 std::string error; |
| 26 bool bad_message = false; | 176 bool bad_message = false; |
| 27 scoped_ptr<WebRequestAction> result; | 177 scoped_ptr<WebRequestAction> result; |
| 28 | 178 |
| 29 // Test wrong data type passed. | 179 // Test wrong data type passed. |
| 30 error.clear(); | 180 error.clear(); |
| 31 base::ListValue empty_list; | 181 base::ListValue empty_list; |
| 32 result = WebRequestAction::Create(empty_list, &error, &bad_message); | 182 result = WebRequestAction::Create(empty_list, &error, &bad_message); |
| 33 EXPECT_TRUE(bad_message); | 183 EXPECT_TRUE(bad_message); |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 108 EXPECT_EQ("foo$1bar", CallPerlToRe2Style("foo\\$1bar")); | 258 EXPECT_EQ("foo$1bar", CallPerlToRe2Style("foo\\$1bar")); |
| 109 // foo\\$1bar -> foo\\\1bar | 259 // foo\\$1bar -> foo\\\1bar |
| 110 EXPECT_EQ("foo\\\\\\1bar", CallPerlToRe2Style("foo\\\\$1bar")); | 260 EXPECT_EQ("foo\\\\\\1bar", CallPerlToRe2Style("foo\\\\$1bar")); |
| 111 // foo\bar -> foobar | 261 // foo\bar -> foobar |
| 112 EXPECT_EQ("foobar", CallPerlToRe2Style("foo\\bar")); | 262 EXPECT_EQ("foobar", CallPerlToRe2Style("foo\\bar")); |
| 113 // foo$bar -> foo$bar | 263 // foo$bar -> foo$bar |
| 114 EXPECT_EQ("foo$bar", CallPerlToRe2Style("foo$bar")); | 264 EXPECT_EQ("foo$bar", CallPerlToRe2Style("foo$bar")); |
| 115 #undef CallPerlToRe2Style | 265 #undef CallPerlToRe2Style |
| 116 } | 266 } |
| 117 | 267 |
| 118 TEST(WebRequestActionTest, TestPermissions) { | 268 TEST_F(WebRequestActionWithThreadsTest, PermissionsToRedirect) { |
| 119 // Necessary for TestURLRequest. | 269 const char* action = |
| 120 MessageLoop message_loop(MessageLoop::TYPE_IO); | 270 "[{" |
| 121 net::TestURLRequestContext context; | 271 " \"instanceType\": \"declarativeWebRequest.RedirectRequest\"," |
| 122 | 272 " \"redirectUrl\": \"http://www.foobar.com\"" |
| 123 std::string error; | 273 "}]"; |
| 124 bool bad_message = false; | 274 CheckActionNeedsAllUrls(action, ON_BEFORE_REQUEST); |
| 125 scoped_ptr<WebRequestActionSet> action_set; | 275 } |
| 126 | 276 |
| 127 // Setup redirect to http://www.foobar.com. | 277 TEST_F(WebRequestActionWithThreadsTest, PermissionsToRedirectByRegEx) { |
| 128 base::DictionaryValue redirect; | 278 const char* action = |
| 129 redirect.SetString(keys::kInstanceTypeKey, keys::kRedirectRequestType); | 279 "[{" |
| 130 redirect.SetString(keys::kRedirectUrlKey, "http://www.foobar.com"); | 280 " \"instanceType\": \"declarativeWebRequest.RedirectByRegEx\"," |
| 131 | 281 " \"from\": \".*\"," |
| 132 WebRequestActionSet::AnyVector actions; | 282 " \"to\": \"http://www.foobar.com\"" |
| 133 actions.push_back(linked_ptr<base::Value>(redirect.DeepCopy())); | 283 "}]"; |
| 134 | 284 CheckActionNeedsAllUrls(action, ON_BEFORE_REQUEST); |
| 135 action_set = WebRequestActionSet::Create(actions, &error, &bad_message); | 285 } |
| 136 EXPECT_EQ("", error); | 286 |
| 137 EXPECT_FALSE(bad_message); | 287 TEST_F(WebRequestActionWithThreadsTest, PermissionsToSetRequestHeader) { |
| 138 | 288 const char* action = |
| 139 // Check that redirect works on regular URLs but not on protected URLs. | 289 "[{" |
| 140 net::TestURLRequest regular_request( | 290 " \"instanceType\": \"declarativeWebRequest.SetRequestHeader\"," |
| 141 GURL("http://test.com"), NULL, &context, NULL); | 291 " \"name\": \"testname\"," |
| 142 std::list<LinkedPtrEventResponseDelta> deltas; | 292 " \"value\": \"testvalue\"" |
| 143 WebRequestData request_data(®ular_request, ON_BEFORE_REQUEST); | 293 "}]"; |
| 144 WebRequestAction::ApplyInfo apply_info = { | 294 CheckActionNeedsAllUrls(action, ON_BEFORE_SEND_HEADERS); |
| 145 NULL, request_data, false, &deltas | 295 } |
| 146 }; | 296 |
| 147 action_set->Apply("ext1", base::Time(), &apply_info); | 297 TEST_F(WebRequestActionWithThreadsTest, PermissionsToRemoveRequestHeader) { |
| 148 EXPECT_EQ(1u, deltas.size()); | 298 const char* action = |
| 149 | 299 "[{" |
| 150 net::TestURLRequest protected_request(GURL("http://clients1.google.com"), | 300 " \"instanceType\": \"declarativeWebRequest.RemoveRequestHeader\"," |
| 151 NULL, &context, NULL); | 301 " \"name\": \"testname\"" |
| 152 deltas.clear(); | 302 "}]"; |
| 153 request_data = WebRequestData(&protected_request, ON_BEFORE_REQUEST); | 303 CheckActionNeedsAllUrls(action, ON_BEFORE_SEND_HEADERS); |
| 154 // Note that we just updated the request_data reference in apply_info. | 304 } |
| 155 action_set->Apply("ext1", base::Time(), &apply_info); | 305 |
| 156 EXPECT_EQ(0u, deltas.size()); | 306 TEST_F(WebRequestActionWithThreadsTest, PermissionsToAddResponseHeader) { |
| 307 const char* action = |
| 308 "[{" |
| 309 " \"instanceType\": \"declarativeWebRequest.AddResponseHeader\"," |
| 310 " \"name\": \"testname\"," |
| 311 " \"value\": \"testvalue\"" |
| 312 "}]"; |
| 313 CheckActionNeedsAllUrls(action, ON_HEADERS_RECEIVED); |
| 314 } |
| 315 |
| 316 TEST_F(WebRequestActionWithThreadsTest, PermissionsToRemoveResponseHeader) { |
| 317 const char* action = |
| 318 "[{" |
| 319 " \"instanceType\": \"declarativeWebRequest.RemoveResponseHeader\"," |
| 320 " \"name\": \"testname\"" |
| 321 "}]"; |
| 322 CheckActionNeedsAllUrls(action, ON_HEADERS_RECEIVED); |
| 323 } |
| 324 |
| 325 TEST_F(WebRequestActionWithThreadsTest, PermissionsToSendMessageToExtension) { |
| 326 const char* action = |
| 327 "[{" |
| 328 " \"instanceType\": \"declarativeWebRequest.SendMessageToExtension\"," |
| 329 " \"message\": \"testtext\"" |
| 330 "}]"; |
| 331 scoped_ptr<WebRequestActionSet> action_set(CreateSetOfActions(action)); |
| 332 |
| 333 // For sending messages, specific host permissions actually matter. |
| 334 EXPECT_TRUE(ActionWorksOnRequest("http://test.com", |
| 335 extension_->id(), |
| 336 action_set.get(), |
| 337 ON_BEFORE_REQUEST)); |
| 338 // With the "<all_urls>" host permission they are allowed. |
| 339 EXPECT_TRUE(ActionWorksOnRequest("http://test.com", |
| 340 extension_all_urls_->id(), |
| 341 action_set.get(), |
| 342 ON_BEFORE_REQUEST)); |
| 343 |
| 344 // The protected URLs should not be touched at all. |
| 345 EXPECT_FALSE(ActionWorksOnRequest("http://clients1.google.com", |
| 346 extension_->id(), |
| 347 action_set.get(), |
| 348 ON_BEFORE_REQUEST)); |
| 349 EXPECT_FALSE(ActionWorksOnRequest("http://clients1.google.com", |
| 350 extension_all_urls_->id(), |
| 351 action_set.get(), |
| 352 ON_BEFORE_REQUEST)); |
| 353 } |
| 354 |
| 355 TEST_F(WebRequestActionWithThreadsTest, PermissionsToAddRequestCookie) { |
| 356 const char* action = |
| 357 "[{" |
| 358 " \"instanceType\": \"declarativeWebRequest.AddRequestCookie\"," |
| 359 " \"cookie\": { \"name\": \"cookiename\", \"value\": \"cookievalue\" }" |
| 360 "}]"; |
| 361 CheckActionNeedsAllUrls(action, ON_BEFORE_SEND_HEADERS); |
| 362 } |
| 363 |
| 364 TEST_F(WebRequestActionWithThreadsTest, PermissionsToAddResponseCookie) { |
| 365 const char* action = |
| 366 "[{" |
| 367 " \"instanceType\": \"declarativeWebRequest.AddResponseCookie\"," |
| 368 " \"cookie\": { \"name\": \"cookiename\", \"value\": \"cookievalue\" }" |
| 369 "}]"; |
| 370 CheckActionNeedsAllUrls(action, ON_HEADERS_RECEIVED); |
| 371 } |
| 372 |
| 373 TEST_F(WebRequestActionWithThreadsTest, PermissionsToEditRequestCookie) { |
| 374 const char* action = |
| 375 "[{" |
| 376 " \"instanceType\": \"declarativeWebRequest.EditRequestCookie\"," |
| 377 " \"filter\": { \"name\": \"cookiename\", \"value\": \"cookievalue\" }," |
| 378 " \"modification\": { \"name\": \"name2\", \"value\": \"value2\" }" |
| 379 "}]"; |
| 380 CheckActionNeedsAllUrls(action, ON_BEFORE_SEND_HEADERS); |
| 381 } |
| 382 |
| 383 TEST_F(WebRequestActionWithThreadsTest, PermissionsToEditResponseCookie) { |
| 384 const char* action = |
| 385 "[{" |
| 386 " \"instanceType\": \"declarativeWebRequest.EditResponseCookie\"," |
| 387 " \"filter\": { \"name\": \"cookiename\", \"value\": \"cookievalue\" }," |
| 388 " \"modification\": { \"name\": \"name2\", \"value\": \"value2\" }" |
| 389 "}]"; |
| 390 CheckActionNeedsAllUrls(action, ON_HEADERS_RECEIVED); |
| 391 } |
| 392 |
| 393 TEST_F(WebRequestActionWithThreadsTest, PermissionsToRemoveRequestCookie) { |
| 394 const char* action = |
| 395 "[{" |
| 396 " \"instanceType\": \"declarativeWebRequest.RemoveRequestCookie\"," |
| 397 " \"filter\": { \"name\": \"cookiename\", \"value\": \"cookievalue\" }" |
| 398 "}]"; |
| 399 CheckActionNeedsAllUrls(action, ON_BEFORE_SEND_HEADERS); |
| 400 } |
| 401 |
| 402 TEST_F(WebRequestActionWithThreadsTest, PermissionsToRemoveResponseCookie) { |
| 403 const char* action = |
| 404 "[{" |
| 405 " \"instanceType\": \"declarativeWebRequest.RemoveResponseCookie\"," |
| 406 " \"filter\": { \"name\": \"cookiename\", \"value\": \"cookievalue\" }" |
| 407 "}]"; |
| 408 CheckActionNeedsAllUrls(action, ON_HEADERS_RECEIVED); |
| 409 } |
| 410 |
| 411 TEST_F(WebRequestActionWithThreadsTest, PermissionsToCancel) { |
| 412 const char* action = |
| 413 "[{" |
| 414 " \"instanceType\": \"declarativeWebRequest.CancelRequest\"" |
| 415 "}]"; |
| 416 scoped_ptr<WebRequestActionSet> action_set(CreateSetOfActions(action)); |
| 417 |
| 418 // Cancelling requests works without full host permissions. |
| 419 EXPECT_TRUE(ActionWorksOnRequest("http://test.org", |
| 420 extension_->id(), |
| 421 action_set.get(), |
| 422 ON_BEFORE_REQUEST)); |
| 423 } |
| 424 |
| 425 TEST_F(WebRequestActionWithThreadsTest, |
| 426 PermissionsToRedirectToTransparentImage) { |
| 427 const char* action = |
| 428 "[{" |
| 429 " \"instanceType\": \"declarativeWebRequest.RedirectToTransparentImage\"" |
| 430 "}]"; |
| 431 scoped_ptr<WebRequestActionSet> action_set(CreateSetOfActions(action)); |
| 432 |
| 433 // Redirecting to transparent images works without full host permissions. |
| 434 EXPECT_TRUE(ActionWorksOnRequest("http://test.org", |
| 435 extension_->id(), |
| 436 action_set.get(), |
| 437 ON_BEFORE_REQUEST)); |
| 438 } |
| 439 |
| 440 TEST_F(WebRequestActionWithThreadsTest, PermissionsToRedirectToEmptyDocument) { |
| 441 const char* action = |
| 442 "[{" |
| 443 " \"instanceType\": \"declarativeWebRequest.RedirectToEmptyDocument\"" |
| 444 "}]"; |
| 445 scoped_ptr<WebRequestActionSet> action_set(CreateSetOfActions(action)); |
| 446 |
| 447 // Redirecting to the empty document works without full host permissions. |
| 448 EXPECT_TRUE(ActionWorksOnRequest("http://test.org", |
| 449 extension_->id(), |
| 450 action_set.get(), |
| 451 ON_BEFORE_REQUEST)); |
| 452 } |
| 453 |
| 454 TEST_F(WebRequestActionWithThreadsTest, PermissionsToIgnore) { |
| 455 const char* action = |
| 456 "[{" |
| 457 " \"instanceType\": \"declarativeWebRequest.IgnoreRules\"," |
| 458 " \"lowerPriorityThan\": 123," |
| 459 " \"hasTag\": \"some_tag\"" |
| 460 "}]"; |
| 461 scoped_ptr<WebRequestActionSet> action_set(CreateSetOfActions(action)); |
| 462 |
| 463 // Ignoring rules works without full host permissions. |
| 464 EXPECT_TRUE(ActionWorksOnRequest("http://test.org", |
| 465 extension_->id(), |
| 466 action_set.get(), |
| 467 ON_BEFORE_REQUEST)); |
| 157 } | 468 } |
| 158 | 469 |
| 159 } // namespace extensions | 470 } // namespace extensions |
| OLD | NEW |