普通文本  |  219行  |  7.72 KB

// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "extensions/common/manifest_handlers/externally_connectable.h"

#include <algorithm>

#include "base/stl_util.h"
#include "base/strings/utf_string_conversions.h"
#include "extensions/common/api/extensions_manifest_types.h"
#include "extensions/common/error_utils.h"
#include "extensions/common/manifest_constants.h"
#include "extensions/common/manifest_handlers/permissions_parser.h"
#include "extensions/common/permissions/api_permission_set.h"
#include "extensions/common/url_pattern.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "url/gurl.h"

namespace rcd = net::registry_controlled_domains;

namespace extensions {

namespace externally_connectable_errors {
const char kErrorInvalidMatchPattern[] = "Invalid match pattern '*'";
const char kErrorInvalidId[] = "Invalid ID '*'";
const char kErrorNothingSpecified[] =
    "'externally_connectable' specifies neither 'matches' nor 'ids'; "
    "nothing will be able to connect";
const char kErrorTopLevelDomainsNotAllowed[] =
    "\"*\" is an effective top level domain for which wildcard subdomains such "
    "as \"*\" are not allowed";
const char kErrorWildcardHostsNotAllowed[] =
    "Wildcard domain patterns such as \"*\" are not allowed";
}  // namespace externally_connectable_errors

namespace keys = extensions::manifest_keys;
namespace errors = externally_connectable_errors;
using core_api::extensions_manifest_types::ExternallyConnectable;

namespace {

const char kAllIds[] = "*";

template <typename T>
std::vector<T> Sorted(const std::vector<T>& in) {
  std::vector<T> out = in;
  std::sort(out.begin(), out.end());
  return out;
}

}  // namespace

ExternallyConnectableHandler::ExternallyConnectableHandler() {
}

ExternallyConnectableHandler::~ExternallyConnectableHandler() {
}

bool ExternallyConnectableHandler::Parse(Extension* extension,
                                         base::string16* error) {
  const base::Value* externally_connectable = NULL;
  CHECK(extension->manifest()->Get(keys::kExternallyConnectable,
                                   &externally_connectable));
  std::vector<InstallWarning> install_warnings;
  scoped_ptr<ExternallyConnectableInfo> info =
      ExternallyConnectableInfo::FromValue(*externally_connectable,
                                           &install_warnings,
                                           error);
  if (!info)
    return false;
  if (!info->matches.is_empty()) {
    PermissionsParser::AddAPIPermission(extension,
                                        APIPermission::kWebConnectable);
  }
  extension->AddInstallWarnings(install_warnings);
  extension->SetManifestData(keys::kExternallyConnectable, info.release());
  return true;
}

const std::vector<std::string> ExternallyConnectableHandler::Keys() const {
  return SingleKey(keys::kExternallyConnectable);
}

// static
ExternallyConnectableInfo* ExternallyConnectableInfo::Get(
    const Extension* extension) {
  return static_cast<ExternallyConnectableInfo*>(
      extension->GetManifestData(keys::kExternallyConnectable));
}

// static
scoped_ptr<ExternallyConnectableInfo> ExternallyConnectableInfo::FromValue(
    const base::Value& value,
    std::vector<InstallWarning>* install_warnings,
    base::string16* error) {
  scoped_ptr<ExternallyConnectable> externally_connectable =
      ExternallyConnectable::FromValue(value, error);
  if (!externally_connectable)
    return scoped_ptr<ExternallyConnectableInfo>();

  URLPatternSet matches;

  if (externally_connectable->matches) {
    for (std::vector<std::string>::iterator it =
             externally_connectable->matches->begin();
         it != externally_connectable->matches->end();
         ++it) {
      // Safe to use SCHEME_ALL here; externally_connectable gives a page ->
      // extension communication path, not the other way.
      URLPattern pattern(URLPattern::SCHEME_ALL);
      if (pattern.Parse(*it) != URLPattern::PARSE_SUCCESS) {
        *error = ErrorUtils::FormatErrorMessageUTF16(
            errors::kErrorInvalidMatchPattern, *it);
        return scoped_ptr<ExternallyConnectableInfo>();
      }

      // Wildcard hosts are not allowed.
      if (pattern.host().empty()) {
        // Warning not error for forwards compatibility.
        install_warnings->push_back(
            InstallWarning(ErrorUtils::FormatErrorMessage(
                               errors::kErrorWildcardHostsNotAllowed, *it),
                           keys::kExternallyConnectable,
                           *it));
        continue;
      }

      // Wildcards on subdomains of a TLD are not allowed.
      size_t registry_length = rcd::GetRegistryLength(
          pattern.host(),
          // This means that things that look like TLDs - the foobar in
          // http://google.foobar - count as TLDs.
          rcd::INCLUDE_UNKNOWN_REGISTRIES,
          // This means that effective TLDs like appspot.com count as TLDs;
          // codereview.appspot.com and evil.appspot.com are different.
          rcd::INCLUDE_PRIVATE_REGISTRIES);

      if (registry_length == std::string::npos) {
        // The URL parsing combined with host().empty() should have caught this.
        NOTREACHED() << *it;
        *error = ErrorUtils::FormatErrorMessageUTF16(
            errors::kErrorInvalidMatchPattern, *it);
        return scoped_ptr<ExternallyConnectableInfo>();
      }

      // Broad match patterns like "*.com", "*.co.uk", and even "*.appspot.com"
      // are not allowed. However just "appspot.com" is ok.
      if (registry_length == 0 && pattern.match_subdomains()) {
        // Warning not error for forwards compatibility.
        install_warnings->push_back(
            InstallWarning(ErrorUtils::FormatErrorMessage(
                               errors::kErrorTopLevelDomainsNotAllowed,
                               pattern.host().c_str(),
                               *it),
                           keys::kExternallyConnectable,
                           *it));
        continue;
      }

      matches.AddPattern(pattern);
    }
  }

  std::vector<std::string> ids;
  bool all_ids = false;

  if (externally_connectable->ids) {
    for (std::vector<std::string>::iterator it =
             externally_connectable->ids->begin();
         it != externally_connectable->ids->end();
         ++it) {
      if (*it == kAllIds) {
        all_ids = true;
      } else if (Extension::IdIsValid(*it)) {
        ids.push_back(*it);
      } else {
        *error =
            ErrorUtils::FormatErrorMessageUTF16(errors::kErrorInvalidId, *it);
        return scoped_ptr<ExternallyConnectableInfo>();
      }
    }
  }

  if (!externally_connectable->matches && !externally_connectable->ids) {
    install_warnings->push_back(InstallWarning(errors::kErrorNothingSpecified,
                                               keys::kExternallyConnectable));
  }

  bool accepts_tls_channel_id =
      externally_connectable->accepts_tls_channel_id.get() &&
      *externally_connectable->accepts_tls_channel_id;
  return make_scoped_ptr(new ExternallyConnectableInfo(
      matches, ids, all_ids, accepts_tls_channel_id));
}

ExternallyConnectableInfo::~ExternallyConnectableInfo() {
}

ExternallyConnectableInfo::ExternallyConnectableInfo(
    const URLPatternSet& matches,
    const std::vector<std::string>& ids,
    bool all_ids,
    bool accepts_tls_channel_id)
    : matches(matches),
      ids(Sorted(ids)),
      all_ids(all_ids),
      accepts_tls_channel_id(accepts_tls_channel_id) {
}

bool ExternallyConnectableInfo::IdCanConnect(const std::string& id) {
  if (all_ids)
    return true;
  DCHECK(base::STLIsSorted(ids));
  return std::binary_search(ids.begin(), ids.end(), id);
}

}  // namespace extensions