Home | History | Annotate | Download | only in manifest_handlers
      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 "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"
     19 #include "url/gurl.h"
     20 
     21 namespace rcd = net::registry_controlled_domains;
     22 
     23 namespace extensions {
     24 
     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
     37 
     38 namespace keys = extensions::manifest_keys;
     39 namespace errors = externally_connectable_errors;
     40 using core_api::extensions_manifest_types::ExternallyConnectable;
     41 
     42 namespace {
     43 
     44 const char kAllIds[] = "*";
     45 
     46 template <typename T>
     47 std::vector<T> Sorted(const std::vector<T>& in) {
     48   std::vector<T> out = in;
     49   std::sort(out.begin(), out.end());
     50   return out;
     51 }
     52 
     53 }  // namespace
     54 
     55 ExternallyConnectableHandler::ExternallyConnectableHandler() {
     56 }
     57 
     58 ExternallyConnectableHandler::~ExternallyConnectableHandler() {
     59 }
     60 
     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);
     68 
     69   std::vector<InstallWarning> install_warnings;
     70   scoped_ptr<ExternallyConnectableInfo> info =
     71       ExternallyConnectableInfo::FromValue(
     72           *externally_connectable, allow_all_urls, &install_warnings, error);
     73   if (!info)
     74     return false;
     75   if (!info->matches.is_empty()) {
     76     PermissionsParser::AddAPIPermission(extension,
     77                                         APIPermission::kWebConnectable);
     78   }
     79   extension->AddInstallWarnings(install_warnings);
     80   extension->SetManifestData(keys::kExternallyConnectable, info.release());
     81   return true;
     82 }
     83 
     84 const std::vector<std::string> ExternallyConnectableHandler::Keys() const {
     85   return SingleKey(keys::kExternallyConnectable);
     86 }
     87 
     88 // static
     89 ExternallyConnectableInfo* ExternallyConnectableInfo::Get(
     90     const Extension* extension) {
     91   return static_cast<ExternallyConnectableInfo*>(
     92       extension->GetManifestData(keys::kExternallyConnectable));
     93 }
     94 
     95 // static
     96 scoped_ptr<ExternallyConnectableInfo> ExternallyConnectableInfo::FromValue(
     97     const base::Value& value,
     98     bool allow_all_urls,
     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>();
    105 
    106   URLPatternSet matches;
    107 
    108   if (externally_connectable->matches) {
    109     for (std::vector<std::string>::iterator it =
    110              externally_connectable->matches->begin();
    111          it != externally_connectable->matches->end();
    112          ++it) {
    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>();
    120       }
    121 
    122       if (allow_all_urls && pattern.match_all_urls()) {
    123         matches.AddPattern(pattern);
    124         continue;
    125       }
    126 
    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,
    134                            *it));
    135         continue;
    136       }
    137 
    138       // Wildcards on subdomains of a TLD are not allowed.
    139       size_t registry_length = rcd::GetRegistryLength(
    140           pattern.host(),
    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);
    147 
    148       if (registry_length == std::string::npos) {
    149         // The URL parsing combined with host().empty() should have caught this.
    150         NOTREACHED() << *it;
    151         *error = ErrorUtils::FormatErrorMessageUTF16(
    152             errors::kErrorInvalidMatchPattern, *it);
    153         return scoped_ptr<ExternallyConnectableInfo>();
    154       }
    155 
    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(),
    164                                *it),
    165                            keys::kExternallyConnectable,
    166                            *it));
    167         continue;
    168       }
    169 
    170       matches.AddPattern(pattern);
    171     }
    172   }
    173 
    174   std::vector<std::string> ids;
    175   bool all_ids = false;
    176 
    177   if (externally_connectable->ids) {
    178     for (std::vector<std::string>::iterator it =
    179              externally_connectable->ids->begin();
    180          it != externally_connectable->ids->end();
    181          ++it) {
    182       if (*it == kAllIds) {
    183         all_ids = true;
    184       } else if (crx_file::id_util::IdIsValid(*it)) {
    185         ids.push_back(*it);
    186       } else {
    187         *error =
    188             ErrorUtils::FormatErrorMessageUTF16(errors::kErrorInvalidId, *it);
    189         return scoped_ptr<ExternallyConnectableInfo>();
    190       }
    191     }
    192   }
    193 
    194   if (!externally_connectable->matches && !externally_connectable->ids) {
    195     install_warnings->push_back(InstallWarning(errors::kErrorNothingSpecified,
    196                                                keys::kExternallyConnectable));
    197   }
    198 
    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));
    204 }
    205 
    206 ExternallyConnectableInfo::~ExternallyConnectableInfo() {
    207 }
    208 
    209 ExternallyConnectableInfo::ExternallyConnectableInfo(
    210     const URLPatternSet& matches,
    211     const std::vector<std::string>& ids,
    212     bool all_ids,
    213     bool accepts_tls_channel_id)
    214     : matches(matches),
    215       ids(Sorted(ids)),
    216       all_ids(all_ids),
    217       accepts_tls_channel_id(accepts_tls_channel_id) {
    218 }
    219 
    220 bool ExternallyConnectableInfo::IdCanConnect(const std::string& id) {
    221   if (all_ids)
    222     return true;
    223   DCHECK(base::STLIsSorted(ids));
    224   return std::binary_search(ids.begin(), ids.end(), id);
    225 }
    226 
    227 }  // namespace extensions
    228