blob: fed0fe2be75354a8fcb49171ab12b473ad38495b [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/enterprise/connectors/analysis/analysis_service_settings.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/enterprise/connectors/service_provider_config.h"
#include "components/url_matcher/url_util.h"
namespace enterprise_connectors {
AnalysisServiceSettings::AnalysisServiceSettings(
const base::Value& settings_value,
const ServiceProviderConfig& service_provider_config) {
if (!settings_value.is_dict())
return;
// The service provider identifier should always be there, and it should match
// an existing provider.
const std::string* service_provider_name =
settings_value.FindStringKey(kKeyServiceProvider);
if (service_provider_name) {
service_provider_name_ = *service_provider_name;
service_provider_ =
service_provider_config.GetServiceProvider(*service_provider_name);
if (!service_provider_)
return;
} else {
return;
}
// Add the patterns to the settings, which configures settings.matcher and
// settings.*_pattern_settings. No enable patterns implies the settings are
// invalid.
matcher_ = std::make_unique<url_matcher::URLMatcher>();
url_matcher::URLMatcherConditionSet::ID id(0);
const base::Value* enable = settings_value.FindListKey(kKeyEnable);
if (enable && enable->is_list() && !enable->GetListDeprecated().empty()) {
for (const base::Value& value : enable->GetListDeprecated())
AddUrlPatternSettings(value, true, &id);
} else {
return;
}
const base::Value* disable = settings_value.FindListKey(kKeyDisable);
if (disable && disable->is_list()) {
for (const base::Value& value : disable->GetListDeprecated())
AddUrlPatternSettings(value, false, &id);
}
// The block settings are optional, so a default is used if they can't be
// found.
block_until_verdict_ =
settings_value.FindIntKey(kKeyBlockUntilVerdict).value_or(0)
? BlockUntilVerdict::BLOCK
: BlockUntilVerdict::NO_BLOCK;
block_password_protected_files_ =
settings_value.FindBoolKey(kKeyBlockPasswordProtected).value_or(false);
block_large_files_ =
settings_value.FindBoolKey(kKeyBlockLargeFiles).value_or(false);
block_unsupported_file_types_ =
settings_value.FindBoolKey(kKeyBlockUnsupportedFileTypes).value_or(false);
minimum_data_size_ =
settings_value.FindIntKey(kKeyMinimumDataSize).value_or(100);
const base::Value* custom_messages =
settings_value.FindListKey(kKeyCustomMessages);
if (custom_messages && custom_messages->is_list()) {
for (const base::Value& value : custom_messages->GetListDeprecated()) {
// As of now, this list will contain one message per tag. At some point,
// the server may start sending one message per language/tag pair. If this
// is the case, this code should be changed to match the language to
// Chrome's UI language.
const std::string* tag = value.FindStringKey(kKeyCustomMessagesTag);
if (!tag)
continue;
CustomMessageData data;
const std::string* message =
value.FindStringKey(kKeyCustomMessagesMessage);
// This string originates as a protobuf string on the server, which are
// utf8 and it's used in the UI where it needs to be encoded as utf16. Do
// the conversion now, otherwise code down the line may not be able to
// determine if the std::string is ASCII or UTF8 before passing it to the
// UI.
data.message = base::UTF8ToUTF16(message ? *message : "");
const std::string* url =
value.FindStringKey(kKeyCustomMessagesLearnMoreUrl);
data.learn_more_url = url ? GURL(*url) : GURL();
custom_message_data_[*tag] = data;
}
}
const base::Value* require_justification_tags =
settings_value.FindListKey(kKeyRequireJustificationTags);
if (require_justification_tags && require_justification_tags->is_list()) {
for (const base::Value& tag :
require_justification_tags->GetListDeprecated()) {
tags_requiring_justification_.insert(tag.GetString());
}
}
}
// static
absl::optional<AnalysisServiceSettings::URLPatternSettings>
AnalysisServiceSettings::GetPatternSettings(
const PatternSettings& patterns,
url_matcher::URLMatcherConditionSet::ID match) {
// If the pattern exists directly in the map, return its settings.
if (patterns.count(match) == 1)
return patterns.at(match);
// If the pattern doesn't exist in the map, it might mean that it wasn't the
// only pattern to correspond to its settings and that the ID added to
// the map was the one of the last pattern corresponding to those settings.
// This means the next match ID greater than |match| has the correct settings
// if it exists.
auto next = patterns.upper_bound(match);
if (next != patterns.end())
return next->second;
return absl::nullopt;
}
absl::optional<AnalysisSettings> AnalysisServiceSettings::GetAnalysisSettings(
const GURL& url) const {
if (!IsValid())
return absl::nullopt;
DCHECK(matcher_);
auto matches = matcher_->MatchURL(url);
if (matches.empty())
return absl::nullopt;
auto tags = GetTags(matches);
if (tags.empty())
return absl::nullopt;
AnalysisSettings settings;
settings.tags = std::move(tags);
settings.block_until_verdict = block_until_verdict_;
settings.block_password_protected_files = block_password_protected_files_;
settings.block_large_files = block_large_files_;
settings.block_unsupported_file_types = block_unsupported_file_types_;
settings.analysis_url = GURL(service_provider_->analysis_url());
DCHECK(settings.analysis_url.is_valid());
settings.minimum_data_size = minimum_data_size_;
settings.custom_message_data = custom_message_data_;
settings.tags_requiring_justification = tags_requiring_justification_;
return settings;
}
bool AnalysisServiceSettings::ShouldBlockUntilVerdict() const {
if (!IsValid())
return false;
return block_until_verdict_ == BlockUntilVerdict::BLOCK;
}
absl::optional<std::u16string> AnalysisServiceSettings::GetCustomMessage(
const std::string& tag) {
const auto& element = custom_message_data_.find(tag);
if (!IsValid() || element == custom_message_data_.end() ||
element->second.message.empty()) {
return absl::nullopt;
}
return element->second.message;
}
absl::optional<GURL> AnalysisServiceSettings::GetLearnMoreUrl(
const std::string& tag) {
const auto& element = custom_message_data_.find(tag);
if (!IsValid() || element == custom_message_data_.end() ||
element->second.learn_more_url.is_empty()) {
return absl::nullopt;
}
return element->second.learn_more_url;
}
void AnalysisServiceSettings::AddUrlPatternSettings(
const base::Value& url_settings_value,
bool enabled,
url_matcher::URLMatcherConditionSet::ID* id) {
DCHECK(id);
DCHECK(service_provider_);
if (enabled)
DCHECK(disabled_patterns_settings_.empty());
else
DCHECK(!enabled_patterns_settings_.empty());
URLPatternSettings setting;
const base::Value* tags = url_settings_value.FindListKey(kKeyTags);
if (!tags)
return;
for (const base::Value& tag : tags->GetListDeprecated()) {
if (tag.is_string() &&
(service_provider_->analysis_tags().count(tag.GetString()) == 1)) {
setting.tags.insert(tag.GetString());
}
}
// Add the URL patterns to the matcher and store the condition set IDs.
const base::Value* url_list = url_settings_value.FindListKey(kKeyUrlList);
if (!url_list)
return;
url_matcher::util::AddFilters(matcher_.get(), enabled, id,
&base::Value::AsListValue(*url_list));
if (enabled)
enabled_patterns_settings_[*id] = std::move(setting);
else
disabled_patterns_settings_[*id] = std::move(setting);
}
std::set<std::string> AnalysisServiceSettings::GetTags(
const std::set<url_matcher::URLMatcherConditionSet::ID>& matches) const {
std::set<std::string> enable_tags;
std::set<std::string> disable_tags;
for (const url_matcher::URLMatcherConditionSet::ID match : matches) {
// Enabled patterns need to be checked first, otherwise they always match
// the first disabled pattern.
bool enable = true;
auto maybe_pattern_setting =
GetPatternSettings(enabled_patterns_settings_, match);
if (!maybe_pattern_setting.has_value()) {
maybe_pattern_setting =
GetPatternSettings(disabled_patterns_settings_, match);
enable = false;
}
DCHECK(maybe_pattern_setting.has_value());
auto tags = std::move(maybe_pattern_setting.value().tags);
if (enable)
enable_tags.insert(tags.begin(), tags.end());
else
disable_tags.insert(tags.begin(), tags.end());
}
for (const std::string& tag_to_disable : disable_tags)
enable_tags.erase(tag_to_disable);
return enable_tags;
}
bool AnalysisServiceSettings::IsValid() const {
// The settings are invalid if no provider was given.
if (!service_provider_)
return false;
// The settings are invalid if no enabled pattern(s) exist since that would
// imply no URL can ever have an analysis.
if (enabled_patterns_settings_.empty())
return false;
return true;
}
AnalysisServiceSettings::AnalysisServiceSettings(AnalysisServiceSettings&&) =
default;
AnalysisServiceSettings::~AnalysisServiceSettings() = default;
AnalysisServiceSettings::URLPatternSettings::URLPatternSettings() = default;
AnalysisServiceSettings::URLPatternSettings::URLPatternSettings(
const AnalysisServiceSettings::URLPatternSettings&) = default;
AnalysisServiceSettings::URLPatternSettings::URLPatternSettings(
AnalysisServiceSettings::URLPatternSettings&&) = default;
AnalysisServiceSettings::URLPatternSettings&
AnalysisServiceSettings::URLPatternSettings::operator=(
const AnalysisServiceSettings::URLPatternSettings&) = default;
AnalysisServiceSettings::URLPatternSettings&
AnalysisServiceSettings::URLPatternSettings::operator=(
AnalysisServiceSettings::URLPatternSettings&&) = default;
AnalysisServiceSettings::URLPatternSettings::~URLPatternSettings() = default;
} // namespace enterprise_connectors