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 "extensions/common/manifest_handlers/externally_connectable.h" 6 7 #include <algorithm> 8 9 #include "base/stl_util.h" 10 #include "base/strings/utf_string_conversions.h" 11 #include "extensions/common/api/extensions_manifest_types.h" 12 #include "extensions/common/error_utils.h" 13 #include "extensions/common/manifest_constants.h" 14 #include "extensions/common/manifest_handlers/permissions_parser.h" 15 #include "extensions/common/permissions/api_permission_set.h" 16 #include "extensions/common/url_pattern.h" 17 #include "net/base/registry_controlled_domains/registry_controlled_domain.h" 18 #include "url/gurl.h" 19 20 namespace rcd = net::registry_controlled_domains; 21 22 namespace extensions { 23 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 36 37 namespace keys = extensions::manifest_keys; 38 namespace errors = externally_connectable_errors; 39 using core_api::extensions_manifest_types::ExternallyConnectable; 40 41 namespace { 42 43 const char kAllIds[] = "*"; 44 45 template <typename T> 46 std::vector<T> Sorted(const std::vector<T>& in) { 47 std::vector<T> out = in; 48 std::sort(out.begin(), out.end()); 49 return out; 50 } 51 52 } // namespace 53 54 ExternallyConnectableHandler::ExternallyConnectableHandler() { 55 } 56 57 ExternallyConnectableHandler::~ExternallyConnectableHandler() { 58 } 59 60 bool ExternallyConnectableHandler::Parse(Extension* extension, 61 base::string16* error) { 62 const base::Value* externally_connectable = NULL; 63 CHECK(extension->manifest()->Get(keys::kExternallyConnectable, 64 &externally_connectable)); 65 std::vector<InstallWarning> install_warnings; 66 scoped_ptr<ExternallyConnectableInfo> info = 67 ExternallyConnectableInfo::FromValue(*externally_connectable, 68 &install_warnings, 69 error); 70 if (!info) 71 return false; 72 if (!info->matches.is_empty()) { 73 PermissionsParser::AddAPIPermission(extension, 74 APIPermission::kWebConnectable); 75 } 76 extension->AddInstallWarnings(install_warnings); 77 extension->SetManifestData(keys::kExternallyConnectable, info.release()); 78 return true; 79 } 80 81 const std::vector<std::string> ExternallyConnectableHandler::Keys() const { 82 return SingleKey(keys::kExternallyConnectable); 83 } 84 85 // static 86 ExternallyConnectableInfo* ExternallyConnectableInfo::Get( 87 const Extension* extension) { 88 return static_cast<ExternallyConnectableInfo*>( 89 extension->GetManifestData(keys::kExternallyConnectable)); 90 } 91 92 // static 93 scoped_ptr<ExternallyConnectableInfo> ExternallyConnectableInfo::FromValue( 94 const base::Value& value, 95 std::vector<InstallWarning>* install_warnings, 96 base::string16* error) { 97 scoped_ptr<ExternallyConnectable> externally_connectable = 98 ExternallyConnectable::FromValue(value, error); 99 if (!externally_connectable) 100 return scoped_ptr<ExternallyConnectableInfo>(); 101 102 URLPatternSet matches; 103 104 if (externally_connectable->matches) { 105 for (std::vector<std::string>::iterator it = 106 externally_connectable->matches->begin(); 107 it != externally_connectable->matches->end(); 108 ++it) { 109 // Safe to use SCHEME_ALL here; externally_connectable gives a page -> 110 // extension communication path, not the other way. 111 URLPattern pattern(URLPattern::SCHEME_ALL); 112 if (pattern.Parse(*it) != URLPattern::PARSE_SUCCESS) { 113 *error = ErrorUtils::FormatErrorMessageUTF16( 114 errors::kErrorInvalidMatchPattern, *it); 115 return scoped_ptr<ExternallyConnectableInfo>(); 116 } 117 118 // Wildcard hosts are not allowed. 119 if (pattern.host().empty()) { 120 // Warning not error for forwards compatibility. 121 install_warnings->push_back( 122 InstallWarning(ErrorUtils::FormatErrorMessage( 123 errors::kErrorWildcardHostsNotAllowed, *it), 124 keys::kExternallyConnectable, 125 *it)); 126 continue; 127 } 128 129 // Wildcards on subdomains of a TLD are not allowed. 130 size_t registry_length = rcd::GetRegistryLength( 131 pattern.host(), 132 // This means that things that look like TLDs - the foobar in 133 // http://google.foobar - count as TLDs. 134 rcd::INCLUDE_UNKNOWN_REGISTRIES, 135 // This means that effective TLDs like appspot.com count as TLDs; 136 // codereview.appspot.com and evil.appspot.com are different. 137 rcd::INCLUDE_PRIVATE_REGISTRIES); 138 139 if (registry_length == std::string::npos) { 140 // The URL parsing combined with host().empty() should have caught this. 141 NOTREACHED() << *it; 142 *error = ErrorUtils::FormatErrorMessageUTF16( 143 errors::kErrorInvalidMatchPattern, *it); 144 return scoped_ptr<ExternallyConnectableInfo>(); 145 } 146 147 // Broad match patterns like "*.com", "*.co.uk", and even "*.appspot.com" 148 // are not allowed. However just "appspot.com" is ok. 149 if (registry_length == 0 && pattern.match_subdomains()) { 150 // Warning not error for forwards compatibility. 151 install_warnings->push_back( 152 InstallWarning(ErrorUtils::FormatErrorMessage( 153 errors::kErrorTopLevelDomainsNotAllowed, 154 pattern.host().c_str(), 155 *it), 156 keys::kExternallyConnectable, 157 *it)); 158 continue; 159 } 160 161 matches.AddPattern(pattern); 162 } 163 } 164 165 std::vector<std::string> ids; 166 bool all_ids = false; 167 168 if (externally_connectable->ids) { 169 for (std::vector<std::string>::iterator it = 170 externally_connectable->ids->begin(); 171 it != externally_connectable->ids->end(); 172 ++it) { 173 if (*it == kAllIds) { 174 all_ids = true; 175 } else if (Extension::IdIsValid(*it)) { 176 ids.push_back(*it); 177 } else { 178 *error = 179 ErrorUtils::FormatErrorMessageUTF16(errors::kErrorInvalidId, *it); 180 return scoped_ptr<ExternallyConnectableInfo>(); 181 } 182 } 183 } 184 185 if (!externally_connectable->matches && !externally_connectable->ids) { 186 install_warnings->push_back(InstallWarning(errors::kErrorNothingSpecified, 187 keys::kExternallyConnectable)); 188 } 189 190 bool accepts_tls_channel_id = 191 externally_connectable->accepts_tls_channel_id.get() && 192 *externally_connectable->accepts_tls_channel_id; 193 return make_scoped_ptr(new ExternallyConnectableInfo( 194 matches, ids, all_ids, accepts_tls_channel_id)); 195 } 196 197 ExternallyConnectableInfo::~ExternallyConnectableInfo() { 198 } 199 200 ExternallyConnectableInfo::ExternallyConnectableInfo( 201 const URLPatternSet& matches, 202 const std::vector<std::string>& ids, 203 bool all_ids, 204 bool accepts_tls_channel_id) 205 : matches(matches), 206 ids(Sorted(ids)), 207 all_ids(all_ids), 208 accepts_tls_channel_id(accepts_tls_channel_id) { 209 } 210 211 bool ExternallyConnectableInfo::IdCanConnect(const std::string& id) { 212 if (all_ids) 213 return true; 214 DCHECK(base::STLIsSorted(ids)); 215 return std::binary_search(ids.begin(), ids.end(), id); 216 } 217 218 } // namespace extensions 219