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. 4 5 #include "chrome/common/extensions/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 "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" 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 api::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 ExternallyConnectableHandler::~ExternallyConnectableHandler() {} 57 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, 66 &install_warnings, 67 error); 68 if (!info) 69 return false; 70 if (!info->matches.is_empty()) { 71 PermissionsData::GetInitialAPIPermissions(extension)->insert( 72 APIPermission::kWebConnectable); 73 } 74 extension->AddInstallWarnings(install_warnings); 75 extension->SetManifestData(keys::kExternallyConnectable, info.release()); 76 return true; 77 } 78 79 const std::vector<std::string> ExternallyConnectableHandler::Keys() const { 80 return SingleKey(keys::kExternallyConnectable); 81 } 82 83 // static 84 ExternallyConnectableInfo* ExternallyConnectableInfo::Get( 85 const Extension* extension) { 86 return static_cast<ExternallyConnectableInfo*>( 87 extension->GetManifestData(keys::kExternallyConnectable)); 88 } 89 90 // static 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>(); 99 100 URLPatternSet matches; 101 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>(); 113 } 114 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, 122 *it)); 123 continue; 124 } 125 126 // Wildcards on subdomains of a TLD are not allowed. 127 size_t registry_length = rcd::GetRegistryLength( 128 pattern.host(), 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); 135 136 if (registry_length == std::string::npos) { 137 // The URL parsing combined with host().empty() should have caught this. 138 NOTREACHED() << *it; 139 *error = ErrorUtils::FormatErrorMessageUTF16( 140 errors::kErrorInvalidMatchPattern, *it); 141 return scoped_ptr<ExternallyConnectableInfo>(); 142 } 143 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(), 152 *it), 153 keys::kExternallyConnectable, 154 *it)); 155 continue; 156 } 157 158 matches.AddPattern(pattern); 159 } 160 } 161 162 std::vector<std::string> ids; 163 bool all_ids = false; 164 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) { 170 all_ids = true; 171 } else if (Extension::IdIsValid(*it)) { 172 ids.push_back(*it); 173 } else { 174 *error = ErrorUtils::FormatErrorMessageUTF16( 175 errors::kErrorInvalidId, *it); 176 return scoped_ptr<ExternallyConnectableInfo>(); 177 } 178 } 179 } 180 181 if (!externally_connectable->matches && 182 !externally_connectable->ids) { 183 install_warnings->push_back(InstallWarning( 184 errors::kErrorNothingSpecified, 185 keys::kExternallyConnectable)); 186 } 187 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)); 194 } 195 196 ExternallyConnectableInfo::~ExternallyConnectableInfo() {} 197 198 ExternallyConnectableInfo::ExternallyConnectableInfo( 199 const URLPatternSet& matches, 200 const std::vector<std::string>& ids, 201 bool all_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) {} 205 206 bool ExternallyConnectableInfo::IdCanConnect(const std::string& id) { 207 if (all_ids) 208 return true; 209 DCHECK(base::STLIsSorted(ids)); 210 return std::binary_search(ids.begin(), ids.end(), id); 211 } 212 213 } // namespace extensions 214