1 // Copyright 2013 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/common/extensions/manifest_handlers/externally_connectable.h"
9 #include "base/stl_util.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "chrome/common/extensions/api/manifest_types.h"
12 #include "extensions/common/error_utils.h"
13 #include "extensions/common/manifest_constants.h"
14 #include "extensions/common/permissions/api_permission_set.h"
15 #include "extensions/common/permissions/permissions_data.h"
16 #include "extensions/common/url_pattern.h"
17 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
20 namespace rcd
= net::registry_controlled_domains
;
22 namespace extensions
{
24 namespace externally_connectable_errors
{
25 const char kErrorInvalidMatchPattern
[] = "Invalid match pattern '*'";
26 const char kErrorInvalidId
[] = "Invalid ID '*'";
27 const char kErrorNothingSpecified
[] =
28 "'externally_connectable' specifies neither 'matches' nor 'ids'; "
29 "nothing will be able to connect";
30 const char kErrorTopLevelDomainsNotAllowed
[] =
31 "\"*\" is an effective top level domain for which wildcard subdomains such "
32 "as \"*\" are not allowed";
33 const char kErrorWildcardHostsNotAllowed
[] =
34 "Wildcard domain patterns such as \"*\" are not allowed";
35 } // namespace externally_connectable_errors
37 namespace keys
= extensions::manifest_keys
;
38 namespace errors
= externally_connectable_errors
;
39 using api::manifest_types::ExternallyConnectable
;
43 const char kAllIds
[] = "*";
46 std::vector
<T
> Sorted(const std::vector
<T
>& in
) {
47 std::vector
<T
> out
= in
;
48 std::sort(out
.begin(), out
.end());
54 ExternallyConnectableHandler::ExternallyConnectableHandler() {}
56 ExternallyConnectableHandler::~ExternallyConnectableHandler() {}
58 bool ExternallyConnectableHandler::Parse(Extension
* extension
,
59 base::string16
* error
) {
60 const base::Value
* externally_connectable
= NULL
;
61 CHECK(extension
->manifest()->Get(keys::kExternallyConnectable
,
62 &externally_connectable
));
63 std::vector
<InstallWarning
> install_warnings
;
64 scoped_ptr
<ExternallyConnectableInfo
> info
=
65 ExternallyConnectableInfo::FromValue(*externally_connectable
,
70 if (!info
->matches
.is_empty()) {
71 PermissionsData::GetInitialAPIPermissions(extension
)->insert(
72 APIPermission::kWebConnectable
);
74 extension
->AddInstallWarnings(install_warnings
);
75 extension
->SetManifestData(keys::kExternallyConnectable
, info
.release());
79 const std::vector
<std::string
> ExternallyConnectableHandler::Keys() const {
80 return SingleKey(keys::kExternallyConnectable
);
84 ExternallyConnectableInfo
* ExternallyConnectableInfo::Get(
85 const Extension
* extension
) {
86 return static_cast<ExternallyConnectableInfo
*>(
87 extension
->GetManifestData(keys::kExternallyConnectable
));
91 scoped_ptr
<ExternallyConnectableInfo
> ExternallyConnectableInfo::FromValue(
92 const base::Value
& value
,
93 std::vector
<InstallWarning
>* install_warnings
,
94 base::string16
* error
) {
95 scoped_ptr
<ExternallyConnectable
> externally_connectable
=
96 ExternallyConnectable::FromValue(value
, error
);
97 if (!externally_connectable
)
98 return scoped_ptr
<ExternallyConnectableInfo
>();
100 URLPatternSet matches
;
102 if (externally_connectable
->matches
) {
103 for (std::vector
<std::string
>::iterator it
=
104 externally_connectable
->matches
->begin();
105 it
!= externally_connectable
->matches
->end(); ++it
) {
106 // Safe to use SCHEME_ALL here; externally_connectable gives a page ->
107 // extension communication path, not the other way.
108 URLPattern
pattern(URLPattern::SCHEME_ALL
);
109 if (pattern
.Parse(*it
) != URLPattern::PARSE_SUCCESS
) {
110 *error
= ErrorUtils::FormatErrorMessageUTF16(
111 errors::kErrorInvalidMatchPattern
, *it
);
112 return scoped_ptr
<ExternallyConnectableInfo
>();
115 // Wildcard hosts are not allowed.
116 if (pattern
.host().empty()) {
117 // Warning not error for forwards compatibility.
118 install_warnings
->push_back(InstallWarning(
119 ErrorUtils::FormatErrorMessage(
120 errors::kErrorWildcardHostsNotAllowed
, *it
),
121 keys::kExternallyConnectable
,
126 // Wildcards on subdomains of a TLD are not allowed.
127 size_t registry_length
= rcd::GetRegistryLength(
129 // This means that things that look like TLDs - the foobar in
130 // http://google.foobar - count as TLDs.
131 rcd::INCLUDE_UNKNOWN_REGISTRIES
,
132 // This means that effective TLDs like appspot.com count as TLDs;
133 // codereview.appspot.com and evil.appspot.com are different.
134 rcd::INCLUDE_PRIVATE_REGISTRIES
);
136 if (registry_length
== std::string::npos
) {
137 // The URL parsing combined with host().empty() should have caught this.
139 *error
= ErrorUtils::FormatErrorMessageUTF16(
140 errors::kErrorInvalidMatchPattern
, *it
);
141 return scoped_ptr
<ExternallyConnectableInfo
>();
144 // Broad match patterns like "*.com", "*.co.uk", and even "*.appspot.com"
145 // are not allowed. However just "appspot.com" is ok.
146 if (registry_length
== 0 && pattern
.match_subdomains()) {
147 // Warning not error for forwards compatibility.
148 install_warnings
->push_back(InstallWarning(
149 ErrorUtils::FormatErrorMessage(
150 errors::kErrorTopLevelDomainsNotAllowed
,
151 pattern
.host().c_str(),
153 keys::kExternallyConnectable
,
158 matches
.AddPattern(pattern
);
162 std::vector
<std::string
> ids
;
163 bool all_ids
= false;
165 if (externally_connectable
->ids
) {
166 for (std::vector
<std::string
>::iterator it
=
167 externally_connectable
->ids
->begin();
168 it
!= externally_connectable
->ids
->end(); ++it
) {
169 if (*it
== kAllIds
) {
171 } else if (Extension::IdIsValid(*it
)) {
174 *error
= ErrorUtils::FormatErrorMessageUTF16(
175 errors::kErrorInvalidId
, *it
);
176 return scoped_ptr
<ExternallyConnectableInfo
>();
181 if (!externally_connectable
->matches
&&
182 !externally_connectable
->ids
) {
183 install_warnings
->push_back(InstallWarning(
184 errors::kErrorNothingSpecified
,
185 keys::kExternallyConnectable
));
188 bool accepts_tls_channel_id
=
189 externally_connectable
->accepts_tls_channel_id
.get() &&
190 *externally_connectable
->accepts_tls_channel_id
;
191 return make_scoped_ptr(
192 new ExternallyConnectableInfo(matches
, ids
, all_ids
,
193 accepts_tls_channel_id
));
196 ExternallyConnectableInfo::~ExternallyConnectableInfo() {}
198 ExternallyConnectableInfo::ExternallyConnectableInfo(
199 const URLPatternSet
& matches
,
200 const std::vector
<std::string
>& ids
,
202 bool accepts_tls_channel_id
)
203 : matches(matches
), ids(Sorted(ids
)), all_ids(all_ids
),
204 accepts_tls_channel_id(accepts_tls_channel_id
) {}
206 bool ExternallyConnectableInfo::IdCanConnect(const std::string
& id
) {
209 DCHECK(base::STLIsSorted(ids
));
210 return std::binary_search(ids
.begin(), ids
.end(), id
);
213 } // namespace extensions