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
21using content::ResourceType;
22using url_matcher::URLMatcher;
23using url_matcher::URLMatcherConditionSet;
24
25namespace extensions {
26
27TEST(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
117TEST(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.
169TEST(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
242TEST(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
311TEST(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.
371TEST(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