root/chrome/common/extensions/manifest_handlers/externally_connectable.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. Sorted
  2. Parse
  3. Keys
  4. Get
  5. FromValue
  6. accepts_tls_channel_id
  7. IdCanConnect

// Copyright 2013 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 "chrome/common/extensions/manifest_handlers/externally_connectable.h"

#include <algorithm>

#include "base/stl_util.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/common/extensions/api/manifest_types.h"
#include "extensions/common/error_utils.h"
#include "extensions/common/manifest_constants.h"
#include "extensions/common/permissions/api_permission_set.h"
#include "extensions/common/permissions/permissions_data.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 api::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()) {
    PermissionsData::GetInitialAPIPermissions(extension)->insert(
        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

/* [<][>][^][v][top][bottom][index][help] */