Commit message: None
CVE: CVE-2011-2785
CWE: CWE-20
Vulnerability description: The extensions implementation in Google Chrome before 13.0.782.107 does not properly validate the URL for the home page, which allows remote attackers to have an unspecified impact via a crafted extension.

Project: Chrome

bool Extension::CanExecuteScriptOnPage(const GURL& page_url,
                                       const UserScript* script,
                                       std::string* error) const {
  if ((page_url.host() == GURL(Extension::ChromeStoreLaunchURL()).host()) &&
      !CanExecuteScriptEverywhere() &&
      !CommandLine::ForCurrentProcess()->HasSwitch(
          switches::kAllowScriptingGallery)) {
    if (error)
      *error = errors::kCannotScriptGallery;
    return false;
  }

  if (page_url.SchemeIs(chrome::kChromeUIScheme) &&
      !CanExecuteScriptEverywhere())
    return false;

  if (script)
    return script->MatchesURL(page_url);

  for (size_t i = 0; i < host_permissions_.size(); ++i) {
    if (host_permissions_[i].MatchesURL(page_url))
      return true;
  }

  if (error) {
    *error = ExtensionErrorUtils::FormatErrorMessage(errors::kCannotAccessPage,
                                                     page_url.spec());
  }

  return false;
}

Project: Chrome

Extension::PermissionMessage Extension::PermissionMessage::CreateFromHostList(
    const std::vector<std::string> hosts) {
  CHECK(hosts.size() > 0);

  MessageId message_id;
  string16 message;
  switch (hosts.size()) {
    case 1:
      message_id = ID_HOSTS_1;
      message = l10n_util::GetStringFUTF16(kMessageIds[message_id],
                                           UTF8ToUTF16(hosts[0]));
      break;
    case 2:
      message_id = ID_HOSTS_2;
      message = l10n_util::GetStringFUTF16(kMessageIds[message_id],
                                           UTF8ToUTF16(hosts[0]),
                                           UTF8ToUTF16(hosts[1]));
      break;
    case 3:
      message_id = ID_HOSTS_3;
      message = l10n_util::GetStringFUTF16(kMessageIds[message_id],
                                           UTF8ToUTF16(hosts[0]),
                                           UTF8ToUTF16(hosts[1]),
                                           UTF8ToUTF16(hosts[2]));
      break;
    default:
      message_id = ID_HOSTS_4_OR_MORE;
      message = l10n_util::GetStringFUTF16(
          kMessageIds[message_id],
          UTF8ToUTF16(hosts[0]),
          UTF8ToUTF16(hosts[1]),
          base::IntToString16(hosts.size() - 2));
      break;
  }

  return PermissionMessage(message_id, message);
}

Project: Chrome

Extension::PermissionMessage Extension::PermissionMessage::CreateFromMessageId(
    Extension::PermissionMessage::MessageId message_id) {
  DCHECK_GT(PermissionMessage::ID_NONE, PermissionMessage::ID_UNKNOWN);
  if (message_id <= ID_NONE)
    return PermissionMessage(message_id, string16());

  string16 message = l10n_util::GetStringUTF16(kMessageIds[message_id]);
  return PermissionMessage(message_id, message);
}

Project: Chrome

  ExtensionConfig() {
    for (size_t i = 0; i < Extension::kNumPermissions; ++i)
      permission_map_[Extension::kPermissions[i].name] = i;
  };

Project: Chrome

std::vector<std::string> Extension::GetDistinctHosts(
    const URLPatternList& host_patterns,
    bool include_rcd) {
  typedef std::vector<std::pair<std::string, std::string> > HostVector;
  HostVector hosts_best_rcd;
  for (size_t i = 0; i < host_patterns.size(); ++i) {
    std::string host = host_patterns[i].host();

    if (host_patterns[i].match_subdomains())
      host = "*." + host;

    std::string rcd;
    size_t reg_len = net::RegistryControlledDomainService::GetRegistryLength(
        host, false);
    if (reg_len && reg_len != std::string::npos) {
      if (include_rcd)  // else leave rcd empty
        rcd = host.substr(host.size() - reg_len);
      host = host.substr(0, host.size() - reg_len);
    }

    HostVector::iterator it = hosts_best_rcd.begin();
    for (; it != hosts_best_rcd.end(); ++it) {
      if (it->first == host)
        break;
    }
    if (it != hosts_best_rcd.end()) {
      if (include_rcd && RcdBetterThan(rcd, it->second))
        it->second = rcd;
    } else {  // Previously unseen host, append it.
      hosts_best_rcd.push_back(std::make_pair(host, rcd));
    }
  }

  std::vector<std::string> distinct_hosts;
  for (HostVector::iterator it = hosts_best_rcd.begin();
       it != hosts_best_rcd.end(); ++it)
    distinct_hosts.push_back(it->first + it->second);
  return distinct_hosts;
}

Project: Chrome

std::vector<std::string> Extension::GetDistinctHostsForDisplay(
    const URLPatternList& list) {
  return GetDistinctHosts(list, true);
}

Project: Chrome

  Extension::PermissionMessage::MessageId GetPermissionMessageId(
      const std::string& permission) {
    return Extension::kPermissions[permission_map_[permission]].message_id;
  }

Project: Chrome

Extension::PermissionMessage::MessageId Extension::GetPermissionMessageId(
    const std::string& permission) {
  return ExtensionConfig::GetInstance()->GetPermissionMessageId(permission);
}

Project: Chrome

std::vector<string16> Extension::GetPermissionMessageStrings() const {
  std::vector<string16> messages;
  PermissionMessages permissions = GetPermissionMessages();
  for (PermissionMessages::const_iterator i = permissions.begin();
       i != permissions.end(); ++i)
    messages.push_back(i->message());
  return messages;
}

Project: Chrome

Extension::PermissionMessages Extension::GetPermissionMessages() const {
  PermissionMessages messages;
  if (!plugins().empty()) {
    messages.push_back(PermissionMessage::CreateFromMessageId(
        PermissionMessage::ID_FULL_ACCESS));
    return messages;
  }

  if (HasEffectiveAccessToAllHosts()) {
    messages.push_back(PermissionMessage::CreateFromMessageId(
        PermissionMessage::ID_HOSTS_ALL));
  } else {
    std::vector<std::string> hosts = GetDistinctHostsForDisplay(
        GetEffectiveHostPermissions().patterns());
    if (!hosts.empty())
      messages.push_back(PermissionMessage::CreateFromHostList(hosts));
  }

  std::set<PermissionMessage> simple_msgs = GetSimplePermissionMessages();
  messages.insert(messages.end(), simple_msgs.begin(), simple_msgs.end());

  return messages;
}

Project: Chrome

    Extension::GetSimplePermissionMessages() const {
  std::set<PermissionMessage> messages;
  std::set<std::string>::const_iterator i;
  for (i = api_permissions().begin(); i != api_permissions().end(); ++i) {
    PermissionMessage::MessageId message_id = GetPermissionMessageId(*i);
    DCHECK_GT(PermissionMessage::ID_NONE, PermissionMessage::ID_UNKNOWN);
    if (message_id > PermissionMessage::ID_NONE)
      messages.insert(PermissionMessage::CreateFromMessageId(message_id));
  }
  return messages;
}

Project: Chrome

bool Extension::HasApiPermission(
    const std::set<std::string>& api_permissions,
    const std::string& function_name) {
  std::string permission_name = function_name;

  for (size_t i = 0; i < kNumNonPermissionFunctionNames; ++i) {
    if (permission_name == kNonPermissionFunctionNames[i])
      return true;
  }

  size_t separator = function_name.find_first_of("./");
  if (separator != std::string::npos)
    permission_name = function_name.substr(0, separator);

  if (permission_name == kWindowPermission)
    permission_name = Extension::kTabPermission;

  if (api_permissions.count(permission_name))
    return true;

  for (size_t i = 0; i < kNumNonPermissionModuleNames; ++i) {
    if (permission_name == kNonPermissionModuleNames[i]) {
      return true;
    }
  }

  return false;
}

Project: Chrome

bool Extension::HasEffectiveAccessToAllHosts(
    const URLPatternSet& effective_host_permissions,
    const std::set<std::string>& api_permissions) {
  const URLPatternList patterns = effective_host_permissions.patterns();
  for (URLPatternList::const_iterator host = patterns.begin();
       host != patterns.end(); ++host) {
    if (host->match_all_urls() ||
        (host->match_subdomains() && host->host().empty()))
      return true;
  }

  return false;
}

Project: Chrome

bool Extension::HasEffectiveAccessToAllHosts() const {
  return HasEffectiveAccessToAllHosts(GetEffectiveHostPermissions(),
                                      api_permissions());
}

Project: Chrome

bool Extension::HasFullPermissions() const {
  return !plugins().empty();
}

Project: Chrome

bool Extension::HasHostPermission(const GURL& url) const {
  for (URLPatternList::const_iterator host = host_permissions().begin();
       host != host_permissions().end(); ++host) {
    if (url.SchemeIs(chrome::kChromeUIScheme) &&
        url.host() != chrome::kChromeUIFaviconHost &&
        location() != Extension::COMPONENT)
      return false;

    if (host->MatchesURL(url))
      return true;
  }
  return false;
}

Project: Chrome

void Extension::InitEffectiveHostPermissions() {
  if (HasApiPermission(api_permissions_, kProxyPermission) ||
      !devtools_url_.is_empty()) {
    URLPattern all_urls(URLPattern::SCHEME_ALL);
    all_urls.set_match_all_urls(true);
    effective_host_permissions_.AddPattern(all_urls);
    return;
  }

  for (URLPatternList::const_iterator host = host_permissions().begin();
       host != host_permissions().end(); ++host)
    effective_host_permissions_.AddPattern(*host);

  for (UserScriptList::const_iterator content_script =
           content_scripts().begin();
       content_script != content_scripts().end(); ++content_script) {
    URLPatternList::const_iterator pattern =
        content_script->url_patterns().begin();
    for (; pattern != content_script->url_patterns().end(); ++pattern)
      effective_host_permissions_.AddPattern(*pattern);
  }
}

Project: Chrome

bool Extension::IsAPIPermission(const std::string& str) const {
  for (size_t i = 0; i < Extension::kNumPermissions; ++i) {
    if (str == Extension::kPermissions[i].name) {
      return true;
    }
  }
  return false;
}

Project: Chrome

bool Extension::IsComponentOnlyPermission(const std::string& permission) const {
  if (location() == Extension::COMPONENT)
    return true;

  for (size_t i = 0; i < Extension::kNumComponentPrivatePermissions; ++i) {
    if (permission == Extension::kComponentPrivatePermissionNames[i])
      return false;
  }
  return true;
}

Project: Chrome

bool Extension::IsElevatedHostList(
    const URLPatternList& old_list, const URLPatternList& new_list) {

  std::vector<std::string> new_hosts = GetDistinctHosts(new_list, false);
  std::vector<std::string> old_hosts = GetDistinctHosts(old_list, false);

  std::set<std::string> old_hosts_set(old_hosts.begin(), old_hosts.end());
  std::set<std::string> new_hosts_set(new_hosts.begin(), new_hosts.end());
  std::set<std::string> new_hosts_only;

  std::set_difference(new_hosts_set.begin(), new_hosts_set.end(),
                      old_hosts_set.begin(), old_hosts_set.end(),
                      std::inserter(new_hosts_only, new_hosts_only.begin()));

  return !new_hosts_only.empty();
}

Project: Chrome

bool Extension::IsHostedAppPermission(const std::string& str) {
  for (size_t i = 0; i < Extension::kNumHostedAppPermissions; ++i) {
    if (str == Extension::kHostedAppPermissionNames[i]) {
      return true;
    }
  }
  return false;
}

Project: Chrome

bool Extension::IsPrivilegeIncrease(const bool granted_full_access,
                                    const std::set<std::string>& granted_apis,
                                    const URLPatternSet& granted_extent,
                                    const Extension* new_extension) {
  if (granted_full_access)
    return false;

  if (new_extension->HasFullPermissions())
    return true;

  if (!HasEffectiveAccessToAllHosts(granted_extent, granted_apis)) {
    if (new_extension->HasEffectiveAccessToAllHosts())
      return true;

    const URLPatternSet new_extent =
        new_extension->GetEffectiveHostPermissions();

    if (IsElevatedHostList(granted_extent.patterns(), new_extent.patterns()))
      return true;
  }

  std::set<std::string> new_apis = new_extension->api_permissions();
  std::set<std::string> new_apis_only;
  std::set_difference(new_apis.begin(), new_apis.end(),
                      granted_apis.begin(), granted_apis.end(),
                      std::inserter(new_apis_only, new_apis_only.begin()));

  size_t new_api_count = 0;
  for (std::set<std::string>::iterator i = new_apis_only.begin();
       i != new_apis_only.end(); ++i) {
    DCHECK_GT(PermissionMessage::ID_NONE, PermissionMessage::ID_UNKNOWN);
    if (GetPermissionMessageId(*i) > PermissionMessage::ID_NONE)
      new_api_count++;
  }

  if (new_api_count)
    return true;

  return false;
}

Project: Chrome

bool Extension::LoadAppIsolation(const DictionaryValue* manifest,
                                 std::string* error) {
  if (!CommandLine::ForCurrentProcess()->HasSwitch(
          switches::kEnableExperimentalAppManifests))
    return true;

  Value* temp = NULL;
  if (!manifest->Get(keys::kIsolation, &temp))
    return true;

  if (temp->GetType() != Value::TYPE_LIST) {
    *error = errors::kInvalidIsolation;
    return false;
  }

  ListValue* isolation_list = static_cast<ListValue*>(temp);
  for (size_t i = 0; i < isolation_list->GetSize(); ++i) {
    std::string isolation_string;
    if (!isolation_list->GetString(i, &isolation_string)) {
      *error = ExtensionErrorUtils::FormatErrorMessage(
          errors::kInvalidIsolationValue,
          base::UintToString(i));
      return false;
    }

    if (isolation_string == values::kIsolatedStorage) {
      is_storage_isolated_ = true;
    } else {
      LOG(WARNING) << "Did not recognize isolation type: "
                   << isolation_string;
    }
  }
  return true;
}

Project: Chrome

bool Extension::LoadUserScriptHelper(const DictionaryValue* content_script,
                                     int definition_index,
                                     int flags,
                                     std::string* error,
                                     UserScript* result) {
  URLPattern::ParseOption parse_strictness =
      (flags & STRICT_ERROR_CHECKS ? URLPattern::PARSE_STRICT
                                   : URLPattern::PARSE_LENIENT);

  if (content_script->HasKey(keys::kRunAt)) {
    std::string run_location;
    if (!content_script->GetString(keys::kRunAt, &run_location)) {
      *error = ExtensionErrorUtils::FormatErrorMessage(errors::kInvalidRunAt,
          base::IntToString(definition_index));
      return false;
    }

    if (run_location == values::kRunAtDocumentStart) {
      result->set_run_location(UserScript::DOCUMENT_START);
    } else if (run_location == values::kRunAtDocumentEnd) {
      result->set_run_location(UserScript::DOCUMENT_END);
    } else if (run_location == values::kRunAtDocumentIdle) {
      result->set_run_location(UserScript::DOCUMENT_IDLE);
    } else {
      *error = ExtensionErrorUtils::FormatErrorMessage(errors::kInvalidRunAt,
          base::IntToString(definition_index));
      return false;
    }
  }

  if (content_script->HasKey(keys::kAllFrames)) {
    bool all_frames = false;
    if (!content_script->GetBoolean(keys::kAllFrames, &all_frames)) {
      *error = ExtensionErrorUtils::FormatErrorMessage(
            errors::kInvalidAllFrames, base::IntToString(definition_index));
      return false;
    }
    result->set_match_all_frames(all_frames);
  }

  ListValue* matches = NULL;
  if (!content_script->GetList(keys::kMatches, &matches)) {
    *error = ExtensionErrorUtils::FormatErrorMessage(errors::kInvalidMatches,
        base::IntToString(definition_index));
    return false;
  }

  if (matches->GetSize() == 0) {
    *error = ExtensionErrorUtils::FormatErrorMessage(errors::kInvalidMatchCount,
        base::IntToString(definition_index));
    return false;
  }
  for (size_t j = 0; j < matches->GetSize(); ++j) {
    std::string match_str;
    if (!matches->GetString(j, &match_str)) {
      *error = ExtensionErrorUtils::FormatErrorMessage(
          errors::kInvalidMatch,
          base::IntToString(definition_index),
          base::IntToString(j),
          errors::kExpectString);
      return false;
    }

    URLPattern pattern(UserScript::kValidUserScriptSchemes);
    if (CanExecuteScriptEverywhere())
      pattern.set_valid_schemes(URLPattern::SCHEME_ALL);

    URLPattern::ParseResult parse_result = pattern.Parse(match_str,
                                                         parse_strictness);
    if (parse_result != URLPattern::PARSE_SUCCESS) {
      *error = ExtensionErrorUtils::FormatErrorMessage(
          errors::kInvalidMatch,
          base::IntToString(definition_index),
          base::IntToString(j),
          URLPattern::GetParseResultString(parse_result));
      return false;
    }

    if (pattern.MatchesScheme(chrome::kFileScheme) &&
        !CanExecuteScriptEverywhere()) {
      wants_file_access_ = true;
      if (!(flags & ALLOW_FILE_ACCESS))
        pattern.set_valid_schemes(
            pattern.valid_schemes() & ~URLPattern::SCHEME_FILE);
    }

    result->add_url_pattern(pattern);
  }

  if (content_script->HasKey(keys::kExcludeMatches)) {  // optional
    ListValue* exclude_matches = NULL;
    if (!content_script->GetList(keys::kExcludeMatches, &exclude_matches)) {
      *error = ExtensionErrorUtils::FormatErrorMessage(
          errors::kInvalidExcludeMatches,
          base::IntToString(definition_index));
      return false;
    }

    for (size_t j = 0; j < exclude_matches->GetSize(); ++j) {
      std::string match_str;
      if (!exclude_matches->GetString(j, &match_str)) {
        *error = ExtensionErrorUtils::FormatErrorMessage(
            errors::kInvalidExcludeMatch,
            base::IntToString(definition_index),
            base::IntToString(j),
            errors::kExpectString);
        return false;
      }

      URLPattern pattern(UserScript::kValidUserScriptSchemes);
      if (CanExecuteScriptEverywhere())
        pattern.set_valid_schemes(URLPattern::SCHEME_ALL);
      URLPattern::ParseResult parse_result = pattern.Parse(match_str,
                                                           parse_strictness);
      if (parse_result != URLPattern::PARSE_SUCCESS) {
        *error = ExtensionErrorUtils::FormatErrorMessage(
            errors::kInvalidExcludeMatch,
            base::IntToString(definition_index), base::IntToString(j),
            URLPattern::GetParseResultString(parse_result));
        return false;
      }

      result->add_exclude_url_pattern(pattern);
    }
  }

  if (!LoadGlobsHelper(content_script, definition_index, keys::kIncludeGlobs,
                       error, &UserScript::add_glob, result)) {
      return false;
  }

  if (!LoadGlobsHelper(content_script, definition_index, keys::kExcludeGlobs,
                       error, &UserScript::add_exclude_glob, result)) {
      return false;
  }

  ListValue* js = NULL;
  if (content_script->HasKey(keys::kJs) &&
      !content_script->GetList(keys::kJs, &js)) {
    *error = ExtensionErrorUtils::FormatErrorMessage(errors::kInvalidJsList,
        base::IntToString(definition_index));
    return false;
  }

  ListValue* css = NULL;
  if (content_script->HasKey(keys::kCss) &&
      !content_script->GetList(keys::kCss, &css)) {
    *error = ExtensionErrorUtils::FormatErrorMessage(errors::kInvalidCssList,
        base::IntToString(definition_index));
    return false;
  }

  if (((js ? js->GetSize() : 0) + (css ? css->GetSize() : 0)) == 0) {
    *error = ExtensionErrorUtils::FormatErrorMessage(errors::kMissingFile,
        base::IntToString(definition_index));
    return false;
  }

  if (js) {
    for (size_t script_index = 0; script_index < js->GetSize();
         ++script_index) {
      Value* value;
      std::string relative;
      if (!js->Get(script_index, &value) || !value->GetAsString(&relative)) {
        *error = ExtensionErrorUtils::FormatErrorMessage(errors::kInvalidJs,
            base::IntToString(definition_index),
            base::IntToString(script_index));
        return false;
      }
      GURL url = GetResourceURL(relative);
      ExtensionResource resource = GetResource(relative);
      result->js_scripts().push_back(UserScript::File(
          resource.extension_root(), resource.relative_path(), url));
    }
  }

  if (css) {
    for (size_t script_index = 0; script_index < css->GetSize();
         ++script_index) {
      Value* value;
      std::string relative;
      if (!css->Get(script_index, &value) || !value->GetAsString(&relative)) {
        *error = ExtensionErrorUtils::FormatErrorMessage(errors::kInvalidCss,
            base::IntToString(definition_index),
            base::IntToString(script_index));
        return false;
      }
      GURL url = GetResourceURL(relative);
      ExtensionResource resource = GetResource(relative);
      result->css_scripts().push_back(UserScript::File(
          resource.extension_root(), resource.relative_path(), url));
    }
  }

  return true;
}

Project: Chrome

Extension::PermissionMessage::PermissionMessage(
    Extension::PermissionMessage::MessageId message_id, string16 message)
    : message_id_(message_id),
      message_(message) {
}

Project: Chrome

UninstalledExtensionInfo::UninstalledExtensionInfo(
    const Extension& extension)
    : extension_id(extension.id()),
      extension_api_permissions(extension.api_permissions()),
      extension_type(extension.GetType()),
      update_url(extension.update_url()) {}

Project: Chrome

  scoped_refptr<Extension> LoadAndExpectSuccess(DictionaryValue* manifest,
                                                const std::string& name) {
    std::string error;
    scoped_refptr<Extension> extension = LoadExtension(manifest, &error);
    EXPECT_TRUE(extension) << "Unexpected success for " << name;
    EXPECT_EQ("", error) << "Unexpected no error for " << name;
    return extension;
  }