blob: 70dc7e02855ddd6f2fbc93e66c799e3f1b1d9fc7 [file] [log] [blame]
// 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/enterprise/connectors/connectors_service.h"
#include <tuple>
#include "base/json/json_reader.h"
#include "base/memory/raw_ptr.h"
#include "base/strings/stringprintf.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_future.h"
#include "build/build_config.h"
#include "chrome/browser/enterprise/connectors/common.h"
#include "chrome/browser/enterprise/connectors/connectors_manager.h"
#include "chrome/browser/enterprise/connectors/test/deep_scanning_test_utils.h"
#include "chrome/browser/policy/dm_token_utils.h"
#include "chrome/browser/profiles/profile_testing_helper.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile_manager.h"
#include "components/enterprise/browser/controller/fake_browser_dm_token_storage.h"
#include "components/enterprise/common/proto/connectors.pb.h"
#include "components/enterprise/connectors/core/common.h"
#include "components/enterprise/connectors/core/connectors_prefs.h"
#include "components/enterprise/connectors/core/service_provider_config.h"
#include "components/policy/core/common/policy_types.h"
#include "content/public/test/browser_task_environment.h"
#include "storage/browser/file_system/file_system_url.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
#if BUILDFLAG(IS_CHROMEOS)
#include "base/strings/strcat.h"
#include "chromeos/components/mgs/managed_guest_session_test_utils.h"
#include "extensions/common/constants.h"
#endif
namespace enterprise_connectors {
namespace {
#if BUILDFLAG(ENTERPRISE_CONTENT_ANALYSIS)
constexpr char kEmptySettingsPref[] = "[]";
constexpr char kNormalReportingSettingsPref[] = R"([
{
"service_provider": "google"
}
])";
#endif // BUILDFLAG(ENTERPRISE_CONTENT_ANALYSIS)
#if BUILDFLAG(ENTERPRISE_CONTENT_ANALYSIS)
constexpr char kWildcardAnalysisSettingsPref[] = R"([
{
"service_provider": "google",
"enable": [
{"url_list": ["*"], "tags": ["dlp", "malware"]}
]
}
])";
constexpr char kCustomMessage[] = "Custom Admin Message";
constexpr char kCustomUrl[] = "https://learn.more.com";
#endif // BUILDFLAG(ENTERPRISE_CONTENT_ANALYSIS)
constexpr char kFakeDmToken[] = "fake-token";
#if !BUILDFLAG(IS_CHROMEOS)
constexpr char kFakeDeviceId[] = "fake-device-id";
#endif
#if BUILDFLAG(ENTERPRISE_CONTENT_ANALYSIS)
std::string CreateCustomUIPref(const char* custom_message,
const char* custom_url,
bool bypass_enabled) {
std::string custom_messages_section;
if (custom_message || custom_url) {
std::string message_section =
custom_message
? base::StringPrintf(R"("message": "%s" ,)", custom_message)
: "";
std::string learn_more_url_section =
custom_url
? base::StringPrintf(R"("learn_more_url": "%s" ,)", custom_url)
: "";
custom_messages_section = base::StringPrintf(
R"( "custom_messages": [
{ "language": "default",
%s
%s
"tag": "dlp"
} ] ,)",
message_section.c_str(), learn_more_url_section.c_str());
}
std::string bypass_enabled_section;
if (bypass_enabled) {
bypass_enabled_section = R"("require_justification_tags": [ "dlp"],)";
}
std::string pref = base::StringPrintf(
R"({ "enable": [{"url_list": ["*"], "tags": ["dlp"]}],
%s
%s
"service_provider": "google"
})",
custom_messages_section.c_str(), bypass_enabled_section.c_str());
return pref;
}
#endif // BUILDFLAG(ENTERPRISE_CONTENT_ANALYSIS)
} // namespace
class ConnectorsServiceTest : public testing::Test {
public:
ConnectorsServiceTest()
: profile_manager_(TestingBrowserProcess::GetGlobal()) {
EXPECT_TRUE(profile_manager_.SetUp());
profile_ = profile_manager_.CreateTestingProfile("test-user");
policy::SetDMTokenForTesting(
policy::DMToken::CreateValidToken(kFakeDmToken));
}
#if !BUILDFLAG(IS_CHROMEOS)
void SetUp() override {
fake_browser_dm_token_storage_.SetClientId(kFakeDeviceId);
policy::BrowserDMTokenStorage::SetForTesting(
&fake_browser_dm_token_storage_);
fake_browser_dm_token_storage_.ResetForTesting();
}
#endif
protected:
content::BrowserTaskEnvironment task_environment_;
base::test::ScopedFeatureList scoped_feature_list_;
TestingProfileManager profile_manager_;
raw_ptr<TestingProfile> profile_;
#if !BUILDFLAG(IS_CHROMEOS)
policy::FakeBrowserDMTokenStorage fake_browser_dm_token_storage_;
#endif
};
#if BUILDFLAG(ENTERPRISE_CONTENT_ANALYSIS)
// Test to make sure that HasExtraUiToDisplay returns the right value to
// show the extra UI from opt in features like custom message, URL and bypass
// on Download.
class ConnectorsServiceHasExtraUiTest
: public ConnectorsServiceTest,
public testing::WithParamInterface<std::tuple<std::string, bool>> {
public:
std::string pref() { return std::get<0>(GetParam()); }
bool has_extra_ui() { return std::get<1>(GetParam()); }
};
TEST_P(ConnectorsServiceHasExtraUiTest, AnalysisConnectors) {
test::SetAnalysisConnector(profile_->GetPrefs(), FILE_DOWNLOADED, pref());
auto* service = ConnectorsServiceFactory::GetForBrowserContext(profile_);
bool show_extra_ui = service->HasExtraUiToDisplay(FILE_DOWNLOADED, kDlpTag);
ASSERT_EQ(show_extra_ui, has_extra_ui());
}
INSTANTIATE_TEST_SUITE_P(
,
ConnectorsServiceHasExtraUiTest,
testing::Values(
std::make_tuple(CreateCustomUIPref(kCustomMessage, kCustomUrl, true),
true),
std::make_tuple(CreateCustomUIPref(kCustomMessage, kCustomUrl, false),
true),
std::make_tuple(CreateCustomUIPref(kCustomMessage, nullptr, true),
true),
std::make_tuple(CreateCustomUIPref(kCustomMessage, nullptr, false),
true),
std::make_tuple(CreateCustomUIPref(nullptr, kCustomUrl, true), true),
std::make_tuple(CreateCustomUIPref(nullptr, kCustomUrl, false), true),
std::make_tuple(CreateCustomUIPref(nullptr, nullptr, true), true),
std::make_tuple(CreateCustomUIPref(nullptr, nullptr, false), false)));
#endif // BUILDFLAG(ENTERPRISE_CONTENT_ANALYSIS)
// Tests to make sure getting reporting settings work with both the feature flag
// and the OnSecurityEventEnterpriseConnector policy. The parameter for these
// tests is a tuple of:
//
// enum class ReportingConnector[]: array of all reporting connectors.
// bool: enable feature flag.
// int: policy value. 0: don't set, 1: set to normal, 2: set to empty.
#if BUILDFLAG(ENTERPRISE_CONTENT_ANALYSIS)
class ConnectorsServiceReportingFeatureTest
: public ConnectorsServiceTest,
public testing::WithParamInterface<const char*> {
public:
const char* pref_value() const { return GetParam(); }
const char* pref() const { return kOnSecurityEventPref; }
const char* scope_pref() const { return kOnSecurityEventScopePref; }
PrefService* pref_service() const { return profile_->GetPrefs(); }
bool reporting_enabled() const {
return pref_value() == kNormalReportingSettingsPref;
}
};
#if BUILDFLAG(IS_CHROMEOS)
TEST_P(ConnectorsServiceReportingFeatureTest,
ChromeOsManagedGuestSessionFlagSetInMgs) {
// A fake Managed Guest Session that gets destroyed at the end of the test.
chromeos::FakeManagedGuestSession fake_mgs;
if (pref_value()) {
profile_->GetPrefs()->Set(
pref(), *base::JSONReader::Read(pref_value(),
base::JSON_PARSE_CHROMIUM_EXTENSIONS));
profile_->GetPrefs()->SetInteger(scope_pref(),
policy::POLICY_SCOPE_MACHINE);
}
EXPECT_TRUE(ConnectorsServiceFactory::GetForBrowserContext(profile_)
->BuildClientMetadata(/*is_cloud=*/true)
->is_chrome_os_managed_guest_session());
// The flag is currently not included for local content scanning.
EXPECT_FALSE(ConnectorsServiceFactory::GetForBrowserContext(profile_)
->BuildClientMetadata(/*is_cloud=*/false)
->is_chrome_os_managed_guest_session());
}
TEST_P(ConnectorsServiceReportingFeatureTest,
ChromeOsManagedGuestSessionFlagNotSetInUserSession) {
if (pref_value()) {
profile_->GetPrefs()->Set(
pref(), *base::JSONReader::Read(pref_value(),
base::JSON_PARSE_CHROMIUM_EXTENSIONS));
profile_->GetPrefs()->SetInteger(scope_pref(),
policy::POLICY_SCOPE_MACHINE);
}
EXPECT_FALSE(ConnectorsServiceFactory::GetForBrowserContext(profile_)
->BuildClientMetadata(/*is_cloud=*/true)
->is_chrome_os_managed_guest_session());
EXPECT_FALSE(ConnectorsServiceFactory::GetForBrowserContext(profile_)
->BuildClientMetadata(/*is_cloud=*/false)
->is_chrome_os_managed_guest_session());
}
#endif
#if BUILDFLAG(ENTERPRISE_CONTENT_ANALYSIS)
TEST_P(ConnectorsServiceReportingFeatureTest, CheckTelemetryPolicyObserver) {
ConnectorsService* connectors_service =
ConnectorsServiceFactory::GetForBrowserContext(profile_);
ConnectorsManager* connectors_manager =
connectors_service->ConnectorsManagerForTesting();
base::test::TestFuture<void> future;
connectors_service->ObserveTelemetryReporting(future.GetRepeatingCallback());
ASSERT_FALSE(
connectors_manager->GetTelemetryObserverCallbackForTesting().is_null());
// Cache initially empty
ASSERT_TRUE(
connectors_manager->GetReportingConnectorsSettingsForTesting().empty());
// Enable browser crash event
test::SetOnSecurityEventReporting(pref_service(), true, {kBrowserCrashEvent},
{});
EXPECT_TRUE(future.WaitAndClear());
// Clear enabled events (not cached when cleared)
test::SetOnSecurityEventReporting(pref_service(), false, {}, {});
ASSERT_TRUE(
connectors_manager->GetReportingConnectorsSettingsForTesting().empty());
EXPECT_TRUE(future.WaitAndClear());
// Enable telemetry event
test::SetOnSecurityEventReporting(pref_service(), true,
{kExtensionTelemetryEvent}, {});
EXPECT_TRUE(future.WaitAndClear());
}
#endif // BUILDFLAG(ENTERPRISE_CONTENT_ANALYSIS)
INSTANTIATE_TEST_SUITE_P(,
ConnectorsServiceReportingFeatureTest,
testing::Values(nullptr,
kNormalReportingSettingsPref,
kEmptySettingsPref));
#endif // BUILDFLAG(ENTERPRISE_CONTENT_ANALYSIS)
TEST_F(ConnectorsServiceTest, RealtimeURLCheck) {
profile_->GetPrefs()->SetInteger(
kEnterpriseRealTimeUrlCheckMode,
enterprise_connectors::REAL_TIME_CHECK_FOR_MAINFRAME_ENABLED);
profile_->GetPrefs()->SetInteger(kEnterpriseRealTimeUrlCheckScope,
policy::POLICY_SCOPE_MACHINE);
auto maybe_dm_token = ConnectorsServiceFactory::GetForBrowserContext(profile_)
->GetDMTokenForRealTimeUrlCheck();
EXPECT_TRUE(maybe_dm_token.has_value());
EXPECT_EQ(kFakeDmToken, maybe_dm_token.value());
#if !BUILDFLAG(IS_CHROMEOS)
std::string identifier =
ConnectorsServiceFactory::GetForBrowserContext(profile_)
->GetRealTimeUrlCheckIdentifier();
EXPECT_EQ(identifier, kFakeDeviceId);
#endif
policy::SetDMTokenForTesting(policy::DMToken::CreateEmptyToken());
maybe_dm_token = ConnectorsServiceFactory::GetForBrowserContext(profile_)
->GetDMTokenForRealTimeUrlCheck();
ASSERT_EQ(
maybe_dm_token.error(),
ConnectorsServiceBase::NoDMTokenForRealTimeUrlCheckReason::kNoDmToken);
EXPECT_FALSE(maybe_dm_token.has_value());
#if !BUILDFLAG(IS_CHROMEOS)
identifier = ConnectorsServiceFactory::GetForBrowserContext(profile_)
->GetRealTimeUrlCheckIdentifier();
EXPECT_TRUE(identifier.empty());
#endif
}
#if BUILDFLAG(ENTERPRISE_CONTENT_ANALYSIS)
class ConnectorsServiceExemptURLsTest
: public ConnectorsServiceTest,
public testing::WithParamInterface<AnalysisConnector> {
public:
ConnectorsServiceExemptURLsTest() = default;
void SetUp() override {
profile_->GetPrefs()->Set(
AnalysisConnectorPref(connector()),
*base::JSONReader::Read(kWildcardAnalysisSettingsPref,
base::JSON_PARSE_CHROMIUM_EXTENSIONS));
profile_->GetPrefs()->SetInteger(AnalysisConnectorScopePref(connector()),
policy::POLICY_SCOPE_MACHINE);
}
AnalysisConnector connector() { return GetParam(); }
};
TEST_P(ConnectorsServiceExemptURLsTest, WebUI) {
auto* service = ConnectorsServiceFactory::GetForBrowserContext(profile_);
for (const char* url :
{"chrome://settings", "chrome://help-app/background",
"chrome://foo/bar/baz.html", "chrome://foo/bar/baz.html?param=value"}) {
auto settings = service->GetAnalysisSettings(GURL(url), connector());
ASSERT_FALSE(settings.has_value());
}
}
TEST_P(ConnectorsServiceExemptURLsTest, ThirdPartyExtensions) {
auto* service = ConnectorsServiceFactory::GetForBrowserContext(profile_);
for (const char* url :
{"chrome-extension://fake_id", "chrome-extension://fake_id/background",
"chrome-extension://fake_id/main.html",
"chrome-extension://fake_id/main.html?param=value"}) {
ASSERT_TRUE(GURL(url).is_valid());
auto settings = service->GetAnalysisSettings(GURL(url), connector());
ASSERT_TRUE(settings.has_value());
}
}
TEST_P(ConnectorsServiceExemptURLsTest, BlobAndFilesystem) {
auto* service = ConnectorsServiceFactory::GetForBrowserContext(profile_);
// Test against wildcard policy.
for (const char* url_string :
{"blob:https://foo.com", "blob:ftp://foo.com/with/path",
"filesystem:http://foo.com/with.extension",
"filesystem:http://foo.com/with/path"}) {
GURL url(url_string);
ASSERT_TRUE(url.is_valid());
ASSERT_TRUE(url.SchemeIsFileSystem() || url.SchemeIsBlob());
auto settings = service->GetAnalysisSettings(GURL(url), connector());
ASSERT_TRUE(settings.has_value());
}
// Test against a specific pattern policy to validate the correct inner URL is
// used.
profile_->GetPrefs()->Set(
AnalysisConnectorPref(connector()),
*base::JSONReader::Read(R"([
{
"service_provider": "google",
"enable": [
{"url_list": ["foo.com"], "tags": ["dlp", "malware"]}
]
}
])",
base::JSON_PARSE_CHROMIUM_EXTENSIONS));
for (const char* url_string :
{"blob:https://foo.com", "blob:ftp://foo.com/with/path",
"filesystem:http://foo.com/with.extension",
"filesystem:http://foo.com/with/path"}) {
GURL url(url_string);
ASSERT_TRUE(url.is_valid());
ASSERT_TRUE(url.SchemeIsFileSystem() || url.SchemeIsBlob());
auto settings = service->GetAnalysisSettings(GURL(url), connector());
ASSERT_TRUE(settings.has_value());
}
for (const char* url_string :
{"blob:https://notfoo.com", "blob:ftp://notfoo.com/with/path",
"filesystem:http://notfoo.com/with.extension",
"filesystem:http://notfoo.com/with/path"}) {
GURL url(url_string);
ASSERT_TRUE(url.is_valid());
ASSERT_TRUE(url.SchemeIsFileSystem() || url.SchemeIsBlob());
auto settings = service->GetAnalysisSettings(GURL(url), connector());
ASSERT_FALSE(settings.has_value());
}
}
#if BUILDFLAG(IS_CHROMEOS)
TEST_P(ConnectorsServiceExemptURLsTest, FirstPartyExtensions) {
auto* service = ConnectorsServiceFactory::GetForBrowserContext(profile_);
for (const std::string& suffix :
{"/", "/background", "/main.html", "/main.html?param=value"}) {
std::string url = base::StrCat(
{"chrome-extension://", extension_misc::kFilesManagerAppId, suffix});
auto settings = service->GetAnalysisSettings(GURL(url), connector());
ASSERT_FALSE(settings.has_value());
}
}
#endif // BUILDFLAG(IS_CHROMEOS)
INSTANTIATE_TEST_SUITE_P(
,
ConnectorsServiceExemptURLsTest,
testing::Values(FILE_ATTACHED, FILE_DOWNLOADED, BULK_DATA_ENTRY, PRINT));
#endif // BUILDFLAG(ENTERPRISE_CONTENT_ANALYSIS)
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
class ConnectorsServiceProfileTypeBrowserTest : public testing::Test {
public:
protected:
TestingProfile* regular_profile() {
return profile_testing_helper_.regular_profile();
}
Profile* incognito_profile() {
return profile_testing_helper_.incognito_profile();
}
TestingProfile* guest_profile() {
return profile_testing_helper_.guest_profile();
}
Profile* guest_profile_otr() {
return profile_testing_helper_.guest_profile_otr();
}
TestingProfile* system_profile() {
return profile_testing_helper_.system_profile();
}
Profile* system_profile_otr() {
return profile_testing_helper_.system_profile_otr();
}
std::unique_ptr<ConnectorsService> CreateService(Profile* profile) {
auto manager = std::make_unique<ConnectorsManager>(
profile->GetPrefs(), GetServiceProviderConfig(), false);
return std::make_unique<ConnectorsService>(profile, std::move(manager));
}
private:
void SetUp() override {
testing::Test::SetUp();
profile_testing_helper_.SetUp();
}
base::test::ScopedFeatureList scoped_feature_list_;
ProfileTestingHelper profile_testing_helper_;
};
TEST_F(ConnectorsServiceProfileTypeBrowserTest, IsEnabled) {
EXPECT_TRUE(CreateService(regular_profile())->ConnectorsEnabled());
EXPECT_FALSE(CreateService(incognito_profile())->ConnectorsEnabled());
EXPECT_FALSE(CreateService(guest_profile())->ConnectorsEnabled());
EXPECT_TRUE(CreateService(guest_profile_otr())->ConnectorsEnabled());
EXPECT_FALSE(CreateService(system_profile())->ConnectorsEnabled());
EXPECT_FALSE(CreateService(system_profile_otr())->ConnectorsEnabled());
}
#endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
} // namespace enterprise_connectors