blob: a147875b434c43c92bb2751692dfc5e2c2da07ab [file] [log] [blame]
// Copyright 2020 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/browser/external_protocol/auto_launch_protocols_policy_handler.h"
#include <memory>
#include <string>
#include <utility>
#include "base/strings/string_util.h"
#include "base/values.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/policy_constants.h"
#include "components/prefs/pref_value_map.h"
#include "components/strings/grit/components_strings.h"
#include "url/gurl.h"
namespace policy {
const char AutoLaunchProtocolsPolicyHandler::kProtocolNameKey[] = "protocol";
const char AutoLaunchProtocolsPolicyHandler::kOriginListKey[] =
"allowed_origins";
namespace {
const char kValidProtocolChars[] =
"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+-.";
bool IsValidProtocol(const base::StringPiece 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(const base::StringPiece origin_pattern) {
GURL gurl(origin_pattern);
if (gurl.has_path() && gurl.path_piece() != "/")
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::ConstListView policy_list = policy_value->GetList();
for (size_t i = 0; i < policy_list.size(); ++i) {
const base::DictionaryValue& protocol_origins_map =
base::Value::AsDictionaryValue(policy_list[i]);
// If the protocol is invalid mark it as an error.
const std::string* protocol = protocol_origins_map.FindStringKey(
AutoLaunchProtocolsPolicyHandler::kProtocolNameKey);
DCHECK(protocol);
if (!IsValidProtocol(*protocol)) {
errors->AddError(policy::key::kAutoLaunchProtocolsFromOrigins, i,
IDS_POLICY_VALUE_FORMAT_ERROR);
}
const base::Value* origins_list = protocol_origins_map.FindListKey(
AutoLaunchProtocolsPolicyHandler::kOriginListKey);
for (const auto& entry : origins_list->GetList()) {
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, i,
IDS_POLICY_VALUE_FORMAT_ERROR);
}
}
// If the origin list is empty mark it as an error.
if (origins_list->GetList().empty()) {
errors->AddError(policy::key::kAutoLaunchProtocolsFromOrigins, i,
IDS_POLICY_VALUE_FORMAT_ERROR);
}
}
// 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::ListValue validated_pref_values;
for (auto& protocol_origins_map : policy_value->GetList()) {
// If the protocol is invalid skip the entry.
const std::string* protocol = protocol_origins_map.FindStringKey(
AutoLaunchProtocolsPolicyHandler::kProtocolNameKey);
DCHECK(protocol);
if (!IsValidProtocol(*protocol))
continue;
// Remove invalid patterns from the list.
base::Value* origin_patterns_list = protocol_origins_map.FindListKey(
AutoLaunchProtocolsPolicyHandler::kOriginListKey);
origin_patterns_list->EraseListValueIf([](const base::Value& pattern) {
return !IsValidOriginMatchingPattern(pattern.GetString());
});
// If the origin list is empty skip the entry.
if (origin_patterns_list->GetList().size() == 0)
continue;
validated_pref_values.Append(protocol_origins_map.Clone());
}
prefs->SetValue(prefs::kAutoLaunchProtocolsFromOrigins,
std::move(validated_pref_values));
}
} // namespace policy