1// Copyright 2014 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 <algorithm>
6
7#include "extensions/common/error_utils.h"
8#include "extensions/common/manifest_constants.h"
9#include "extensions/common/manifest_handlers/externally_connectable.h"
10#include "extensions/common/manifest_test.h"
11#include "extensions/common/permissions/permissions_data.h"
12#include "testing/gmock/include/gmock/gmock.h"
13#include "testing/gtest/include/gtest/gtest.h"
14
15using testing::ElementsAre;
16
17namespace extensions {
18
19namespace errors = externally_connectable_errors;
20
21class ExternallyConnectableTest : public ManifestTest {
22 public:
23  ExternallyConnectableTest() {}
24  virtual ~ExternallyConnectableTest() {}
25
26 protected:
27  ExternallyConnectableInfo* GetExternallyConnectableInfo(
28      scoped_refptr<Extension> extension) {
29    return static_cast<ExternallyConnectableInfo*>(
30        extension->GetManifestData(manifest_keys::kExternallyConnectable));
31  }
32};
33
34TEST_F(ExternallyConnectableTest, IDsAndMatches) {
35  scoped_refptr<Extension> extension =
36      LoadAndExpectSuccess("externally_connectable_ids_and_matches.json");
37  ASSERT_TRUE(extension.get());
38
39  EXPECT_TRUE(extension->permissions_data()->HasAPIPermission(
40      APIPermission::kWebConnectable));
41
42  ExternallyConnectableInfo* info =
43      ExternallyConnectableInfo::Get(extension.get());
44  ASSERT_TRUE(info);
45
46  EXPECT_THAT(info->ids,
47              ElementsAre("abcdefghijklmnopabcdefghijklmnop",
48                          "ponmlkjihgfedcbaponmlkjihgfedcba"));
49
50  EXPECT_FALSE(info->all_ids);
51
52  EXPECT_TRUE(info->matches.MatchesURL(GURL("http://example.com")));
53  EXPECT_TRUE(info->matches.MatchesURL(GURL("http://example.com/")));
54  EXPECT_FALSE(info->matches.MatchesURL(GURL("http://example.com/index.html")));
55
56  EXPECT_TRUE(info->matches.MatchesURL(GURL("http://google.com")));
57  EXPECT_TRUE(info->matches.MatchesURL(GURL("http://google.com/")));
58  EXPECT_TRUE(info->matches.MatchesURL(GURL("http://google.com/index.html")));
59  EXPECT_TRUE(info->matches.MatchesURL(GURL("http://www.google.com")));
60  EXPECT_TRUE(info->matches.MatchesURL(GURL("http://www.google.com/")));
61  EXPECT_TRUE(info->matches.MatchesURL(GURL("https://google.com")));
62  EXPECT_TRUE(info->matches.MatchesURL(GURL("https://google.com/")));
63
64  EXPECT_TRUE(info->matches.MatchesURL(GURL("http://build.chromium.org")));
65  EXPECT_TRUE(info->matches.MatchesURL(GURL("http://build.chromium.org/")));
66  EXPECT_TRUE(
67      info->matches.MatchesURL(GURL("http://build.chromium.org/index.html")));
68  EXPECT_FALSE(info->matches.MatchesURL(GURL("https://build.chromium.org")));
69  EXPECT_FALSE(info->matches.MatchesURL(GURL("https://build.chromium.org/")));
70  EXPECT_FALSE(
71      info->matches.MatchesURL(GURL("http://foo.chromium.org/index.html")));
72
73  EXPECT_FALSE(info->matches.MatchesURL(GURL("http://yahoo.com")));
74  EXPECT_FALSE(info->matches.MatchesURL(GURL("http://yahoo.com/")));
75
76  // TLD-style patterns should match just the TLD.
77  EXPECT_TRUE(info->matches.MatchesURL(GURL("http://appspot.com/foo.html")));
78  EXPECT_TRUE(info->matches.MatchesURL(GURL("http://com")));
79  EXPECT_TRUE(info->matches.MatchesURL(GURL("http://go/here")));
80
81  // TLD-style patterns should *not* match any subdomains of the TLD.
82  EXPECT_FALSE(
83      info->matches.MatchesURL(GURL("http://codereview.appspot.com/foo.html")));
84  EXPECT_FALSE(
85      info->matches.MatchesURL(GURL("http://chromium.com/index.html")));
86  EXPECT_FALSE(info->matches.MatchesURL(GURL("http://here.go/somewhere")));
87
88  // Paths that don't have any wildcards should match the exact domain, but
89  // ignore the trailing slash. This is kind of a corner case, so let's test it.
90  EXPECT_TRUE(info->matches.MatchesURL(GURL("http://no.wildcard.path")));
91  EXPECT_TRUE(info->matches.MatchesURL(GURL("http://no.wildcard.path/")));
92  EXPECT_FALSE(
93      info->matches.MatchesURL(GURL("http://no.wildcard.path/index.html")));
94}
95
96TEST_F(ExternallyConnectableTest, IDs) {
97  scoped_refptr<Extension> extension =
98      LoadAndExpectSuccess("externally_connectable_ids.json");
99  ASSERT_TRUE(extension.get());
100
101  EXPECT_FALSE(extension->permissions_data()->HasAPIPermission(
102      APIPermission::kWebConnectable));
103
104  ExternallyConnectableInfo* info =
105      ExternallyConnectableInfo::Get(extension.get());
106  ASSERT_TRUE(info);
107
108  EXPECT_THAT(info->ids,
109              ElementsAre("abcdefghijklmnopabcdefghijklmnop",
110                          "ponmlkjihgfedcbaponmlkjihgfedcba"));
111
112  EXPECT_FALSE(info->all_ids);
113
114  EXPECT_FALSE(info->matches.MatchesURL(GURL("http://google.com/index.html")));
115}
116
117TEST_F(ExternallyConnectableTest, Matches) {
118  scoped_refptr<Extension> extension =
119      LoadAndExpectSuccess("externally_connectable_matches.json");
120  ASSERT_TRUE(extension.get());
121
122  EXPECT_TRUE(extension->permissions_data()->HasAPIPermission(
123      APIPermission::kWebConnectable));
124
125  ExternallyConnectableInfo* info =
126      ExternallyConnectableInfo::Get(extension.get());
127  ASSERT_TRUE(info);
128
129  EXPECT_THAT(info->ids, ElementsAre());
130
131  EXPECT_FALSE(info->all_ids);
132
133  EXPECT_FALSE(info->accepts_tls_channel_id);
134
135  EXPECT_TRUE(info->matches.MatchesURL(GURL("http://example.com")));
136  EXPECT_TRUE(info->matches.MatchesURL(GURL("http://example.com/")));
137  EXPECT_FALSE(info->matches.MatchesURL(GURL("http://example.com/index.html")));
138
139  EXPECT_TRUE(info->matches.MatchesURL(GURL("http://google.com")));
140  EXPECT_TRUE(info->matches.MatchesURL(GURL("http://google.com/")));
141  EXPECT_TRUE(info->matches.MatchesURL(GURL("http://google.com/index.html")));
142  EXPECT_TRUE(info->matches.MatchesURL(GURL("http://www.google.com")));
143  EXPECT_TRUE(info->matches.MatchesURL(GURL("http://www.google.com/")));
144  EXPECT_TRUE(info->matches.MatchesURL(GURL("https://google.com")));
145  EXPECT_TRUE(info->matches.MatchesURL(GURL("https://google.com/")));
146
147  EXPECT_TRUE(info->matches.MatchesURL(GURL("http://build.chromium.org")));
148  EXPECT_TRUE(info->matches.MatchesURL(GURL("http://build.chromium.org/")));
149  EXPECT_TRUE(
150      info->matches.MatchesURL(GURL("http://build.chromium.org/index.html")));
151  EXPECT_FALSE(info->matches.MatchesURL(GURL("https://build.chromium.org")));
152  EXPECT_FALSE(info->matches.MatchesURL(GURL("https://build.chromium.org/")));
153  EXPECT_FALSE(
154      info->matches.MatchesURL(GURL("http://foo.chromium.org/index.html")));
155
156  EXPECT_FALSE(info->matches.MatchesURL(GURL("http://yahoo.com")));
157  EXPECT_FALSE(info->matches.MatchesURL(GURL("http://yahoo.com/")));
158}
159
160TEST_F(ExternallyConnectableTest, MatchesWithTlsChannelId) {
161  scoped_refptr<Extension> extension = LoadAndExpectSuccess(
162      "externally_connectable_matches_tls_channel_id.json");
163  ASSERT_TRUE(extension.get());
164
165  EXPECT_TRUE(extension->permissions_data()->HasAPIPermission(
166      APIPermission::kWebConnectable));
167
168  ExternallyConnectableInfo* info =
169      ExternallyConnectableInfo::Get(extension.get());
170  ASSERT_TRUE(info);
171
172  EXPECT_THAT(info->ids, ElementsAre());
173
174  EXPECT_FALSE(info->all_ids);
175
176  EXPECT_TRUE(info->accepts_tls_channel_id);
177
178  // The matches portion of the manifest is identical to those in
179  // externally_connectable_matches, so only a subset of the Matches tests is
180  // repeated here.
181  EXPECT_TRUE(info->matches.MatchesURL(GURL("http://example.com")));
182  EXPECT_FALSE(info->matches.MatchesURL(GURL("http://example.com/index.html")));
183}
184
185TEST_F(ExternallyConnectableTest, AllIDs) {
186  scoped_refptr<Extension> extension =
187      LoadAndExpectSuccess("externally_connectable_all_ids.json");
188  ASSERT_TRUE(extension.get());
189
190  EXPECT_FALSE(extension->permissions_data()->HasAPIPermission(
191      APIPermission::kWebConnectable));
192
193  ExternallyConnectableInfo* info =
194      ExternallyConnectableInfo::Get(extension.get());
195  ASSERT_TRUE(info);
196
197  EXPECT_THAT(info->ids,
198              ElementsAre("abcdefghijklmnopabcdefghijklmnop",
199                          "ponmlkjihgfedcbaponmlkjihgfedcba"));
200
201  EXPECT_TRUE(info->all_ids);
202
203  EXPECT_FALSE(info->matches.MatchesURL(GURL("http://google.com/index.html")));
204}
205
206TEST_F(ExternallyConnectableTest, IdCanConnect) {
207  // Not in order to test that ExternallyConnectableInfo sorts it.
208  std::string matches_ids_array[] = {"g", "h", "c", "i", "a", "z", "b"};
209  std::vector<std::string> matches_ids(
210      matches_ids_array, matches_ids_array + arraysize(matches_ids_array));
211
212  std::string nomatches_ids_array[] = {"2", "3", "1"};
213
214  // all_ids = false.
215  {
216    ExternallyConnectableInfo info(URLPatternSet(), matches_ids, false, false);
217    for (size_t i = 0; i < matches_ids.size(); ++i)
218      EXPECT_TRUE(info.IdCanConnect(matches_ids[i]));
219    for (size_t i = 0; i < arraysize(nomatches_ids_array); ++i)
220      EXPECT_FALSE(info.IdCanConnect(nomatches_ids_array[i]));
221  }
222
223  // all_ids = true.
224  {
225    ExternallyConnectableInfo info(URLPatternSet(), matches_ids, true, false);
226    for (size_t i = 0; i < matches_ids.size(); ++i)
227      EXPECT_TRUE(info.IdCanConnect(matches_ids[i]));
228    for (size_t i = 0; i < arraysize(nomatches_ids_array); ++i)
229      EXPECT_TRUE(info.IdCanConnect(nomatches_ids_array[i]));
230  }
231}
232
233TEST_F(ExternallyConnectableTest, ErrorWrongFormat) {
234  LoadAndExpectError("externally_connectable_error_wrong_format.json",
235                     "expected dictionary, got string");
236}
237
238TEST_F(ExternallyConnectableTest, ErrorBadID) {
239  LoadAndExpectError(
240      "externally_connectable_bad_id.json",
241      ErrorUtils::FormatErrorMessage(errors::kErrorInvalidId, "badid"));
242}
243
244TEST_F(ExternallyConnectableTest, ErrorBadMatches) {
245  LoadAndExpectError("externally_connectable_error_bad_matches.json",
246                     ErrorUtils::FormatErrorMessage(
247                         errors::kErrorInvalidMatchPattern, "www.yahoo.com"));
248}
249
250TEST_F(ExternallyConnectableTest, WarningNoAllURLs) {
251  scoped_refptr<Extension> extension = LoadAndExpectWarning(
252      "externally_connectable_error_all_urls.json",
253      ErrorUtils::FormatErrorMessage(errors::kErrorWildcardHostsNotAllowed,
254                                     "<all_urls>"));
255  ExternallyConnectableInfo* info = GetExternallyConnectableInfo(extension);
256  EXPECT_FALSE(info->matches.MatchesAllURLs());
257  EXPECT_FALSE(info->matches.ContainsPattern(
258      URLPattern(URLPattern::SCHEME_ALL, "<all_urls>")));
259  EXPECT_TRUE(info->matches.MatchesURL(GURL("https://example.com")));
260  EXPECT_TRUE(info->matches.MatchesURL(GURL("http://build.chromium.org")));
261}
262
263TEST_F(ExternallyConnectableTest, AllURLsNotWhitelisted) {
264  scoped_refptr<Extension> extension = LoadAndExpectSuccess(
265      "externally_connectable_all_urls_not_whitelisted.json");
266  ExternallyConnectableInfo* info = GetExternallyConnectableInfo(extension);
267  EXPECT_FALSE(info->matches.MatchesAllURLs());
268}
269
270TEST_F(ExternallyConnectableTest, AllURLsWhitelisted) {
271  scoped_refptr<Extension> extension =
272      LoadAndExpectSuccess("externally_connectable_all_urls_whitelisted.json");
273  ExternallyConnectableInfo* info = GetExternallyConnectableInfo(extension);
274  EXPECT_TRUE(info->matches.MatchesAllURLs());
275  URLPattern pattern(URLPattern::SCHEME_ALL, "<all_urls>");
276  EXPECT_TRUE(info->matches.ContainsPattern(pattern));
277  EXPECT_TRUE(info->matches.MatchesURL(GURL("https://example.com")));
278  EXPECT_TRUE(info->matches.MatchesURL(GURL("http://build.chromium.org")));
279}
280
281TEST_F(ExternallyConnectableTest, WarningWildcardHost) {
282  scoped_refptr<Extension> extension = LoadAndExpectWarning(
283      "externally_connectable_error_wildcard_host.json",
284      ErrorUtils::FormatErrorMessage(errors::kErrorWildcardHostsNotAllowed,
285                                     "http://*/*"));
286  ExternallyConnectableInfo* info = GetExternallyConnectableInfo(extension);
287  EXPECT_FALSE(info->matches.ContainsPattern(
288      URLPattern(URLPattern::SCHEME_ALL, "http://*/*")));
289  EXPECT_TRUE(info->matches.MatchesURL(GURL("https://example.com")));
290  EXPECT_TRUE(info->matches.MatchesURL(GURL("http://build.chromium.org")));
291}
292
293TEST_F(ExternallyConnectableTest, WarningNoTLD) {
294  scoped_refptr<Extension> extension = LoadAndExpectWarning(
295      "externally_connectable_error_tld.json",
296      ErrorUtils::FormatErrorMessage(errors::kErrorTopLevelDomainsNotAllowed,
297                                     "co.uk",
298                                     "http://*.co.uk/*"));
299  ExternallyConnectableInfo* info = GetExternallyConnectableInfo(extension);
300  EXPECT_FALSE(info->matches.ContainsPattern(
301      URLPattern(URLPattern::SCHEME_ALL, "http://*.co.uk/*")));
302  EXPECT_TRUE(info->matches.MatchesURL(GURL("https://example.com")));
303  EXPECT_TRUE(info->matches.MatchesURL(GURL("http://build.chromium.org")));
304}
305
306TEST_F(ExternallyConnectableTest, WarningNoEffectiveTLD) {
307  scoped_refptr<Extension> extension = LoadAndExpectWarning(
308      "externally_connectable_error_effective_tld.json",
309      ErrorUtils::FormatErrorMessage(errors::kErrorTopLevelDomainsNotAllowed,
310                                     "appspot.com",
311                                     "http://*.appspot.com/*"));
312  ExternallyConnectableInfo* info = GetExternallyConnectableInfo(extension);
313  EXPECT_FALSE(info->matches.ContainsPattern(
314      URLPattern(URLPattern::SCHEME_ALL, "http://*.appspot.com/*")));
315  EXPECT_TRUE(info->matches.MatchesURL(GURL("https://example.com")));
316  EXPECT_TRUE(info->matches.MatchesURL(GURL("http://build.chromium.org")));
317}
318
319TEST_F(ExternallyConnectableTest, WarningUnknownTLD) {
320  scoped_refptr<Extension> extension = LoadAndExpectWarning(
321      "externally_connectable_error_unknown_tld.json",
322      ErrorUtils::FormatErrorMessage(errors::kErrorTopLevelDomainsNotAllowed,
323                                     "notatld",
324                                     "http://*.notatld/*"));
325  ExternallyConnectableInfo* info = GetExternallyConnectableInfo(extension);
326  EXPECT_FALSE(info->matches.ContainsPattern(
327      URLPattern(URLPattern::SCHEME_ALL, "http://*.notatld/*")));
328  EXPECT_TRUE(info->matches.MatchesURL(GURL("https://example.com")));
329  EXPECT_TRUE(info->matches.MatchesURL(GURL("http://build.chromium.org")));
330}
331
332TEST_F(ExternallyConnectableTest, WarningNothingSpecified) {
333  LoadAndExpectWarning("externally_connectable_nothing_specified.json",
334                       errors::kErrorNothingSpecified);
335}
336
337}  // namespace extensions
338