#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) {
if (protocol.empty())
return false;
if (!base::IsAsciiAlpha(protocol.front()))
return false;
if (protocol.length() > 1 &&
!base::ContainsOnlyChars(protocol, kValidProtocolChars)) {
return false;
}
return true;
}
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;
}
}
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();
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 (!IsValidOriginMatchingPattern(pattern)) {
errors->AddError(policy::key::kAutoLaunchProtocolsFromOrigins,
IDS_POLICY_INVALID_ORIGIN_ERROR, PolicyErrorPath{i});
}
}
if (origins_list->empty()) {
errors->AddError(policy::key::kAutoLaunchProtocolsFromOrigins,
IDS_POLICY_EMPTY_ORIGIN_LIST_ERROR, PolicyErrorPath{i});
}
}
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()) {
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;
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 (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)));
}
}