910e62b5创建于 1月15日历史提交
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/external_protocol/auto_launch_protocols_policy_handler.h"

#include <memory>
#include <string>
#include <string_view>
#include <utility>

#include "base/strings/string_util.h"
#include "base/values.h"
#include "chrome/browser/external_protocol/constants.h"
#include "chrome/common/pref_names.h"
#include "components/policy/core/browser/policy_error_map.h"
#include "components/policy/core/common/policy_map.h"
#include "components/policy/core/common/policy_pref_names.h"
#include "components/policy/core/common/schema.h"
#include "components/policy/policy_constants.h"
#include "components/prefs/pref_value_map.h"
#include "components/strings/grit/components_strings.h"
#include "url/gurl.h"

namespace policy {

namespace {
const char kValidProtocolChars[] =
    "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+-.";

bool IsValidProtocol(std::string_view protocol) {
  // RFC3986: ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
  if (protocol.empty())
    return false;
  if (!base::IsAsciiAlpha(protocol.front()))
    return false;
  if (protocol.length() > 1 &&
      !base::ContainsOnlyChars(protocol, kValidProtocolChars)) {
    return false;
  }
  return true;
}

// Catches obvious errors like including a [/path] or [@query] element in the
// pattern.
bool IsValidOriginMatchingPattern(std::string_view origin_pattern) {
  GURL gurl(origin_pattern);
  if (gurl.has_path() && gurl.path() != "/") {
    return false;
  }
  if (gurl.has_query())
    return false;
  return true;
}

}  // namespace

AutoLaunchProtocolsPolicyHandler::AutoLaunchProtocolsPolicyHandler(
    const policy::Schema& chrome_schema)
    : SchemaValidatingPolicyHandler(
          policy::key::kAutoLaunchProtocolsFromOrigins,
          chrome_schema.GetKnownProperty(
              policy::key::kAutoLaunchProtocolsFromOrigins),
          policy::SCHEMA_ALLOW_UNKNOWN) {}

AutoLaunchProtocolsPolicyHandler::~AutoLaunchProtocolsPolicyHandler() = default;

bool AutoLaunchProtocolsPolicyHandler::CheckPolicySettings(
    const PolicyMap& policies,
    PolicyErrorMap* errors) {
  std::unique_ptr<base::Value> policy_value;
  if (!CheckAndGetValue(policies, nullptr, &policy_value) || !policy_value)
    return false;

  base::Value::List& policy_list = policy_value->GetList();
  for (size_t i = 0; i < policy_list.size(); ++i) {
    const base::Value::Dict& protocol_origins_map = policy_list[i].GetDict();

    // If the protocol is invalid mark it as an error.
    const std::string* protocol = protocol_origins_map.FindString(
        policy::external_protocol::kProtocolNameKey);
    DCHECK(protocol);
    if (!IsValidProtocol(*protocol)) {
      errors->AddError(policy::key::kAutoLaunchProtocolsFromOrigins,
                       IDS_POLICY_INVALID_PROTOCOL_ERROR, PolicyErrorPath{i});
    }

    const base::Value::List* origins_list = protocol_origins_map.FindList(
        policy::external_protocol::kOriginListKey);
    for (const auto& entry : *origins_list) {
      const std::string pattern = entry.GetString();
      // If it's not a valid origin pattern mark it as an error.
      if (!IsValidOriginMatchingPattern(pattern)) {
        errors->AddError(policy::key::kAutoLaunchProtocolsFromOrigins,
                         IDS_POLICY_INVALID_ORIGIN_ERROR, PolicyErrorPath{i});
      }
    }
    // If the origin list is empty mark it as an error.
    if (origins_list->empty()) {
      errors->AddError(policy::key::kAutoLaunchProtocolsFromOrigins,
                       IDS_POLICY_EMPTY_ORIGIN_LIST_ERROR, PolicyErrorPath{i});
    }
  }

  // Always continue to ApplyPolicySettings which can remove invalid values and
  // apply the valid ones.
  return true;
}

void AutoLaunchProtocolsPolicyHandler::ApplyPolicySettings(
    const PolicyMap& policies,
    PrefValueMap* prefs) {
  std::unique_ptr<base::Value> policy_value;
  CheckAndGetValue(policies, nullptr, &policy_value);

  base::Value::List validated_pref_values;
  for (auto& protocol_origins_map : policy_value->GetList()) {
    // If the protocol is invalid skip the entry.
    base::Value::Dict& protocol_origins_dict = protocol_origins_map.GetDict();
    const std::string* protocol = protocol_origins_dict.FindString(
        policy::external_protocol::kProtocolNameKey);
    DCHECK(protocol);
    if (!IsValidProtocol(*protocol))
      continue;

    // Remove invalid patterns from the list.
    base::Value::List* origin_patterns_list = protocol_origins_dict.FindList(
        policy::external_protocol::kOriginListKey);
    origin_patterns_list->EraseIf([](const base::Value& pattern) {
      return !IsValidOriginMatchingPattern(pattern.GetString());
    });
    // If the origin list is empty skip the entry.
    if (origin_patterns_list->size() == 0)
      continue;

    validated_pref_values.Append(protocol_origins_dict.Clone());
  }
  prefs->SetValue(prefs::kAutoLaunchProtocolsFromOrigins,
                  base::Value(std::move(validated_pref_values)));
}

}  // namespace policy