Home | History | Annotate | Download | only in manifest_handlers
      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