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 "chrome/common/extensions/extension_manifest_constants.h"
     13 #include "chrome/common/extensions/permissions/api_permission_set.h"
     14 #include "chrome/common/extensions/permissions/permissions_data.h"
     15 #include "extensions/common/error_utils.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 kErrorInvalid[] = "Invalid value for 'externally_connectable'";
     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 = extension_manifest_keys;
     39 namespace errors = externally_connectable_errors;
     40 using api::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 ExternallyConnectableHandler::~ExternallyConnectableHandler() {}
     58 
     59 bool ExternallyConnectableHandler::Parse(Extension* extension,
     60                                          string16* error) {
     61   const base::Value* externally_connectable = NULL;
     62   CHECK(extension->manifest()->Get(keys::kExternallyConnectable,
     63                                    &externally_connectable));
     64   std::vector<InstallWarning> install_warnings;
     65   scoped_ptr<ExternallyConnectableInfo> info =
     66       ExternallyConnectableInfo::FromValue(*externally_connectable,
     67                                            &install_warnings,
     68                                            error);
     69   if (!info)
     70     return false;
     71   if (!info->matches.is_empty()) {
     72     PermissionsData::GetInitialAPIPermissions(extension)->insert(
     73         APIPermission::kWebConnectable);
     74   }
     75   extension->AddInstallWarnings(install_warnings);
     76   extension->SetManifestData(keys::kExternallyConnectable, info.release());
     77   return true;
     78 }
     79 
     80 const std::vector<std::string> ExternallyConnectableHandler::Keys() const {
     81   return SingleKey(keys::kExternallyConnectable);
     82 }
     83 
     84 // static
     85 ExternallyConnectableInfo* ExternallyConnectableInfo::Get(
     86     const Extension* extension) {
     87   return static_cast<ExternallyConnectableInfo*>(
     88       extension->GetManifestData(keys::kExternallyConnectable));
     89 }
     90 
     91 // static
     92 scoped_ptr<ExternallyConnectableInfo> ExternallyConnectableInfo::FromValue(
     93     const base::Value& value,
     94     std::vector<InstallWarning>* install_warnings,
     95     string16* error) {
     96   scoped_ptr<ExternallyConnectable> externally_connectable =
     97       ExternallyConnectable::FromValue(value);
     98   if (!externally_connectable) {
     99     *error = UTF8ToUTF16(errors::kErrorInvalid);
    100     return scoped_ptr<ExternallyConnectableInfo>();
    101   }
    102 
    103   URLPatternSet matches;
    104 
    105   if (externally_connectable->matches) {
    106     for (std::vector<std::string>::iterator it =
    107              externally_connectable->matches->begin();
    108          it != externally_connectable->matches->end(); ++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::Text(ErrorUtils::FormatErrorMessage(
    123                 errors::kErrorWildcardHostsNotAllowed, *it)));
    124         continue;
    125       }
    126 
    127       // Wildcards on subdomains of a TLD are not allowed.
    128       size_t registry_length = rcd::GetRegistryLength(
    129           pattern.host(),
    130           // This means that things that look like TLDs - the foobar in
    131           // http://google.foobar - count as TLDs.
    132           rcd::INCLUDE_UNKNOWN_REGISTRIES,
    133           // This means that effective TLDs like appspot.com count as TLDs;
    134           // codereview.appspot.com and evil.appspot.com are different.
    135           rcd::INCLUDE_PRIVATE_REGISTRIES);
    136 
    137       if (registry_length == std::string::npos) {
    138         // The URL parsing combined with host().empty() should have caught this.
    139         NOTREACHED() << *it;
    140         *error = ErrorUtils::FormatErrorMessageUTF16(
    141             errors::kErrorInvalidMatchPattern, *it);
    142         return scoped_ptr<ExternallyConnectableInfo>();
    143       }
    144 
    145       // Broad match patterns like "*.com", "*.co.uk", and even "*.appspot.com"
    146       // are not allowed. However just "appspot.com" is ok.
    147       if (registry_length == 0 && pattern.match_subdomains()) {
    148         // Warning not error for forwards compatibility.
    149         install_warnings->push_back(InstallWarning::Text(
    150             ErrorUtils::FormatErrorMessage(
    151                 errors::kErrorTopLevelDomainsNotAllowed,
    152                 pattern.host().c_str(),
    153                 *it)));
    154         continue;
    155       }
    156 
    157       matches.AddPattern(pattern);
    158     }
    159   }
    160 
    161   std::vector<std::string> ids;
    162   bool all_ids = false;
    163 
    164   if (externally_connectable->ids) {
    165     for (std::vector<std::string>::iterator it =
    166              externally_connectable->ids->begin();
    167          it != externally_connectable->ids->end(); ++it) {
    168       if (*it == kAllIds) {
    169         all_ids = true;
    170       } else if (Extension::IdIsValid(*it)) {
    171         ids.push_back(*it);
    172       } else {
    173         *error = ErrorUtils::FormatErrorMessageUTF16(
    174             errors::kErrorInvalidId, *it);
    175         return scoped_ptr<ExternallyConnectableInfo>();
    176       }
    177     }
    178   }
    179 
    180   if (!externally_connectable->matches &&
    181       !externally_connectable->ids) {
    182     install_warnings->push_back(InstallWarning::Text(
    183         errors::kErrorNothingSpecified));
    184   }
    185 
    186   return make_scoped_ptr(
    187       new ExternallyConnectableInfo(matches, ids, all_ids));
    188 }
    189 
    190 ExternallyConnectableInfo::~ExternallyConnectableInfo() {}
    191 
    192 ExternallyConnectableInfo::ExternallyConnectableInfo(
    193     const URLPatternSet& matches,
    194     const std::vector<std::string>& ids,
    195     bool all_ids)
    196     : matches(matches), ids(Sorted(ids)), all_ids(all_ids) {}
    197 
    198 bool ExternallyConnectableInfo::IdCanConnect(const std::string& id) {
    199   if (all_ids)
    200     return true;
    201   DCHECK(base::STLIsSorted(ids));
    202   return std::binary_search(ids.begin(), ids.end(), id);
    203 }
    204 
    205 }   // namespace extensions
    206