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.
5 #include "chrome/browser/extensions/api/declarative_webrequest/webrequest_action.h"
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"
11 #include "base/message_loop/message_loop.h"
12 #include "base/path_service.h"
13 #include "base/test/values_test_util.h"
14 #include "base/time/time.h"
15 #include "base/values.h"
16 #include "chrome/browser/extensions/api/declarative_webrequest/request_stage.h"
17 #include "chrome/browser/extensions/api/declarative_webrequest/webrequest_condition.h"
18 #include "chrome/browser/extensions/api/declarative_webrequest/webrequest_constants.h"
19 #include "chrome/browser/extensions/api/web_request/web_request_api_helpers.h"
20 #include "chrome/common/chrome_paths.h"
21 #include "chrome/common/extensions/extension_constants.h"
22 #include "chrome/common/extensions/extension_test_util.h"
23 #include "content/public/test/test_browser_thread_bundle.h"
24 #include "extensions/browser/info_map.h"
25 #include "extensions/common/extension.h"
26 #include "net/base/request_priority.h"
27 #include "net/http/http_response_headers.h"
28 #include "net/url_request/url_request_test_util.h"
29 #include "testing/gmock/include/gmock/gmock.h"
30 #include "testing/gtest/include/gtest/gtest.h"
32 using base::DictionaryValue
;
33 using base::ListValue
;
34 using extension_test_util::LoadManifestUnchecked
;
35 using testing::HasSubstr
;
37 namespace extensions
{
41 const char kUnknownActionType
[] = "unknownType";
43 scoped_ptr
<WebRequestActionSet
> CreateSetOfActions(const char* json
) {
44 scoped_ptr
<base::Value
> parsed_value(base::test::ParseJson(json
));
45 const base::ListValue
* parsed_list
;
46 CHECK(parsed_value
->GetAsList(&parsed_list
));
48 WebRequestActionSet::AnyVector actions
;
49 for (base::ListValue::const_iterator it
= parsed_list
->begin();
50 it
!= parsed_list
->end();
52 const base::DictionaryValue
* dict
;
53 CHECK((*it
)->GetAsDictionary(&dict
));
54 actions
.push_back(linked_ptr
<base::Value
>(dict
->DeepCopy()));
58 bool bad_message
= false;
60 scoped_ptr
<WebRequestActionSet
> action_set(
61 WebRequestActionSet::Create(NULL
, actions
, &error
, &bad_message
));
63 EXPECT_FALSE(bad_message
);
65 return action_set
.Pass();
70 namespace keys
= declarative_webrequest_constants
;
72 class WebRequestActionWithThreadsTest
: public testing::Test
{
74 WebRequestActionWithThreadsTest()
75 : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP
) {}
78 virtual void SetUp() OVERRIDE
;
80 // Creates a URL request for URL |url_string|, and applies the actions from
81 // |action_set| as if they were triggered by the extension with
82 // |extension_id| during |stage|.
83 bool ActionWorksOnRequest(const char* url_string
,
84 const std::string
& extension_id
,
85 const WebRequestActionSet
* action_set
,
88 // Expects a JSON description of an |action| requiring <all_urls> host
89 // permission, and checks that only an extensions with full host permissions
90 // can execute that action at |stage|. Also checks that the action is not
91 // executable for http://clients1.google.com.
92 void CheckActionNeedsAllUrls(const char* action
, RequestStage stage
);
94 net::TestURLRequestContext context_
;
96 // An extension with *.com host permissions and the DWR permission.
97 scoped_refptr
<Extension
> extension_
;
98 // An extension with host permissions for all URLs and the DWR permission.
99 scoped_refptr
<Extension
> extension_all_urls_
;
100 scoped_refptr
<InfoMap
> extension_info_map_
;
103 content::TestBrowserThreadBundle thread_bundle_
;
106 void WebRequestActionWithThreadsTest::SetUp() {
107 testing::Test::SetUp();
110 extension_
= LoadManifestUnchecked("permissions",
111 "web_request_com_host_permissions.json",
112 Manifest::INVALID_LOCATION
,
116 ASSERT_TRUE(extension_
.get()) << error
;
117 extension_all_urls_
=
118 LoadManifestUnchecked("permissions",
119 "web_request_all_host_permissions.json",
120 Manifest::INVALID_LOCATION
,
124 ASSERT_TRUE(extension_all_urls_
.get()) << error
;
125 extension_info_map_
= new InfoMap
;
126 ASSERT_TRUE(extension_info_map_
.get());
127 extension_info_map_
->AddExtension(
130 false /*incognito_enabled*/,
131 false /*notifications_disabled*/);
132 extension_info_map_
->AddExtension(extension_all_urls_
.get(),
134 false /*incognito_enabled*/,
135 false /*notifications_disabled*/);
138 bool WebRequestActionWithThreadsTest::ActionWorksOnRequest(
139 const char* url_string
,
140 const std::string
& extension_id
,
141 const WebRequestActionSet
* action_set
,
142 RequestStage stage
) {
143 net::TestURLRequest
regular_request(
144 GURL(url_string
), net::DEFAULT_PRIORITY
, NULL
, &context_
);
145 std::list
<LinkedPtrEventResponseDelta
> deltas
;
146 scoped_refptr
<net::HttpResponseHeaders
> headers(
147 new net::HttpResponseHeaders(""));
148 WebRequestData
request_data(®ular_request
, stage
, headers
.get());
149 std::set
<std::string
> ignored_tags
;
150 WebRequestAction::ApplyInfo apply_info
= { extension_info_map_
.get(),
152 false /*crosses_incognito*/,
153 &deltas
, &ignored_tags
};
154 action_set
->Apply(extension_id
, base::Time(), &apply_info
);
155 return (1u == deltas
.size() || 0u < ignored_tags
.size());
158 void WebRequestActionWithThreadsTest::CheckActionNeedsAllUrls(
160 RequestStage stage
) {
161 scoped_ptr
<WebRequestActionSet
> action_set(CreateSetOfActions(action
));
163 // Although |extension_| has matching *.com host permission, |action|
164 // is intentionally forbidden -- in Declarative WR, host permission
165 // for less than all URLs are ignored (except in SendMessageToExtension).
166 EXPECT_FALSE(ActionWorksOnRequest(
167 "http://test.com", extension_
->id(), action_set
.get(), stage
));
168 // With the "<all_urls>" host permission they are allowed.
169 EXPECT_TRUE(ActionWorksOnRequest(
170 "http://test.com", extension_all_urls_
->id(), action_set
.get(), stage
));
172 // The protected URLs should not be touched at all.
173 EXPECT_FALSE(ActionWorksOnRequest(
174 "http://clients1.google.com", extension_
->id(), action_set
.get(), stage
));
175 EXPECT_FALSE(ActionWorksOnRequest("http://clients1.google.com",
176 extension_all_urls_
->id(),
181 TEST(WebRequestActionTest
, CreateAction
) {
183 bool bad_message
= false;
184 scoped_refptr
<const WebRequestAction
> result
;
186 // Test wrong data type passed.
188 base::ListValue empty_list
;
189 result
= WebRequestAction::Create(NULL
, empty_list
, &error
, &bad_message
);
190 EXPECT_TRUE(bad_message
);
191 EXPECT_FALSE(result
.get());
193 // Test missing instanceType element.
194 base::DictionaryValue input
;
196 result
= WebRequestAction::Create(NULL
, input
, &error
, &bad_message
);
197 EXPECT_TRUE(bad_message
);
198 EXPECT_FALSE(result
.get());
200 // Test wrong instanceType element.
201 input
.SetString(keys::kInstanceTypeKey
, kUnknownActionType
);
203 result
= WebRequestAction::Create(NULL
, input
, &error
, &bad_message
);
204 EXPECT_NE("", error
);
205 EXPECT_FALSE(result
.get());
208 input
.SetString(keys::kInstanceTypeKey
, keys::kCancelRequestType
);
210 result
= WebRequestAction::Create(NULL
, input
, &error
, &bad_message
);
211 EXPECT_EQ("", error
);
212 EXPECT_FALSE(bad_message
);
213 ASSERT_TRUE(result
.get());
214 EXPECT_EQ(WebRequestAction::ACTION_CANCEL_REQUEST
, result
->type());
217 TEST(WebRequestActionTest
, CreateActionSet
) {
219 bool bad_message
= false;
220 scoped_ptr
<WebRequestActionSet
> result
;
222 WebRequestActionSet::AnyVector input
;
226 result
= WebRequestActionSet::Create(NULL
, input
, &error
, &bad_message
);
227 EXPECT_TRUE(error
.empty()) << error
;
228 EXPECT_FALSE(bad_message
);
229 ASSERT_TRUE(result
.get());
230 EXPECT_TRUE(result
->actions().empty());
231 EXPECT_EQ(std::numeric_limits
<int>::min(), result
->GetMinimumPriority());
233 base::DictionaryValue correct_action
;
234 correct_action
.SetString(keys::kInstanceTypeKey
, keys::kIgnoreRulesType
);
235 correct_action
.SetInteger(keys::kLowerPriorityThanKey
, 10);
236 base::DictionaryValue incorrect_action
;
237 incorrect_action
.SetString(keys::kInstanceTypeKey
, kUnknownActionType
);
240 input
.push_back(linked_ptr
<base::Value
>(correct_action
.DeepCopy()));
242 result
= WebRequestActionSet::Create(NULL
, input
, &error
, &bad_message
);
243 EXPECT_TRUE(error
.empty()) << error
;
244 EXPECT_FALSE(bad_message
);
245 ASSERT_TRUE(result
.get());
246 ASSERT_EQ(1u, result
->actions().size());
247 EXPECT_EQ(WebRequestAction::ACTION_IGNORE_RULES
,
248 result
->actions()[0]->type());
249 EXPECT_EQ(10, result
->GetMinimumPriority());
252 input
.push_back(linked_ptr
<base::Value
>(incorrect_action
.DeepCopy()));
254 result
= WebRequestActionSet::Create(NULL
, input
, &error
, &bad_message
);
255 EXPECT_NE("", error
);
256 EXPECT_FALSE(result
.get());
259 // Test capture group syntax conversions of WebRequestRedirectByRegExAction
260 TEST(WebRequestActionTest
, PerlToRe2Style
) {
261 #define CallPerlToRe2Style WebRequestRedirectByRegExAction::PerlToRe2Style
262 // foo$1bar -> foo\1bar
263 EXPECT_EQ("foo\\1bar", CallPerlToRe2Style("foo$1bar"));
264 // foo\$1bar -> foo$1bar
265 EXPECT_EQ("foo$1bar", CallPerlToRe2Style("foo\\$1bar"));
266 // foo\\$1bar -> foo\\\1bar
267 EXPECT_EQ("foo\\\\\\1bar", CallPerlToRe2Style("foo\\\\$1bar"));
269 EXPECT_EQ("foobar", CallPerlToRe2Style("foo\\bar"));
270 // foo$bar -> foo$bar
271 EXPECT_EQ("foo$bar", CallPerlToRe2Style("foo$bar"));
272 #undef CallPerlToRe2Style
275 TEST_F(WebRequestActionWithThreadsTest
, PermissionsToRedirect
) {
276 const char kAction
[] =
278 " \"instanceType\": \"declarativeWebRequest.RedirectRequest\","
279 " \"redirectUrl\": \"http://www.foobar.com\""
281 CheckActionNeedsAllUrls(kAction
, ON_BEFORE_REQUEST
);
282 CheckActionNeedsAllUrls(kAction
, ON_HEADERS_RECEIVED
);
285 TEST_F(WebRequestActionWithThreadsTest
, PermissionsToRedirectByRegEx
) {
286 const char kAction
[] =
288 " \"instanceType\": \"declarativeWebRequest.RedirectByRegEx\","
290 " \"to\": \"http://www.foobar.com\""
292 CheckActionNeedsAllUrls(kAction
, ON_BEFORE_REQUEST
);
295 TEST_F(WebRequestActionWithThreadsTest
, PermissionsToSetRequestHeader
) {
296 const char kAction
[] =
298 " \"instanceType\": \"declarativeWebRequest.SetRequestHeader\","
299 " \"name\": \"testname\","
300 " \"value\": \"testvalue\""
302 CheckActionNeedsAllUrls(kAction
, ON_BEFORE_SEND_HEADERS
);
305 TEST_F(WebRequestActionWithThreadsTest
, PermissionsToRemoveRequestHeader
) {
306 const char kAction
[] =
308 " \"instanceType\": \"declarativeWebRequest.RemoveRequestHeader\","
309 " \"name\": \"testname\""
311 CheckActionNeedsAllUrls(kAction
, ON_BEFORE_SEND_HEADERS
);
314 TEST_F(WebRequestActionWithThreadsTest
, PermissionsToAddResponseHeader
) {
315 const char kAction
[] =
317 " \"instanceType\": \"declarativeWebRequest.AddResponseHeader\","
318 " \"name\": \"testname\","
319 " \"value\": \"testvalue\""
321 CheckActionNeedsAllUrls(kAction
, ON_HEADERS_RECEIVED
);
324 TEST_F(WebRequestActionWithThreadsTest
, PermissionsToRemoveResponseHeader
) {
325 const char kAction
[] =
327 " \"instanceType\": \"declarativeWebRequest.RemoveResponseHeader\","
328 " \"name\": \"testname\""
330 CheckActionNeedsAllUrls(kAction
, ON_HEADERS_RECEIVED
);
333 TEST_F(WebRequestActionWithThreadsTest
, PermissionsToSendMessageToExtension
) {
334 const char kAction
[] =
336 " \"instanceType\": \"declarativeWebRequest.SendMessageToExtension\","
337 " \"message\": \"testtext\""
339 scoped_ptr
<WebRequestActionSet
> action_set(CreateSetOfActions(kAction
));
341 // For sending messages, specific host permissions actually matter.
342 EXPECT_TRUE(ActionWorksOnRequest("http://test.com",
346 // With the "<all_urls>" host permission they are allowed.
347 EXPECT_TRUE(ActionWorksOnRequest("http://test.com",
348 extension_all_urls_
->id(),
352 // The protected URLs should not be touched at all.
353 EXPECT_FALSE(ActionWorksOnRequest("http://clients1.google.com",
357 EXPECT_FALSE(ActionWorksOnRequest("http://clients1.google.com",
358 extension_all_urls_
->id(),
363 TEST_F(WebRequestActionWithThreadsTest
, PermissionsToAddRequestCookie
) {
364 const char kAction
[] =
366 " \"instanceType\": \"declarativeWebRequest.AddRequestCookie\","
367 " \"cookie\": { \"name\": \"cookiename\", \"value\": \"cookievalue\" }"
369 CheckActionNeedsAllUrls(kAction
, ON_BEFORE_SEND_HEADERS
);
372 TEST_F(WebRequestActionWithThreadsTest
, PermissionsToAddResponseCookie
) {
373 const char kAction
[] =
375 " \"instanceType\": \"declarativeWebRequest.AddResponseCookie\","
376 " \"cookie\": { \"name\": \"cookiename\", \"value\": \"cookievalue\" }"
378 CheckActionNeedsAllUrls(kAction
, ON_HEADERS_RECEIVED
);
381 TEST_F(WebRequestActionWithThreadsTest
, PermissionsToEditRequestCookie
) {
382 const char kAction
[] =
384 " \"instanceType\": \"declarativeWebRequest.EditRequestCookie\","
385 " \"filter\": { \"name\": \"cookiename\", \"value\": \"cookievalue\" },"
386 " \"modification\": { \"name\": \"name2\", \"value\": \"value2\" }"
388 CheckActionNeedsAllUrls(kAction
, ON_BEFORE_SEND_HEADERS
);
391 TEST_F(WebRequestActionWithThreadsTest
, PermissionsToEditResponseCookie
) {
392 const char kAction
[] =
394 " \"instanceType\": \"declarativeWebRequest.EditResponseCookie\","
395 " \"filter\": { \"name\": \"cookiename\", \"value\": \"cookievalue\" },"
396 " \"modification\": { \"name\": \"name2\", \"value\": \"value2\" }"
398 CheckActionNeedsAllUrls(kAction
, ON_HEADERS_RECEIVED
);
401 TEST_F(WebRequestActionWithThreadsTest
, PermissionsToRemoveRequestCookie
) {
402 const char kAction
[] =
404 " \"instanceType\": \"declarativeWebRequest.RemoveRequestCookie\","
405 " \"filter\": { \"name\": \"cookiename\", \"value\": \"cookievalue\" }"
407 CheckActionNeedsAllUrls(kAction
, ON_BEFORE_SEND_HEADERS
);
410 TEST_F(WebRequestActionWithThreadsTest
, PermissionsToRemoveResponseCookie
) {
411 const char kAction
[] =
413 " \"instanceType\": \"declarativeWebRequest.RemoveResponseCookie\","
414 " \"filter\": { \"name\": \"cookiename\", \"value\": \"cookievalue\" }"
416 CheckActionNeedsAllUrls(kAction
, ON_HEADERS_RECEIVED
);
419 TEST_F(WebRequestActionWithThreadsTest
, PermissionsToCancel
) {
420 const char kAction
[] =
422 " \"instanceType\": \"declarativeWebRequest.CancelRequest\""
424 scoped_ptr
<WebRequestActionSet
> action_set(CreateSetOfActions(kAction
));
426 // Cancelling requests works without full host permissions.
427 EXPECT_TRUE(ActionWorksOnRequest("http://test.org",
433 TEST_F(WebRequestActionWithThreadsTest
,
434 PermissionsToRedirectToTransparentImage
) {
435 const char kAction
[] =
437 " \"instanceType\": \"declarativeWebRequest.RedirectToTransparentImage\""
439 scoped_ptr
<WebRequestActionSet
> action_set(CreateSetOfActions(kAction
));
441 // Redirecting to transparent images works without full host permissions.
442 EXPECT_TRUE(ActionWorksOnRequest("http://test.org",
446 EXPECT_TRUE(ActionWorksOnRequest("http://test.org",
449 ON_HEADERS_RECEIVED
));
452 TEST_F(WebRequestActionWithThreadsTest
, PermissionsToRedirectToEmptyDocument
) {
453 const char kAction
[] =
455 " \"instanceType\": \"declarativeWebRequest.RedirectToEmptyDocument\""
457 scoped_ptr
<WebRequestActionSet
> action_set(CreateSetOfActions(kAction
));
459 // Redirecting to the empty document works without full host permissions.
460 EXPECT_TRUE(ActionWorksOnRequest("http://test.org",
464 EXPECT_TRUE(ActionWorksOnRequest("http://test.org",
467 ON_HEADERS_RECEIVED
));
470 TEST_F(WebRequestActionWithThreadsTest
, PermissionsToIgnore
) {
471 const char kAction
[] =
473 " \"instanceType\": \"declarativeWebRequest.IgnoreRules\","
474 " \"lowerPriorityThan\": 123,"
475 " \"hasTag\": \"some_tag\""
477 scoped_ptr
<WebRequestActionSet
> action_set(CreateSetOfActions(kAction
));
479 // Ignoring rules works without full host permissions.
480 EXPECT_TRUE(ActionWorksOnRequest("http://test.org",
486 TEST(WebRequestActionTest
, GetName
) {
487 const char kActions
[] =
489 " \"instanceType\": \"declarativeWebRequest.RedirectRequest\","
490 " \"redirectUrl\": \"http://www.foobar.com\""
493 " \"instanceType\": \"declarativeWebRequest.RedirectByRegEx\","
495 " \"to\": \"http://www.foobar.com\""
498 " \"instanceType\": \"declarativeWebRequest.SetRequestHeader\","
499 " \"name\": \"testname\","
500 " \"value\": \"testvalue\""
503 " \"instanceType\": \"declarativeWebRequest.RemoveRequestHeader\","
504 " \"name\": \"testname\""
507 " \"instanceType\": \"declarativeWebRequest.AddResponseHeader\","
508 " \"name\": \"testname\","
509 " \"value\": \"testvalue\""
512 " \"instanceType\": \"declarativeWebRequest.RemoveResponseHeader\","
513 " \"name\": \"testname\""
516 " \"instanceType\": \"declarativeWebRequest.SendMessageToExtension\","
517 " \"message\": \"testtext\""
520 " \"instanceType\": \"declarativeWebRequest.AddRequestCookie\","
521 " \"cookie\": { \"name\": \"cookiename\", \"value\": \"cookievalue\" }"
524 " \"instanceType\": \"declarativeWebRequest.AddResponseCookie\","
525 " \"cookie\": { \"name\": \"cookiename\", \"value\": \"cookievalue\" }"
528 " \"instanceType\": \"declarativeWebRequest.EditRequestCookie\","
529 " \"filter\": { \"name\": \"cookiename\", \"value\": \"cookievalue\" },"
530 " \"modification\": { \"name\": \"name2\", \"value\": \"value2\" }"
533 " \"instanceType\": \"declarativeWebRequest.EditResponseCookie\","
534 " \"filter\": { \"name\": \"cookiename\", \"value\": \"cookievalue\" },"
535 " \"modification\": { \"name\": \"name2\", \"value\": \"value2\" }"
538 " \"instanceType\": \"declarativeWebRequest.RemoveRequestCookie\","
539 " \"filter\": { \"name\": \"cookiename\", \"value\": \"cookievalue\" }"
542 " \"instanceType\": \"declarativeWebRequest.RemoveResponseCookie\","
543 " \"filter\": { \"name\": \"cookiename\", \"value\": \"cookievalue\" }"
546 " \"instanceType\": \"declarativeWebRequest.CancelRequest\""
549 " \"instanceType\": \"declarativeWebRequest.RedirectToTransparentImage\""
552 " \"instanceType\": \"declarativeWebRequest.RedirectToEmptyDocument\""
555 " \"instanceType\": \"declarativeWebRequest.IgnoreRules\","
556 " \"lowerPriorityThan\": 123,"
557 " \"hasTag\": \"some_tag\""
559 const char* kExpectedNames
[] = {
560 "declarativeWebRequest.RedirectRequest",
561 "declarativeWebRequest.RedirectByRegEx",
562 "declarativeWebRequest.SetRequestHeader",
563 "declarativeWebRequest.RemoveRequestHeader",
564 "declarativeWebRequest.AddResponseHeader",
565 "declarativeWebRequest.RemoveResponseHeader",
566 "declarativeWebRequest.SendMessageToExtension",
567 "declarativeWebRequest.AddRequestCookie",
568 "declarativeWebRequest.AddResponseCookie",
569 "declarativeWebRequest.EditRequestCookie",
570 "declarativeWebRequest.EditResponseCookie",
571 "declarativeWebRequest.RemoveRequestCookie",
572 "declarativeWebRequest.RemoveResponseCookie",
573 "declarativeWebRequest.CancelRequest",
574 "declarativeWebRequest.RedirectToTransparentImage",
575 "declarativeWebRequest.RedirectToEmptyDocument",
576 "declarativeWebRequest.IgnoreRules",
578 scoped_ptr
<WebRequestActionSet
> action_set(CreateSetOfActions(kActions
));
579 ASSERT_EQ(arraysize(kExpectedNames
), action_set
->actions().size());
581 for (WebRequestActionSet::Actions::const_iterator it
=
582 action_set
->actions().begin();
583 it
!= action_set
->actions().end();
585 EXPECT_EQ(kExpectedNames
[index
], (*it
)->GetName());
590 } // namespace extensions