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 "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