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.
5 #include "extensions/common/manifest_handlers/externally_connectable.h"
9 #include "base/stl_util.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "components/crx_file/id_util.h"
12 #include "extensions/common/api/extensions_manifest_types.h"
13 #include "extensions/common/error_utils.h"
14 #include "extensions/common/manifest_constants.h"
15 #include "extensions/common/manifest_handlers/permissions_parser.h"
16 #include "extensions/common/permissions/api_permission_set.h"
17 #include "extensions/common/url_pattern.h"
18 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
21 namespace rcd
= net::registry_controlled_domains
;
23 namespace extensions
{
25 namespace externally_connectable_errors
{
26 const char kErrorInvalidMatchPattern
[] = "Invalid match pattern '*'";
27 const char kErrorInvalidId
[] = "Invalid ID '*'";
28 const char kErrorNothingSpecified
[] =
29 "'externally_connectable' specifies neither 'matches' nor 'ids'; "
30 "nothing will be able to connect";
31 const char kErrorTopLevelDomainsNotAllowed
[] =
32 "\"*\" is an effective top level domain for which wildcard subdomains such "
33 "as \"*\" are not allowed";
34 const char kErrorWildcardHostsNotAllowed
[] =
35 "Wildcard domain patterns such as \"*\" are not allowed";
36 } // namespace externally_connectable_errors
38 namespace keys
= extensions::manifest_keys
;
39 namespace errors
= externally_connectable_errors
;
40 using core_api::extensions_manifest_types::ExternallyConnectable
;
44 const char kAllIds
[] = "*";
47 std::vector
<T
> Sorted(const std::vector
<T
>& in
) {
48 std::vector
<T
> out
= in
;
49 std::sort(out
.begin(), out
.end());
55 ExternallyConnectableHandler::ExternallyConnectableHandler() {
58 ExternallyConnectableHandler::~ExternallyConnectableHandler() {
61 bool ExternallyConnectableHandler::Parse(Extension
* extension
,
62 base::string16
* error
) {
63 const base::Value
* externally_connectable
= NULL
;
64 CHECK(extension
->manifest()->Get(keys::kExternallyConnectable
,
65 &externally_connectable
));
66 bool allow_all_urls
= PermissionsParser::HasAPIPermission(
67 extension
, APIPermission::kExternallyConnectableAllUrls
);
69 std::vector
<InstallWarning
> install_warnings
;
70 scoped_ptr
<ExternallyConnectableInfo
> info
=
71 ExternallyConnectableInfo::FromValue(
72 *externally_connectable
, allow_all_urls
, &install_warnings
, error
);
75 if (!info
->matches
.is_empty()) {
76 PermissionsParser::AddAPIPermission(extension
,
77 APIPermission::kWebConnectable
);
79 extension
->AddInstallWarnings(install_warnings
);
80 extension
->SetManifestData(keys::kExternallyConnectable
, info
.release());
84 const std::vector
<std::string
> ExternallyConnectableHandler::Keys() const {
85 return SingleKey(keys::kExternallyConnectable
);
89 ExternallyConnectableInfo
* ExternallyConnectableInfo::Get(
90 const Extension
* extension
) {
91 return static_cast<ExternallyConnectableInfo
*>(
92 extension
->GetManifestData(keys::kExternallyConnectable
));
96 scoped_ptr
<ExternallyConnectableInfo
> ExternallyConnectableInfo::FromValue(
97 const base::Value
& value
,
99 std::vector
<InstallWarning
>* install_warnings
,
100 base::string16
* error
) {
101 scoped_ptr
<ExternallyConnectable
> externally_connectable
=
102 ExternallyConnectable::FromValue(value
, error
);
103 if (!externally_connectable
)
104 return scoped_ptr
<ExternallyConnectableInfo
>();
106 URLPatternSet matches
;
108 if (externally_connectable
->matches
) {
109 for (std::vector
<std::string
>::iterator it
=
110 externally_connectable
->matches
->begin();
111 it
!= externally_connectable
->matches
->end();
113 // Safe to use SCHEME_ALL here; externally_connectable gives a page ->
114 // extension communication path, not the other way.
115 URLPattern
pattern(URLPattern::SCHEME_ALL
);
116 if (pattern
.Parse(*it
) != URLPattern::PARSE_SUCCESS
) {
117 *error
= ErrorUtils::FormatErrorMessageUTF16(
118 errors::kErrorInvalidMatchPattern
, *it
);
119 return scoped_ptr
<ExternallyConnectableInfo
>();
122 if (allow_all_urls
&& pattern
.match_all_urls()) {
123 matches
.AddPattern(pattern
);
127 // Wildcard hosts are not allowed.
128 if (pattern
.host().empty()) {
129 // Warning not error for forwards compatibility.
130 install_warnings
->push_back(
131 InstallWarning(ErrorUtils::FormatErrorMessage(
132 errors::kErrorWildcardHostsNotAllowed
, *it
),
133 keys::kExternallyConnectable
,
138 // Wildcards on subdomains of a TLD are not allowed.
139 size_t registry_length
= rcd::GetRegistryLength(
141 // This means that things that look like TLDs - the foobar in
142 // http://google.foobar - count as TLDs.
143 rcd::INCLUDE_UNKNOWN_REGISTRIES
,
144 // This means that effective TLDs like appspot.com count as TLDs;
145 // codereview.appspot.com and evil.appspot.com are different.
146 rcd::INCLUDE_PRIVATE_REGISTRIES
);
148 if (registry_length
== std::string::npos
) {
149 // The URL parsing combined with host().empty() should have caught this.
151 *error
= ErrorUtils::FormatErrorMessageUTF16(
152 errors::kErrorInvalidMatchPattern
, *it
);
153 return scoped_ptr
<ExternallyConnectableInfo
>();
156 // Broad match patterns like "*.com", "*.co.uk", and even "*.appspot.com"
157 // are not allowed. However just "appspot.com" is ok.
158 if (registry_length
== 0 && pattern
.match_subdomains()) {
159 // Warning not error for forwards compatibility.
160 install_warnings
->push_back(
161 InstallWarning(ErrorUtils::FormatErrorMessage(
162 errors::kErrorTopLevelDomainsNotAllowed
,
163 pattern
.host().c_str(),
165 keys::kExternallyConnectable
,
170 matches
.AddPattern(pattern
);
174 std::vector
<std::string
> ids
;
175 bool all_ids
= false;
177 if (externally_connectable
->ids
) {
178 for (std::vector
<std::string
>::iterator it
=
179 externally_connectable
->ids
->begin();
180 it
!= externally_connectable
->ids
->end();
182 if (*it
== kAllIds
) {
184 } else if (crx_file::id_util::IdIsValid(*it
)) {
188 ErrorUtils::FormatErrorMessageUTF16(errors::kErrorInvalidId
, *it
);
189 return scoped_ptr
<ExternallyConnectableInfo
>();
194 if (!externally_connectable
->matches
&& !externally_connectable
->ids
) {
195 install_warnings
->push_back(InstallWarning(errors::kErrorNothingSpecified
,
196 keys::kExternallyConnectable
));
199 bool accepts_tls_channel_id
=
200 externally_connectable
->accepts_tls_channel_id
.get() &&
201 *externally_connectable
->accepts_tls_channel_id
;
202 return make_scoped_ptr(new ExternallyConnectableInfo(
203 matches
, ids
, all_ids
, accepts_tls_channel_id
));
206 ExternallyConnectableInfo::~ExternallyConnectableInfo() {
209 ExternallyConnectableInfo::ExternallyConnectableInfo(
210 const URLPatternSet
& matches
,
211 const std::vector
<std::string
>& ids
,
213 bool accepts_tls_channel_id
)
217 accepts_tls_channel_id(accepts_tls_channel_id
) {
220 bool ExternallyConnectableInfo::IdCanConnect(const std::string
& id
) {
223 DCHECK(base::STLIsSorted(ids
));
224 return std::binary_search(ids
.begin(), ids
.end(), id
);
227 } // namespace extensions