Commit b08d5a32 authored by Dominique Fauteux-Chapleau's avatar Dominique Fauteux-Chapleau Committed by Commit Bot

Create enterprise_connectors::AnalysisServiceSettings

This class is meant to parse the JSON of the connector policies and
give a clean abstraction to get settings and to eventually cache these
settings.

Change-Id: I7b15dddc36a0a8727c26cd5421eec04e3fdde2a0
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2147874Reviewed-by: default avatarRoger Tawa <rogerta@chromium.org>
Commit-Queue: Dominique Fauteux-Chapleau <domfc@chromium.org>
Cr-Commit-Position: refs/heads/master@{#761953}
parent 048dd965
...@@ -3185,6 +3185,8 @@ jumbo_static_library("browser") { ...@@ -3185,6 +3185,8 @@ jumbo_static_library("browser") {
"download/download_shelf_context_menu.h", "download/download_shelf_context_menu.h",
"download/download_shelf_controller.cc", "download/download_shelf_controller.cc",
"download/download_shelf_controller.h", "download/download_shelf_controller.h",
"enterprise/connectors/analysis_service_settings.cc",
"enterprise/connectors/analysis_service_settings.h",
"enterprise/connectors/common.cc", "enterprise/connectors/common.cc",
"enterprise/connectors/common.h", "enterprise/connectors/common.h",
"enterprise/connectors/connectors_manager.cc", "enterprise/connectors/connectors_manager.cc",
......
// 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_service_settings.h"
#include "components/policy/core/browser/url_util.h"
namespace enterprise_connectors {
namespace {
// Keys used to read a connector's policy values.
constexpr char kKeyServiceProvider[] = "service_provider";
constexpr char kKeyEnable[] = "enable";
constexpr char kKeyDisable[] = "disable";
constexpr char kKeyUrlList[] = "url_list";
constexpr char kKeyTags[] = "tags";
constexpr char kKeyBlockUntilVerdict[] = "block_until_verdict";
constexpr char kKeyBlockPasswordProtected[] = "block_password_protected";
constexpr char kKeyBlockLargeFiles[] = "block_large_files";
constexpr char kKeyBlockUnsupportedFileTypes[] = "block_unsupported_file_types";
} // namespace
AnalysisServiceSettings::AnalysisServiceSettings(
const base::Value& settings_value) {
if (!settings_value.is_dict())
return;
// The service provider identifier should always be there.
const std::string* service_provider =
settings_value.FindStringKey(kKeyServiceProvider);
if (service_provider)
service_provider_ = *service_provider;
else
return;
// Add the patterns to the settings, which configures settings.matcher and
// settings.*_pattern_settings. No enable patterns implies the settings are
// invalid.
url_matcher::URLMatcherConditionSet::ID id(0);
const base::Value* enable = settings_value.FindListKey(kKeyEnable);
if (enable && enable->is_list()) {
for (const base::Value& value : enable->GetList())
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->GetList())
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);
}
// static
base::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 base::nullopt;
}
base::Optional<AnalysisSettings> AnalysisServiceSettings::GetAnalysisSettings(
const GURL& url) const {
if (!IsValid())
return base::nullopt;
auto matches = matcher_.MatchURL(url);
if (matches.empty())
return base::nullopt;
auto tags = GetTags(matches);
if (tags.empty())
return base::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_;
return settings;
}
void AnalysisServiceSettings::AddUrlPatternSettings(
const base::Value& url_settings_value,
bool enabled,
url_matcher::URLMatcherConditionSet::ID* id) {
DCHECK(id);
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 && tags->is_list()) {
for (const base::Value& tag : tags->GetList()) {
if (tag.is_string())
setting.tags.insert(tag.GetString());
}
} else {
return;
}
// 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 && url_list->is_list()) {
const base::ListValue* url_list_value = nullptr;
url_list->GetAsList(&url_list_value);
DCHECK(url_list_value);
policy::url_util::AddFilters(&matcher_, enabled, id, url_list_value);
} else {
return;
}
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_.empty())
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() = default;
AnalysisServiceSettings::URLPatternSettings::URLPatternSettings() = default;
AnalysisServiceSettings::URLPatternSettings::URLPatternSettings(
const AnalysisServiceSettings::URLPatternSettings&) = default;
AnalysisServiceSettings::URLPatternSettings::~URLPatternSettings() = default;
} // namespace enterprise_connectors
// 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.
#ifndef CHROME_BROWSER_ENTERPRISE_CONNECTORS_ANALYSIS_SERVICE_SETTINGS_H_
#define CHROME_BROWSER_ENTERPRISE_CONNECTORS_ANALYSIS_SERVICE_SETTINGS_H_
#include "base/values.h"
#include "chrome/browser/enterprise/connectors/common.h"
#include "components/url_matcher/url_matcher.h"
namespace enterprise_connectors {
// The settings for an analysis service obtained from a connector policy.
class AnalysisServiceSettings {
public:
explicit AnalysisServiceSettings(const base::Value& settings_value);
~AnalysisServiceSettings();
// Get the settings to apply to a specific analysis. base::nullopt implies no
// analysis should take place.
base::Optional<AnalysisSettings> GetAnalysisSettings(const GURL& url) const;
private:
// The setting to apply when a specific URL pattern is matched.
struct URLPatternSettings {
URLPatternSettings();
URLPatternSettings(const URLPatternSettings&);
~URLPatternSettings();
// Tags that correspond to the pattern.
std::set<std::string> tags;
};
// Map from an ID representing a specific matched pattern to its settings.
using PatternSettings =
std::map<url_matcher::URLMatcherConditionSet::ID, URLPatternSettings>;
// Accessors for the pattern setting maps.
static base::Optional<URLPatternSettings> GetPatternSettings(
const PatternSettings& patterns,
url_matcher::URLMatcherConditionSet::ID match);
// Returns true if the settings were initialized correctly. If this returns
// false, then GetAnalysisSettings will always return base::nullopt.
bool IsValid() const;
// Updates the states of |matcher_|, |enabled_patterns_settings_| and/or
// |disabled_patterns_settings_| from a policy value.
void AddUrlPatternSettings(const base::Value& url_settings_value,
bool enabled,
url_matcher::URLMatcherConditionSet::ID* id);
// Return tags found in |enabled_patterns_settings| corresponding to the
// matches while excluding the ones in |disable_patterns_settings|.
std::set<std::string> GetTags(
const std::set<url_matcher::URLMatcherConditionSet::ID>& matches) const;
// The service provider's identifier. This is unique amongst providers.
std::string service_provider_;
// The URL matcher created from the patterns set in the analysis policy. The
// condition set IDs returned after matching against a URL can be used to
// check |enabled_patterns_settings| and |disable_patterns_settings| to
// obtain URL-specific settings.
url_matcher::URLMatcher matcher_;
// These members map URL patterns to corresponding settings. If an entry in
// the "enabled" or "disabled" lists contains more than one pattern in its
// "url_list" property, only the last pattern's matcher ID will be added the
// map. This keeps the count of these maps smaller and keeps the code from
// duplicating memory for the settings, which are the same for all URL
// patterns in a given entry. This optimization works by using
// std::map::upper_bound to access these maps. The IDs in the disabled
// settings must be greater than the ones in the enabled settings for this to
// work and avoid having the two maps cover an overlap of matches.
PatternSettings enabled_patterns_settings_;
PatternSettings disabled_patterns_settings_;
BlockUntilVerdict block_until_verdict_ = BlockUntilVerdict::NO_BLOCK;
bool block_password_protected_files_ = false;
bool block_large_files_ = false;
bool block_unsupported_file_types_ = false;
};
} // namespace enterprise_connectors
#endif // CHROME_BROWSER_ENTERPRISE_CONNECTORS_ANALYSIS_SERVICE_SETTINGS_H_
// 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_service_settings.h"
#include "base/json/json_reader.h"
#include "base/no_destructor.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace enterprise_connectors {
namespace {
struct TestParam {
TestParam(const char* url,
const char* settings_value,
AnalysisSettings* expected_settings)
: url(url),
settings_value(settings_value),
expected_settings(expected_settings) {}
const char* url;
const char* settings_value;
AnalysisSettings* expected_settings;
};
constexpr char kNormalSettings[] = R"({
"service_provider": "Google",
"enable": [
{"url_list": ["*"], "tags": ["dlp", "malware"]},
],
"disable": [
{"url_list": ["no.dlp.com", "no.dlp.or.malware.ca"], "tags": ["dlp"]},
{"url_list": ["no.malware.com", "no.dlp.or.malware.ca"],
"tags": ["malware"]},
{"url_list": ["scan2.com"], "tags": ["dlp", "malware"]},
],
"block_until_verdict": 1,
"block_password_protected": true,
"block_large_files": true,
"block_unsupported_file_types": true,
})";
constexpr char kOnlyEnabledPatternsSettings[] = R"({
"service_provider": "Google",
"enable": [
{"url_list": ["scan1.com", "scan2.com"], "tags": ["enabled"]},
],
})";
constexpr char kNoProviderSettings[] = R"({
"enable": [
{"url_list": ["*"], "tags": ["dlp", "malware"]},
],
"disable": [
{"url_list": ["no.dlp.com", "no.dlp.or.malware.ca"], "tags": ["dlp"]},
{"url_list": ["no.malware.com", "no.dlp.or.malware.ca"],
"tags": ["malware"]},
{"url_list": ["scan2.com"], "tags": ["dlp", "malware"]},
],
"block_until_verdict": 1,
"block_password_protected": true,
"block_large_files": true,
"block_unsupported_file_types": true,
})";
constexpr char kNoEnabledPatternsSettings[] = R"({
"service_provider": "Google",
"disable": [
{"url_list": ["no.dlp.com", "no.dlp.or.malware.ca"], "tags": ["dlp"]},
{"url_list": ["no.malware.com", "no.dlp.or.malware.ca"],
"tags": ["malware"]},
{"url_list": ["scan2.com"], "tags": ["dlp", "malware"]},
],
"block_until_verdict": 1,
"block_password_protected": true,
"block_large_files": true,
"block_unsupported_file_types": true,
})";
constexpr char kScan1DotCom[] = "https://scan1.com";
constexpr char kScan2DotCom[] = "https://scan2.com";
constexpr char kNoDlpDotCom[] = "https://no.dlp.com";
constexpr char kNoMalwareDotCom[] = "https://no.malware.com";
constexpr char kNoDlpOrMalwareDotCa[] = "https://no.dlp.or.malware.ca";
AnalysisSettings* OnlyEnabledSettings() {
static base::NoDestructor<AnalysisSettings> settings([]() {
AnalysisSettings settings;
settings.tags = {"enabled"};
return settings;
}());
return settings.get();
}
AnalysisSettings NormalSettingsWithTags(std::set<std::string> tags) {
AnalysisSettings settings;
settings.tags = tags;
settings.block_until_verdict = BlockUntilVerdict::BLOCK;
settings.block_password_protected_files = true;
settings.block_large_files = true;
settings.block_unsupported_file_types = true;
return settings;
}
AnalysisSettings* NormalDlpSettings() {
static base::NoDestructor<AnalysisSettings> settings(
NormalSettingsWithTags({"dlp"}));
return settings.get();
}
AnalysisSettings* NormalMalwareSettings() {
static base::NoDestructor<AnalysisSettings> settings(
NormalSettingsWithTags({"malware"}));
return settings.get();
}
AnalysisSettings* NormalDlpAndMalwareSettings() {
static base::NoDestructor<AnalysisSettings> settings(
NormalSettingsWithTags({"dlp", "malware"}));
return settings.get();
}
AnalysisSettings* NoSettings() {
return nullptr;
}
} // namespace
class AnalysisServiceSettingsTest : public testing::TestWithParam<TestParam> {
public:
GURL url() const { return GURL(GetParam().url); }
const char* settings_value() const { return GetParam().settings_value; }
AnalysisSettings* expected_settings() const {
return GetParam().expected_settings;
}
private:
content::BrowserTaskEnvironment task_environment_;
};
TEST_P(AnalysisServiceSettingsTest, Test) {
// auto value = base::Value::AsDictionaryValue(base::Value(pref_value()));
auto settings = base::JSONReader::Read(settings_value(),
base::JSON_ALLOW_TRAILING_COMMAS);
ASSERT_TRUE(settings.has_value());
AnalysisServiceSettings service_settings(settings.value());
auto analysis_settings = service_settings.GetAnalysisSettings(url());
ASSERT_EQ((expected_settings() != nullptr), analysis_settings.has_value());
if (analysis_settings.has_value()) {
ASSERT_EQ(analysis_settings.value().tags, expected_settings()->tags);
ASSERT_EQ(analysis_settings.value().block_until_verdict,
expected_settings()->block_until_verdict);
ASSERT_EQ(analysis_settings.value().block_password_protected_files,
expected_settings()->block_password_protected_files);
ASSERT_EQ(analysis_settings.value().block_large_files,
expected_settings()->block_large_files);
ASSERT_EQ(analysis_settings.value().block_unsupported_file_types,
expected_settings()->block_unsupported_file_types);
}
}
INSTANTIATE_TEST_CASE_P(
,
AnalysisServiceSettingsTest,
testing::Values(
// Validate that the enabled patterns match the expected patterns.
TestParam(kScan1DotCom,
kOnlyEnabledPatternsSettings,
OnlyEnabledSettings()),
TestParam(kScan2DotCom,
kOnlyEnabledPatternsSettings,
OnlyEnabledSettings()),
TestParam(kNoDlpDotCom, kOnlyEnabledPatternsSettings, NoSettings()),
TestParam(kNoMalwareDotCom, kOnlyEnabledPatternsSettings, NoSettings()),
TestParam(kNoDlpOrMalwareDotCa,
kOnlyEnabledPatternsSettings,
NoSettings()),
// Validate that each URL gets the correct tag on the normal settings.
TestParam(kScan1DotCom, kNormalSettings, NormalDlpAndMalwareSettings()),
TestParam(kScan2DotCom, kNormalSettings, NoSettings()),
TestParam(kNoDlpDotCom, kNormalSettings, NormalMalwareSettings()),
TestParam(kNoMalwareDotCom, kNormalSettings, NormalDlpSettings()),
TestParam(kNoDlpOrMalwareDotCa, kNormalSettings, NoSettings()),
// Validate that each URL gets no settings when either the provider is
// absent or when there are no enabled patterns.
TestParam(kScan1DotCom, kNoProviderSettings, NoSettings()),
TestParam(kScan2DotCom, kNoProviderSettings, NoSettings()),
TestParam(kNoDlpDotCom, kNoProviderSettings, NoSettings()),
TestParam(kNoMalwareDotCom, kNoProviderSettings, NoSettings()),
TestParam(kNoDlpOrMalwareDotCa, kNoProviderSettings, NoSettings()),
TestParam(kScan1DotCom, kNoEnabledPatternsSettings, NoSettings()),
TestParam(kScan2DotCom, kNoEnabledPatternsSettings, NoSettings()),
TestParam(kNoDlpDotCom, kNoEnabledPatternsSettings, NoSettings()),
TestParam(kNoMalwareDotCom, kNoEnabledPatternsSettings, NoSettings()),
TestParam(kNoDlpOrMalwareDotCa,
kNoEnabledPatternsSettings,
NoSettings())));
} // namespace enterprise_connectors
...@@ -43,10 +43,10 @@ struct AnalysisSettings { ...@@ -43,10 +43,10 @@ struct AnalysisSettings {
GURL analysis_url; GURL analysis_url;
std::set<std::string> tags; std::set<std::string> tags;
BlockUntilVerdict block_until_verdict; BlockUntilVerdict block_until_verdict = BlockUntilVerdict::NO_BLOCK;
bool block_password_protected_files; bool block_password_protected_files = false;
bool block_large_files; bool block_large_files = false;
bool block_unsupported_file_types; bool block_unsupported_file_types = false;
}; };
struct ReportingSettings { struct ReportingSettings {
......
...@@ -3898,6 +3898,7 @@ test("unit_tests") { ...@@ -3898,6 +3898,7 @@ test("unit_tests") {
"../browser/diagnostics/diagnostics_model_unittest.cc", "../browser/diagnostics/diagnostics_model_unittest.cc",
"../browser/download/download_commands_unittest.cc", "../browser/download/download_commands_unittest.cc",
"../browser/download/download_shelf_unittest.cc", "../browser/download/download_shelf_unittest.cc",
"../browser/enterprise/connectors/analysis_service_settings_unittest.cc",
"../browser/enterprise/connectors/connectors_manager_unittest.cc", "../browser/enterprise/connectors/connectors_manager_unittest.cc",
"../browser/enterprise/connectors/enterprise_connectors_policy_handler_unittest.cc", "../browser/enterprise/connectors/enterprise_connectors_policy_handler_unittest.cc",
"../browser/enterprise/reporting/browser_report_generator_unittest.cc", "../browser/enterprise/reporting/browser_report_generator_unittest.cc",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment