// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/memory/raw_ptr.h"
#include "base/test/bind.h"
#include "base/test/gmock_callback_support.h"
#include "base/test/mock_callback.h"
#include "base/test/test_mock_time_task_runner.h"
#include "build/config/coverage/buildflags.h"
#include "chrome/browser/ash/file_manager/file_manager_browsertest_base.h"
#include "chrome/browser/ash/file_manager/file_manager_browsertest_utils.h"
#include "chrome/browser/ash/file_manager/file_manager_test_util.h"
#include "chrome/browser/ash/file_manager/path_util.h"
#include "chrome/browser/ash/policy/dlp/dialogs/files_policy_error_dialog.h"
#include "chrome/browser/ash/policy/dlp/dialogs/files_policy_warn_dialog.h"
#include "chrome/browser/ash/policy/dlp/dlp_files_controller_ash.h"
#include "chrome/browser/ash/policy/dlp/files_policy_notification_manager.h"
#include "chrome/browser/ash/policy/dlp/files_policy_notification_manager_factory.h"
#include "chrome/browser/ash/policy/dlp/test/mock_dlp_files_controller_ash.h"
#include "chrome/browser/chromeos/policy/dlp/dlp_files_utils.h"
#include "chrome/browser/chromeos/policy/dlp/dlp_policy_constants.h"
#include "chrome/browser/chromeos/policy/dlp/dlp_rules_manager.h"
#include "chrome/browser/chromeos/policy/dlp/dlp_rules_manager_factory.h"
#include "chrome/browser/chromeos/policy/dlp/test/mock_dlp_rules_manager.h"
#include "chrome/browser/enterprise/connectors/analysis/mock_file_transfer_analysis_delegate.h"
#include "chrome/browser/enterprise/connectors/reporting/realtime_reporting_client_factory.h"
#include "chrome/browser/enterprise/connectors/test/deep_scanning_test_utils.h"
#include "chrome/browser/enterprise/connectors/test/fake_content_analysis_delegate.h"
#include "chrome/browser/enterprise/connectors/test/fake_files_request_handler.h"
#include "chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.h"
#include "chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_factory.h"
#include "chrome/browser/policy/dm_token_utils.h"
#include "chromeos/dbus/dlp/dlp_client.h"
#include "chromeos/dbus/dlp/dlp_service.pb.h"
#include "components/file_access/test/mock_scoped_file_access_delegate.h"
#include "components/policy/core/common/cloud/mock_cloud_policy_client.h"
#include "components/signin/public/identity_manager/identity_test_environment.h"
#include "content/public/test/browser_test.h"
#include "ui/base/mojom/dialog_button.mojom.h"
#include "ui/views/controls/textarea/textarea.h"
namespace file_manager {
namespace {
// DLP source URLs
constexpr char kBlockedSourceUrl[] = "https://blocked.com";
constexpr char kWarnSourceUrl[] = "https://warned.com";
constexpr char kNotSetSourceUrl[] = "https://not-set.com";
constexpr char kNotBlockedSourceUrl[] = "https://allowed.com";
constexpr char16_t kUserJustification[] = u"User justification";
// Compares DLP AddFilesRequests ignoring the order of repeated fields.
MATCHER_P(EqualsAddFilesRequestsProto, add_files, "") {
::dlp::AddFilesRequest reference(add_files);
::dlp::AddFilesRequest actual(arg);
auto CompareAddFileRequest = [](const ::dlp::AddFileRequest& add1,
const ::dlp::AddFileRequest& add2) {
return std::make_tuple(add1.file_path(), add1.source_url(),
add1.referrer_url()) <
std::make_tuple(add2.file_path(), add2.source_url(),
add2.referrer_url());
};
std::sort(reference.mutable_add_file_requests()->begin(),
reference.mutable_add_file_requests()->end(),
CompareAddFileRequest);
std::sort(actual.mutable_add_file_requests()->begin(),
actual.mutable_add_file_requests()->end(), CompareAddFileRequest);
std::string expected_serialized, actual_serialized;
reference.SerializeToString(&expected_serialized);
actual.SerializeToString(&actual_serialized);
return expected_serialized == actual_serialized;
}
// Base class for DLP setup needed for browsertests.
class DlpFilesAppBrowserTestBase {
public:
DlpFilesAppBrowserTestBase(const DlpFilesAppBrowserTestBase&) = delete;
DlpFilesAppBrowserTestBase& operator=(const DlpFilesAppBrowserTestBase&) =
delete;
std::unique_ptr<KeyedService> SetDlpRulesManager(
content::BrowserContext* context) {
auto dlp_rules_manager =
std::make_unique<testing::NiceMock<policy::MockDlpRulesManager>>(
Profile::FromBrowserContext(context));
mock_rules_manager_ = dlp_rules_manager.get();
ON_CALL(*mock_rules_manager_, IsFilesPolicyEnabled)
.WillByDefault(testing::Return(true));
files_controller_ = std::make_unique<policy::DlpFilesControllerAsh>(
*mock_rules_manager_, Profile::FromBrowserContext(context));
ON_CALL(*mock_rules_manager_, GetDlpFilesController)
.WillByDefault(testing::Return(files_controller_.get()));
return dlp_rules_manager;
}
protected:
DlpFilesAppBrowserTestBase() = default;
~DlpFilesAppBrowserTestBase() = default;
bool HandleDlpCommands(Profile* profile,
const std::string& name,
const base::Value::Dict& value,
std::string* output) {
if (name == "setGetFilesSourcesMock") {
base::FilePath result =
file_manager::util::GetDownloadsFolderForProfile(profile);
const base::Value::List* file_names = value.FindList("fileNames");
auto* source_urls = value.FindList("sourceUrls");
EXPECT_TRUE(file_names);
EXPECT_TRUE(source_urls);
EXPECT_EQ(file_names->size(), source_urls->size());
::dlp::GetFilesSourcesResponse response;
for (unsigned long i = 0; i < file_names->size(); i++) {
auto* metadata = response.add_files_metadata();
metadata->set_path(result.Append((*file_names)[i].GetString()).value());
metadata->set_source_url((*source_urls)[i].GetString());
}
chromeos::DlpClient::Get()->GetTestInterface()->SetGetFilesSourceMock(
base::BindRepeating(&DlpFilesAppBrowserTestBase::GetFilesSourcesMock,
base::Unretained(this), response));
return true;
}
if (name == "setBlockedFilesTransfer") {
base::FilePath result =
file_manager::util::GetDownloadsFolderForProfile(profile);
auto* file_names = value.FindList("fileNames");
EXPECT_TRUE(file_names);
::dlp::CheckFilesTransferResponse check_files_transfer_response;
for (const auto& file_name : *file_names) {
check_files_transfer_response.add_files_paths(
result.Append(file_name.GetString()).value());
}
chromeos::DlpClient::Get()->GetTestInterface()->SetIsAlive(true);
chromeos::DlpClient::Get()
->GetTestInterface()
->SetCheckFilesTransferResponse(check_files_transfer_response);
return true;
}
if (name == "setIsRestrictedDestinationRestriction") {
EXPECT_CALL(
*mock_rules_manager_,
IsRestrictedDestination(GURL(kBlockedSourceUrl), testing::_,
policy::DlpRulesManager::Restriction::kFiles,
testing::_, testing::_, testing::_))
.WillRepeatedly(
testing::Return(policy::DlpRulesManager::Level::kBlock));
EXPECT_CALL(
*mock_rules_manager_,
IsRestrictedDestination(GURL(kNotBlockedSourceUrl), testing::_,
policy::DlpRulesManager::Restriction::kFiles,
testing::_, testing::_, testing::_))
.WillRepeatedly(
::testing::Return(policy::DlpRulesManager::Level::kAllow));
return true;
}
if (name == "setBlockedComponent") {
auto* component_str = value.FindString("component");
EXPECT_TRUE(component_str);
auto component = MapToPolicyComponent(*component_str);
EXPECT_NE(data_controls::Component::kUnknownComponent, component);
policy::DlpRulesManager::AggregatedComponents components;
components[policy::DlpRulesManager::Level::kBlock].insert(component);
EXPECT_CALL(*mock_rules_manager_, GetAggregatedComponents)
.WillOnce(testing::Return(components));
return true;
}
if (name == "setIsRestrictedByAnyRuleRestrictions") {
EXPECT_CALL(
*mock_rules_manager_,
IsRestrictedByAnyRule(GURL(kNotBlockedSourceUrl),
policy::DlpRulesManager::Restriction::kFiles,
testing::_, testing::_))
.WillRepeatedly(
testing::Return(policy::DlpRulesManager::Level::kAllow));
EXPECT_CALL(
*mock_rules_manager_,
IsRestrictedByAnyRule(GURL(kBlockedSourceUrl),
policy::DlpRulesManager::Restriction::kFiles,
testing::_, testing::_))
.WillRepeatedly(
testing::Return(policy::DlpRulesManager::Level::kBlock));
EXPECT_CALL(
*mock_rules_manager_,
IsRestrictedByAnyRule(GURL(kNotSetSourceUrl),
policy::DlpRulesManager::Restriction::kFiles,
testing::_, testing::_))
.WillRepeatedly(
testing::Return(policy::DlpRulesManager::Level::kNotSet));
EXPECT_CALL(
*mock_rules_manager_,
IsRestrictedByAnyRule(GURL(kWarnSourceUrl),
policy::DlpRulesManager::Restriction::kFiles,
testing::_, testing::_))
.WillRepeatedly(
testing::Return(policy::DlpRulesManager::Level::kWarn));
return true;
}
if (name == "setIsRestrictedByAnyRuleBlocked") {
EXPECT_CALL(*mock_rules_manager_, IsRestrictedByAnyRule)
.WillRepeatedly(
::testing::Return(policy::DlpRulesManager::Level::kBlock));
return true;
}
if (name == "setupScopedFileAccessDelegateAllowed") {
scoped_file_access_delegate_ =
std::make_unique<file_access::MockScopedFileAccessDelegate>();
EXPECT_CALL(*scoped_file_access_delegate_, RequestFilesAccessForSystem)
.WillOnce([](const std::vector<base::FilePath>& paths,
base::OnceCallback<void(file_access::ScopedFileAccess)>
callback) {
std::move(callback).Run(file_access::ScopedFileAccess::Allowed());
});
return true;
}
if (name == "expectFilesAdditionToDaemon") {
base::FilePath download_path =
file_manager::util::GetDownloadsFolderForProfile(profile);
const base::Value::List* file_names = value.FindList("fileNames");
auto* source_urls = value.FindList("sourceUrls");
EXPECT_TRUE(file_names);
EXPECT_TRUE(source_urls);
EXPECT_EQ(file_names->size(), source_urls->size());
::dlp::AddFilesRequest expected_request;
for (unsigned long i = 0; i < file_names->size(); i++) {
::dlp::AddFileRequest* file_request =
expected_request.add_add_file_requests();
file_request->set_file_path(download_path.value() + "/" +
(*file_names)[i].GetString());
file_request->set_source_url((*source_urls)[i].GetString());
}
EXPECT_CALL(add_files_cb,
Run(EqualsAddFilesRequestsProto(expected_request),
base::test::IsNotNullCallback()));
chromeos::DlpClient::Get()->GetTestInterface()->SetIsAlive(true);
chromeos::DlpClient::Get()->GetTestInterface()->SetAddFilesMock(
add_files_cb.Get());
return true;
}
if (name == "setCheckFilesTransferMockToPause") {
base::FilePath download_path =
file_manager::util::GetDownloadsFolderForProfile(profile);
std::optional<int> task_id = value.FindInt("taskId");
EXPECT_TRUE(task_id.has_value() && task_id.value() > 0);
const base::Value::List* file_names = value.FindList("fileNames");
EXPECT_TRUE(file_names);
std::vector<base::FilePath> warning_files;
for (const auto& file_name : *file_names) {
warning_files.emplace_back(download_path.value() + "/" +
file_name.GetString());
}
const std::string* action_str = value.FindString("action");
EXPECT_TRUE(action_str);
EXPECT_TRUE(*action_str == "copy" || *action_str == "move");
policy::dlp::FileAction action = *action_str == "copy"
? policy::dlp::FileAction::kCopy
: policy::dlp::FileAction::kMove;
// FPNM is created lazily, so call it here to make sure it's created and
// starts tracking the tasks.
policy::FilesPolicyNotificationManager* fpnm =
policy::FilesPolicyNotificationManagerFactory::GetForBrowserContext(
profile);
EXPECT_TRUE(fpnm);
// Might be needed to time out the warning.
fpnm->SetTaskRunnerForTesting(task_runner);
auto cb = base::BindLambdaForTesting(
[task_id, warning_files, action, profile](
const dlp::CheckFilesTransferRequest,
chromeos::DlpClient::CheckFilesTransferCallback daemon_callback) {
auto warning_callback = base::BindOnce(
[](chromeos::DlpClient::CheckFilesTransferCallback daemon_cb,
std::optional<std::u16string> justification,
bool should_proceed) {
if (should_proceed) {
std::move(daemon_cb).Run({});
}
},
std::move(daemon_callback));
policy::FilesPolicyNotificationManager* fpnm =
policy::FilesPolicyNotificationManagerFactory::
GetForBrowserContext(profile);
ASSERT_TRUE(fpnm);
ASSERT_TRUE(fpnm->HasIOTask(task_id.value()));
// Call FPNM to show the warning, which pauses the task.
fpnm->ShowDlpWarning(std::move(warning_callback), task_id,
std::move(warning_files),
policy::DlpFileDestination(), action);
});
chromeos::DlpClient::Get()->GetTestInterface()->SetIsAlive(true);
chromeos::DlpClient::Get()->GetTestInterface()->SetCheckFilesTransferMock(
cb);
return true;
}
if (name == "timeoutWarning") {
// Fast forward by 5 minutes to time out the DLP warning.
task_runner->FastForwardBy(base::Minutes(5));
return true;
}
return false;
}
// MockDlpRulesManager is owned by KeyedService and is guaranteed to outlive
// this class.
raw_ptr<policy::MockDlpRulesManager, DanglingUntriaged> mock_rules_manager_ =
nullptr;
std::unique_ptr<policy::DlpFilesControllerAsh> files_controller_;
std::unique_ptr<file_access::MockScopedFileAccessDelegate>
scoped_file_access_delegate_;
// The callback needs to survive the setup method.
base::MockRepeatingCallback<void(
const ::dlp::AddFilesRequest request,
chromeos::DlpClient::AddFilesCallback callback)>
add_files_cb;
scoped_refptr<base::TestMockTimeTaskRunner> task_runner =
base::MakeRefCounted<base::TestMockTimeTaskRunner>();
// Maps |component| to data_controls::Component.
data_controls::Component MapToPolicyComponent(const std::string& component) {
if (component == "arc") {
return data_controls::Component::kArc;
}
if (component == "crostini") {
return data_controls::Component::kCrostini;
}
if (component == "pluginVm") {
return data_controls::Component::kPluginVm;
}
if (component == "usb") {
return data_controls::Component::kUsb;
}
if (component == "drive") {
return data_controls::Component::kDrive;
}
return data_controls::Component::kUnknownComponent;
}
// Invokes `callback` with the previously constructed `response`. Note that
// the result doesn't depend on the value of `request`.
void GetFilesSourcesMock(
const dlp::GetFilesSourcesResponse response,
const dlp::GetFilesSourcesRequest request,
chromeos::DlpClient::GetFilesSourcesCallback callback) {
std::move(callback).Run(response);
}
};
// Returns a file transfer connectors policy for DLP with the given settings.
std::string GetFileTransferConnectorsPolicyForDlp(
const std::string& source,
const std::string& destination,
bool report_only,
bool require_user_justification) {
auto sources = base::Value::List().Append(
base::Value::Dict().Set("file_system_type", source));
auto destinations = base::Value::List().Append(
base::Value::Dict().Set("file_system_type", destination));
auto source_destination_list = base::Value::List().Append(
base::Value::Dict()
.Set("sources", std::move(sources))
.Set("destinations", std::move(destinations)));
auto enable = base::Value::List().Append(
base::Value::Dict()
.Set("source_destination_list", std::move(source_destination_list))
.Set("tags", base::Value::List().Append("dlp")));
auto settings = base::Value::Dict();
settings.Set("service_provider", "google");
settings.Set("enable", std::move(enable));
settings.Set("block_until_verdict", report_only ? 0 : 1);
if (require_user_justification) {
settings.Set("require_justification_tags",
base::Value::List().Append("dlp"));
}
return settings.DebugString();
}
base::TimeDelta kResponseDelay = base::Seconds(0);
const std::set<std::string>* JpgMimeTypes() {
static std::set<std::string> set = {"image/jpeg"};
return &set;
}
// Base class for Enterprise connectrs setup needed for browsertests.
class FileTransferConnectorFilesAppBrowserTestBase {
public:
FileTransferConnectorFilesAppBrowserTestBase(
const FileTransferConnectorFilesAppBrowserTestBase&) = delete;
FileTransferConnectorFilesAppBrowserTestBase& operator=(
const FileTransferConnectorFilesAppBrowserTestBase&) = delete;
protected:
FileTransferConnectorFilesAppBrowserTestBase() = default;
~FileTransferConnectorFilesAppBrowserTestBase() = default;
void SetUpOnMainThread(Profile* profile) {
// Set a device management token. It is required to enable scanning.
// Without it, FileTransferAnalysisDelegate::IsEnabled() always
// returns std::nullopt.
SetDMTokenForTesting(policy::DMToken::CreateValidToken("dm_token"));
// Enable reporting.
enterprise_connectors::test::SetOnSecurityEventReporting(
profile->GetPrefs(),
/*enabled*/ true,
/*enabled_event_names*/ {},
/*enabled_opt_in_events*/ {},
/*machine_scope*/ false);
// Add mock to check reports.
cloud_policy_client_ = std::make_unique<policy::MockCloudPolicyClient>();
cloud_policy_client_->SetDMToken("dm_token");
enterprise_connectors::RealtimeReportingClientFactory::GetForProfile(
profile)
->SetBrowserCloudPolicyClientForTesting(cloud_policy_client_.get());
// Add IdentityTestEnvironment to verify user name.
identity_test_environment_ =
std::make_unique<signin::IdentityTestEnvironment>();
identity_test_environment_->MakePrimaryAccountAvailable(
kUserName, signin::ConsentLevel::kSync);
enterprise_connectors::RealtimeReportingClientFactory::GetForProfile(
profile)
->SetIdentityManagerForTesting(
identity_test_environment_->identity_manager());
}
std::string GetScanIDForFileName(std::string file_name) {
return std::string(kScanId) + file_name;
}
void ScanningHasCompletedCallback() {
DCHECK(run_loop_)
<< "run loop not configured, missing call to `setupScanningRunLoop`";
++finished_file_transfer_analysis_delegates_;
DCHECK_LE(finished_file_transfer_analysis_delegates_,
expected_number_of_file_transfer_analysis_delegates_);
if (finished_file_transfer_analysis_delegates_ ==
expected_number_of_file_transfer_analysis_delegates_) {
// If all FileTransferAnalysisDelegates finished, scanning has been
// completed.
run_loop_->QuitClosure().Run();
}
}
// Setup the expectations of the mock.
// This function uses the stored expectations from the
// `scanning_expectations_` map.
void SetupMock(
enterprise_connectors::MockFileTransferAnalysisDelegate* delegate) {
// Expect one call to UploadData.
EXPECT_CALL(*delegate, UploadData(::testing::_))
.WillOnce([this, delegate](base::OnceClosure callback) {
// When scanning is started, start the normal scan.
// We modify the callback such that in addition to the normal callback
// we also call `ScanningHasCompletedCallback()` to notify the test
// that scanning has completed.
delegate->FileTransferAnalysisDelegate::UploadData(base::BindOnce(
[](base::OnceClosure callback,
base::OnceClosure scanning_has_completed_callback) {
// Call the callback
std::move(callback).Run();
// Notify that scanning of this delegate has completed.
std::move(scanning_has_completed_callback).Run();
},
std::move(callback),
base::BindOnce(&FileTransferConnectorFilesAppBrowserTestBase::
ScanningHasCompletedCallback,
base::Unretained(this))));
});
// Call GetWarnedFiles from the base class.
EXPECT_CALL(*delegate, GetWarnedFiles()).WillRepeatedly([delegate]() {
return delegate->FileTransferAnalysisDelegate::GetWarnedFiles();
});
// Call GetAnalysisResultAfterScan from the base class.
EXPECT_CALL(*delegate, GetAnalysisResultAfterScan(::testing::_))
.WillRepeatedly([delegate](storage::FileSystemURL url) {
return delegate
->FileTransferAnalysisDelegate::GetAnalysisResultAfterScan(url);
});
}
bool HandleEnterpriseConnectorCommands(
Profile* profile,
const FileManagerBrowserTestBase::Options& options,
const std::string& name,
const base::Value::Dict& value,
std::string* output) {
if (name == "setupFileTransferPolicy") {
// Set the analysis connector (enterprise_connectors) for FILE_TRANSFER.
// It is also required for FileTransferAnalysisDelegate::IsEnabled() to
// return a meaningful result.
const std::string* source = value.FindString("source");
CHECK(source);
const std::string* destination = value.FindString("destination");
CHECK(destination);
LOG(INFO) << "Setting file transfer policy for transfers from " << *source
<< " to " << *destination;
enterprise_connectors::test::SetAnalysisConnector(
profile->GetPrefs(), enterprise_connectors::FILE_TRANSFER,
GetFileTransferConnectorsPolicyForDlp(
*source, *destination,
options.file_transfer_connector_report_only,
options.bypass_requires_justification));
// Create a FakeFilesRequestHandler that intercepts uploads and fakes
// responses.
enterprise_connectors::FilesRequestHandler::SetFactoryForTesting(
base::BindRepeating(
&enterprise_connectors::test::FakeFilesRequestHandler::Create,
base::BindRepeating(
&FileTransferConnectorFilesAppBrowserTestBase::
FakeFileUploadCallback,
base::Unretained(this), *source, *destination)));
// Setup FileTransferAnalysisDelegate mock.
enterprise_connectors::FileTransferAnalysisDelegate::SetFactorForTesting(
base::BindRepeating(
[](base::RepeatingCallback<void(
enterprise_connectors::MockFileTransferAnalysisDelegate*)>
mock_setup_callback,
safe_browsing::DeepScanAccessPoint access_point,
storage::FileSystemURL source_url,
storage::FileSystemURL destination_url, Profile* profile,
storage::FileSystemContext* file_system_context,
enterprise_connectors::AnalysisSettings settings)
-> std::unique_ptr<
enterprise_connectors::FileTransferAnalysisDelegate> {
auto delegate = std::make_unique<::testing::StrictMock<
enterprise_connectors::MockFileTransferAnalysisDelegate>>(
access_point, source_url, destination_url, profile,
file_system_context, std::move(settings));
mock_setup_callback.Run(delegate.get());
return delegate;
},
base::BindRepeating(
&FileTransferConnectorFilesAppBrowserTestBase::SetupMock,
base::Unretained(this))));
return true;
}
if (name == "issueFileTransferResponses") {
// Issue all saved responses and issue all future responses directly.
IssueResponses();
return true;
}
if (name == "isReportOnlyFileTransferConnector") {
*output = options.file_transfer_connector_report_only ? "true" : "false";
return true;
}
if (name == "usesNewFileTransferConnectorUI") {
*output =
options.enable_file_transfer_connector_new_ux ? "true" : "false";
return true;
}
if (name == "getExpectedNumberOfBlockedFilesByConnectors") {
*output = base::NumberToString(expected_blocked_files_.size());
return true;
}
if (name == "getExpectedNumberOfWarnedFilesByConnectors") {
*output = base::NumberToString(expected_warned_files_.size());
return true;
}
if (name == "doesBypassRequireJustification") {
*output = options.bypass_requires_justification ? "true" : "false";
return true;
}
if (name == "setupScanningRunLoop") {
// Set the number of expected `FileTransferAnalysisDelegate`s. This is
// done to correctly notify when scanning has completed.
auto maybe_int = value.FindInt("number_of_expected_delegates");
DCHECK(maybe_int.has_value());
expected_number_of_file_transfer_analysis_delegates_ = maybe_int.value();
DCHECK(!run_loop_);
run_loop_ = std::make_unique<base::RunLoop>();
return true;
}
if (name == "waitForFileTransferScanningToComplete") {
DCHECK(run_loop_);
// Wait until the scanning is complete.
run_loop_->Run();
return true;
}
if (name == "expectFileTransferReports") {
// Setup expectations for the deep scan reports.
const std::string* source_volume_name = value.FindString("source_volume");
CHECK(source_volume_name);
const std::string* destination_volume_name =
value.FindString("destination_volume");
CHECK(destination_volume_name);
const base::Value::List* entry_paths = value.FindList("entry_paths");
CHECK(entry_paths);
std::optional<bool> expect_proceed_warning_reports_optional =
value.FindBool("expect_proceed_warning_reports");
bool expect_proceed_warning_reports =
expect_proceed_warning_reports_optional.value_or(false);
std::vector<std::string> file_names;
std::vector<std::string> shas;
std::vector<enterprise_connectors::ContentAnalysisResponse::Result>
expected_dlp_verdicts;
std::vector<std::string> expected_results;
std::vector<std::string> expected_scan_ids;
for (const auto& path_value : *entry_paths) {
const std::string* path_str = path_value.GetIfString();
CHECK(path_str);
base::FilePath path(*path_str);
auto file_name = path.BaseName().AsUTF8Unsafe();
bool should_block = base::Contains(file_name, "blocked");
bool should_warn = base::Contains(file_name, "warned");
CHECK(!(should_block && should_warn))
<< "A file shouldn't be both blocked and warned.";
if (!should_block && !should_warn) {
// If a file name contains neither blocked nor warned, expect no
// report.
continue;
}
if (expect_proceed_warning_reports && !should_warn) {
// If we are expecting proceed warning reports, then we can ignore
// blocked files.
continue;
}
file_names.push_back(file_name);
// sha256sum chrome/test/data/chromeos/file_manager/small.jpg | tr
// '[:lower:]' '[:upper:]'
shas.push_back(
"28F5754447BBA26238B93B820DFFCB6743876F8A82077BA1ABB0F4B2529AE5BE");
// Get the expected verdict from the ConnectorStatusCallback.
expected_dlp_verdicts.push_back(
ConnectorStatusCallback(path).results()[0]);
if (!expect_proceed_warning_reports) {
if (should_block) {
expected_blocked_files_.push_back(file_name);
} else if (should_warn) {
expected_warned_files_.push_back(file_name);
}
}
// For report-only mode, the transfer is always allowed. It's blocked,
// otherwise.
expected_results.push_back(safe_browsing::EventResultToString(
options.file_transfer_connector_report_only
? safe_browsing::EventResult::ALLOWED
: (should_warn ? (expect_proceed_warning_reports
? safe_browsing::EventResult::BYPASSED
: safe_browsing::EventResult::WARNED)
: safe_browsing::EventResult::BLOCKED)));
expected_scan_ids.push_back(GetScanIDForFileName(file_name));
}
validator_ =
std::make_unique<enterprise_connectors::test::EventReportValidator>(
cloud_policy_client());
validator_->ExpectSensitiveDataEvents(
/*url*/ "",
/*tab_url*/ "",
/*source*/ *source_volume_name,
/*destination*/ *destination_volume_name,
/*filenames*/ file_names,
/*sha*/
shas,
/*trigger*/
extensions::SafeBrowsingPrivateEventRouter::kTriggerFileTransfer,
/*dlp_verdict*/ expected_dlp_verdicts,
/*mimetype*/ JpgMimeTypes(),
/*size*/ 886,
/*result*/
expected_results,
/*username*/ kUserName,
/*profile_identifier*/ profile->GetPath().AsUTF8Unsafe(),
/*scan_ids*/ expected_scan_ids,
/*content_transfer_method*/ std::nullopt,
/*user_justification*/
expect_proceed_warning_reports &&
options.bypass_requires_justification
? std::make_optional(kUserJustification)
: std::nullopt);
return true;
}
return false;
}
// Upload callback to issue responses.
void FakeFileUploadCallback(
const std::string& expected_source,
const std::string& expected_destination,
safe_browsing::BinaryUploadService::Result result,
const base::FilePath& path,
std::unique_ptr<safe_browsing::BinaryUploadService::Request> request,
enterprise_connectors::test::FakeFilesRequestHandler::
FakeFileRequestCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
EXPECT_FALSE(path.empty());
EXPECT_EQ(request->device_token(), "dm_token");
// Verify source and destination of the request.
EXPECT_EQ(request->content_analysis_request().request_data().source(),
expected_source);
EXPECT_EQ(request->content_analysis_request().request_data().destination(),
expected_destination);
// Simulate a response.
base::OnceClosure response =
base::BindOnce(std::move(callback), path,
safe_browsing::BinaryUploadService::Result::SUCCESS,
ConnectorStatusCallback(path));
if (save_response_for_later_) {
// We save the responses for later such that we can check the scanning
// label.
// `await sendTestMessage({name: 'issueFileTransferResponses'})` is
// required from the test to issue the requests.
saved_responses_.push_back(std::move(response));
} else {
base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE, std::move(response), kResponseDelay);
}
}
// Issues the saved responses and sets `save_response_for_later_` to `false`.
// After this method is called, no more responses will be saved. Instead, the
// responses will be issued directly.
void IssueResponses() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
save_response_for_later_ = false;
for (auto&& response : saved_responses_) {
base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE, std::move(response), kResponseDelay);
}
saved_responses_.clear();
}
enterprise_connectors::ContentAnalysisResponse ConnectorStatusCallback(
const base::FilePath& path) {
enterprise_connectors::ContentAnalysisResponse response;
// We return a block verdict if the basename contains "blocked".
if (base::Contains(path.BaseName().value(), "blocked")) {
response = enterprise_connectors::test::FakeContentAnalysisDelegate::
FakeContentAnalysisDelegate::DlpResponse(
enterprise_connectors::ContentAnalysisResponse::Result::SUCCESS,
"rule", enterprise_connectors::TriggeredRule::BLOCK);
} else if (base::Contains(path.BaseName().value(), "warned")) {
response = enterprise_connectors::test::FakeContentAnalysisDelegate::
FakeContentAnalysisDelegate::DlpResponse(
enterprise_connectors::ContentAnalysisResponse::Result::SUCCESS,
"rule", enterprise_connectors::TriggeredRule::WARN);
} else {
response = enterprise_connectors::test::FakeContentAnalysisDelegate::
SuccessfulResponse({"dlp"});
}
response.set_request_token(
GetScanIDForFileName(path.BaseName().AsUTF8Unsafe()));
return response;
}
policy::MockCloudPolicyClient* cloud_policy_client() {
return cloud_policy_client_.get();
}
// Used to test reporting.
std::unique_ptr<policy::MockCloudPolicyClient> cloud_policy_client_;
std::unique_ptr<signin::IdentityTestEnvironment> identity_test_environment_;
std::unique_ptr<enterprise_connectors::test::EventReportValidator> validator_;
static constexpr char kUserName[] = "[email protected]";
static constexpr char kScanId[] = "scan id";
// The saved scanning responses.
std::vector<base::OnceClosure> saved_responses_;
// Determines whether a current scanning response should be saved for later or
// issued directly.
bool save_response_for_later_ = true;
size_t finished_file_transfer_analysis_delegates_ = 0;
size_t expected_number_of_file_transfer_analysis_delegates_ = 0;
std::vector<std::string> expected_blocked_files_;
std::vector<std::string> expected_warned_files_;
std::unique_ptr<base::RunLoop> run_loop_;
};
} // namespace
// A version of FilesAppBrowserTest that supports DLP files restrictions.
class DlpFilesAppBrowserTest
: public FileManagerBrowserTestBase,
public ::testing::WithParamInterface<file_manager::test::TestCase>,
public DlpFilesAppBrowserTestBase {
public:
DlpFilesAppBrowserTest(const DlpFilesAppBrowserTest&) = delete;
DlpFilesAppBrowserTest& operator=(const DlpFilesAppBrowserTest&) = delete;
protected:
DlpFilesAppBrowserTest() = default;
~DlpFilesAppBrowserTest() override = default;
void SetUpOnMainThread() override {
FileManagerBrowserTestBase::SetUpOnMainThread();
policy::DlpRulesManagerFactory::GetInstance()->SetTestingFactory(
profile(),
base::BindRepeating(&DlpFilesAppBrowserTestBase::SetDlpRulesManager,
base::Unretained(this)));
}
void TearDownOnMainThread() override {
// Make sure the rules manager does not return a freed files controller.
ON_CALL(*mock_rules_manager_, GetDlpFilesController)
.WillByDefault(testing::Return(nullptr));
// The files controller must be destroyed before the profile since it's
// holding a pointer to it.
files_controller_.reset();
FileManagerBrowserTestBase::TearDownOnMainThread();
}
bool HandleDlpCommands(const std::string& name,
const base::Value::Dict& value,
std::string* output) override {
return DlpFilesAppBrowserTestBase::HandleDlpCommands(profile(), name, value,
output);
}
const char* GetTestCaseName() const override { return GetParam().name; }
std::string GetFullTestCaseName() const override {
return GetParam().GetFullName();
}
const char* GetTestExtensionManifestName() const override {
return "file_manager_test_manifest.json";
}
FileManagerBrowserTestBase::Options GetOptions() const override {
return GetParam().options;
}
};
IN_PROC_BROWSER_TEST_P(DlpFilesAppBrowserTest, Test) {
ASSERT_TRUE(policy::DlpRulesManagerFactory::GetForPrimaryProfile());
ON_CALL(*mock_rules_manager_, IsRestricted)
.WillByDefault(::testing::Return(policy::DlpRulesManager::Level::kAllow));
ON_CALL(*mock_rules_manager_, GetReportingManager)
.WillByDefault(::testing::Return(nullptr));
StartTest();
}
// A version of FilesAppBrowserTest that supports the file transfer enterprise
// connector.
class FileTransferConnectorFilesAppBrowserTest
: public FileManagerBrowserTestBase,
public ::testing::WithParamInterface<file_manager::test::TestCase>,
public FileTransferConnectorFilesAppBrowserTestBase {
public:
FileTransferConnectorFilesAppBrowserTest(
const FileTransferConnectorFilesAppBrowserTest&) = delete;
FileTransferConnectorFilesAppBrowserTest& operator=(
const FileTransferConnectorFilesAppBrowserTest&) = delete;
protected:
FileTransferConnectorFilesAppBrowserTest() = default;
~FileTransferConnectorFilesAppBrowserTest() override = default;
void SetUpOnMainThread() override {
FileManagerBrowserTestBase::SetUpOnMainThread();
FileTransferConnectorFilesAppBrowserTestBase::SetUpOnMainThread(profile());
}
bool HandleEnterpriseConnectorCommands(const std::string& name,
const base::Value::Dict& value,
std::string* output) override {
if (name == "verifyFileTransferErrorDialogAndDismiss") {
const std::string* app_id = value.FindString("app_id");
CHECK_NE(app_id, nullptr);
VerifyFileTransferErrorDialogAndDismiss(*app_id);
return true;
}
if (name == "verifyFileTransferWarningDialogAndProceed") {
const std::string* app_id = value.FindString("app_id");
CHECK_NE(app_id, nullptr);
VerifyFileTransferWarningDialogAndProceed(
*app_id, GetOptions().bypass_requires_justification);
return true;
} else {
return FileTransferConnectorFilesAppBrowserTestBase::
HandleEnterpriseConnectorCommands(profile(), GetOptions(), name,
value, output);
}
}
const char* GetTestCaseName() const override { return GetParam().name; }
std::string GetFullTestCaseName() const override {
return GetParam().GetFullName();
}
const char* GetTestExtensionManifestName() const override {
return "file_manager_test_manifest.json";
}
FileManagerBrowserTestBase::Options GetOptions() const override {
return GetParam().options;
}
void VerifyFileTransferErrorDialogAndDismiss(const std::string& app_id) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
content::WebContents* web_contents = GetWebContentsForId(app_id);
CHECK_NE(web_contents, nullptr);
gfx::NativeWindow native_window = web_contents->GetTopLevelNativeWindow();
std::set<raw_ptr<views::Widget, SetExperimental>> owned_widgets;
views::Widget::GetAllOwnedWidgets(native_window, &owned_widgets);
// Verify that the FilesPolicyErrorDialog widget is displayed.
ASSERT_EQ(owned_widgets.size(), 1ul);
auto* widget = (*owned_widgets.begin()).get();
ASSERT_EQ(widget->GetName(), "FilesPolicyErrorDialog");
auto* view = widget->GetRootView()->GetViewByID(
policy::PolicyDialogBase::kScrollViewId);
ASSERT_TRUE(view);
// Verify the displayed blocked files shown in the dialog.
std::vector<std::string> displayed_files;
for (const views::View* row_view : view->children()) {
const views::Label* label =
static_cast<const views::Label*>(row_view->GetViewByID(
policy::PolicyDialogBase::kConfidentialRowTitleViewId));
if (label) {
displayed_files.push_back(base::UTF16ToUTF8(label->GetText()));
}
}
EXPECT_THAT(displayed_files,
::testing::UnorderedElementsAreArray(expected_blocked_files_));
// Close the dialog.
auto* dialog = static_cast<policy::FilesPolicyErrorDialog*>(
widget->widget_delegate()->AsDialogDelegate());
dialog->AcceptDialog();
// Verify that the dialog is closed.
EXPECT_TRUE(widget->IsClosed());
}
void VerifyFileTransferWarningDialogAndProceed(
const std::string& app_id,
bool bypass_requires_justification) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
content::WebContents* web_contents = GetWebContentsForId(app_id);
CHECK_NE(web_contents, nullptr);
gfx::NativeWindow native_window = web_contents->GetTopLevelNativeWindow();
std::set<raw_ptr<views::Widget, SetExperimental>> owned_widgets;
views::Widget::GetAllOwnedWidgets(native_window, &owned_widgets);
// Verify that the FilesPolicyWarnDialog widget is displayed.
ASSERT_EQ(owned_widgets.size(), 1ul);
auto* widget = (*owned_widgets.begin()).get();
ASSERT_EQ(widget->GetName(), "FilesPolicyWarnDialog");
auto* view = widget->GetRootView()->GetViewByID(
policy::PolicyDialogBase::kScrollViewId);
ASSERT_TRUE(view);
// Verify the displayed blocked files shown in the dialog.
std::vector<std::string> displayed_files;
for (const views::View* row_view : view->children()) {
const views::Label* label =
static_cast<const views::Label*>(row_view->GetViewByID(
policy::PolicyDialogBase::kConfidentialRowTitleViewId));
if (label) {
displayed_files.push_back(base::UTF16ToUTF8(label->GetText()));
}
}
EXPECT_THAT(displayed_files,
::testing::UnorderedElementsAreArray(expected_warned_files_));
policy::FilesPolicyWarnDialog* dialog =
static_cast<policy::FilesPolicyWarnDialog*>(
widget->widget_delegate()->AsDialogDelegate());
ASSERT_TRUE(dialog);
// Verify that the dialog has a text area where the user can enter a
// justification if required.
views::Textarea* justification_area =
static_cast<views::Textarea*>(widget->GetRootView()->GetViewByID(
policy::PolicyDialogBase::
kEnterpriseConnectorsJustificationTextareaId));
if (bypass_requires_justification) {
EXPECT_NE(justification_area, nullptr);
EXPECT_FALSE(dialog->IsDialogButtonEnabled(ui::mojom::DialogButton::kOk));
justification_area->InsertText(
kUserJustification,
ui::TextInputClient::InsertTextCursorBehavior::kMoveCursorAfterText);
EXPECT_TRUE(dialog->IsDialogButtonEnabled(ui::mojom::DialogButton::kOk));
} else {
EXPECT_EQ(justification_area, nullptr);
EXPECT_TRUE(dialog->IsDialogButtonEnabled(ui::mojom::DialogButton::kOk));
}
// Close the dialog.
dialog->AcceptDialog();
// Verify that the dialog is closed.
EXPECT_TRUE(widget->IsClosed());
}
};
IN_PROC_BROWSER_TEST_P(FileTransferConnectorFilesAppBrowserTest, Test) {
StartTest();
}
// A version of FilesAppBrowserTest that supports DLP and Enterprise Connectors
// files restrictions.
class DlpAndEnterpriseConnectorsFilesAppBrowserTest
: public FileManagerBrowserTestBase,
public ::testing::WithParamInterface<file_manager::test::TestCase>,
public DlpFilesAppBrowserTestBase,
public FileTransferConnectorFilesAppBrowserTestBase {
public:
DlpAndEnterpriseConnectorsFilesAppBrowserTest(
const DlpAndEnterpriseConnectorsFilesAppBrowserTest&) = delete;
DlpAndEnterpriseConnectorsFilesAppBrowserTest& operator=(
const DlpAndEnterpriseConnectorsFilesAppBrowserTest&) = delete;
protected:
DlpAndEnterpriseConnectorsFilesAppBrowserTest() = default;
~DlpAndEnterpriseConnectorsFilesAppBrowserTest() override = default;
void SetUpOnMainThread() override {
FileManagerBrowserTestBase::SetUpOnMainThread();
policy::DlpRulesManagerFactory::GetInstance()->SetTestingFactory(
profile(),
base::BindRepeating(&DlpFilesAppBrowserTestBase::SetDlpRulesManager,
base::Unretained(this)));
FileTransferConnectorFilesAppBrowserTestBase::SetUpOnMainThread(profile());
}
void TearDownOnMainThread() override {
// The files controller must be destroyed before the profile since it's
// holding a pointer to it.
files_controller_.reset();
FileManagerBrowserTestBase::TearDownOnMainThread();
}
bool HandleDlpCommands(const std::string& name,
const base::Value::Dict& value,
std::string* output) override {
return DlpFilesAppBrowserTestBase::HandleDlpCommands(profile(), name, value,
output);
}
bool HandleEnterpriseConnectorCommands(const std::string& name,
const base::Value::Dict& value,
std::string* output) override {
return FileTransferConnectorFilesAppBrowserTestBase::
HandleEnterpriseConnectorCommands(profile(), GetOptions(), name, value,
output);
}
const char* GetTestCaseName() const override { return GetParam().name; }
std::string GetFullTestCaseName() const override {
return GetParam().GetFullName();
}
const char* GetTestExtensionManifestName() const override {
return "file_manager_test_manifest.json";
}
FileManagerBrowserTestBase::Options GetOptions() const override {
return GetParam().options;
}
};
IN_PROC_BROWSER_TEST_P(DlpAndEnterpriseConnectorsFilesAppBrowserTest, Test) {
ASSERT_TRUE(policy::DlpRulesManagerFactory::GetForPrimaryProfile());
ON_CALL(*mock_rules_manager_, IsRestricted)
.WillByDefault(::testing::Return(policy::DlpRulesManager::Level::kAllow));
ON_CALL(*mock_rules_manager_, GetReportingManager)
.WillByDefault(::testing::Return(nullptr));
StartTest();
}
WRAPPED_INSTANTIATE_TEST_SUITE_P(
DLP, /* dlp.ts */
DlpFilesAppBrowserTest,
::testing::Values(
file_manager::test::TestCase("transferShowDlpToast").EnableDlp(),
file_manager::test::TestCase("dlpShowManagedIcon").EnableDlp(),
file_manager::test::TestCase("dlpContextMenuRestrictionDetails")
.EnableDlp(),
file_manager::test::TestCase("saveAsDlpRestrictedAndroid")
.EnableArcVm()
.EnableDlp(),
file_manager::test::TestCase("saveAsDlpRestrictedCrostini").EnableDlp(),
file_manager::test::TestCase("saveAsDlpRestrictedVm").EnableDlp(),
file_manager::test::TestCase("saveAsDlpRestrictedUsb").EnableDlp(),
file_manager::test::TestCase("saveAsDlpRestrictedDrive").EnableDlp(),
file_manager::test::TestCase("saveAsNonDlpRestricted").EnableDlp(),
file_manager::test::TestCase("saveAsDlpRestrictedRedirectsToMyFiles")
.EnableDlp(),
file_manager::test::TestCase("openDlpRestrictedFile").EnableDlp(),
// TODO(b/290329625): Enable this once we identify a way to collect coverage
// when windows are closed before the test finishes.
#if !BUILDFLAG(USE_JAVASCRIPT_COVERAGE)
file_manager::test::TestCase("openFolderDlpRestricted").EnableDlp(),
#endif
file_manager::test::TestCase("fileTasksDlpRestricted").EnableDlp(),
file_manager::test::TestCase("zipExtractRestrictedArchiveCheckContent")
.EnableDlp(),
file_manager::test::TestCase("blockShowsPanelItem")
.EnableDlp()
.EnableFilesPolicyNewUX(),
file_manager::test::TestCase("warnShowsPanelItem")
.EnableDlp()
.EnableFilesPolicyNewUX(),
file_manager::test::TestCase("warnTimeoutShowsPanelItem")
.EnableDlp()
.EnableFilesPolicyNewUX(),
file_manager::test::TestCase("mixedSummaryDisplayPanel")
.EnableDlp()
.EnableFilesPolicyNewUX()));
#define FILE_TRANSFER_TEST_CASE(name) \
file_manager::test::TestCase(name).EnableFileTransferConnector()
// Enable both new policy UX and file transfer connector new UX, as the latter
// requires the former.
#define FILE_TRANSFER_TEST_CASE_NEW_UX(name) \
file_manager::test::TestCase(name) \
.EnableFileTransferConnector() \
.EnableFilesPolicyNewUX() \
.EnableFileTransferConnectorNewUX()
WRAPPED_INSTANTIATE_TEST_SUITE_P(
FileTransferConnector, /* file_transfer_connector.ts */
FileTransferConnectorFilesAppBrowserTest,
::testing::Values(
FILE_TRANSFER_TEST_CASE(
"transferConnectorFromAndroidFilesToDownloadsDeep"),
FILE_TRANSFER_TEST_CASE(
"transferConnectorFromAndroidFilesToDownloadsFlat"),
FILE_TRANSFER_TEST_CASE("transferConnectorFromCrostiniToDownloadsDeep"),
FILE_TRANSFER_TEST_CASE("transferConnectorFromCrostiniToDownloadsFlat"),
FILE_TRANSFER_TEST_CASE("transferConnectorFromDriveToDownloadsDeep"),
FILE_TRANSFER_TEST_CASE("transferConnectorFromDriveToDownloadsDeep")
.FileTransferConnectorReportOnlyMode(),
FILE_TRANSFER_TEST_CASE("transferConnectorFromDriveToDownloadsFlat"),
FILE_TRANSFER_TEST_CASE("transferConnectorFromDriveToDownloadsFlat")
.FileTransferConnectorReportOnlyMode(),
FILE_TRANSFER_TEST_CASE("transferConnectorFromDriveToDownloadsFlatDesti"
"nationNoSpaceForReportOnly")
.FileTransferConnectorReportOnlyMode(),
FILE_TRANSFER_TEST_CASE(
"transferConnectorFromDriveToDownloadsMoveDeep"),
FILE_TRANSFER_TEST_CASE("transferConnectorFromDriveToDownloadsMoveDeep")
.FileTransferConnectorReportOnlyMode(),
FILE_TRANSFER_TEST_CASE(
"transferConnectorFromDriveToDownloadsMoveFlat"),
FILE_TRANSFER_TEST_CASE("transferConnectorFromDriveToDownloadsMoveFlat")
.FileTransferConnectorReportOnlyMode(),
FILE_TRANSFER_TEST_CASE("transferConnectorFromMtpToDownloadsDeep"),
FILE_TRANSFER_TEST_CASE("transferConnectorFromMtpToDownloadsFlat"),
FILE_TRANSFER_TEST_CASE("transferConnectorFromSmbfsToDownloadsDeep"),
FILE_TRANSFER_TEST_CASE("transferConnectorFromSmbfsToDownloadsFlat"),
FILE_TRANSFER_TEST_CASE("transferConnectorFromUsbToDownloadsDeep"),
FILE_TRANSFER_TEST_CASE("transferConnectorFromUsbToDownloadsFlat"),
FILE_TRANSFER_TEST_CASE_NEW_UX(
"transferConnectorFromUsbToDownloadsDeepNewUX"),
FILE_TRANSFER_TEST_CASE_NEW_UX(
"transferConnectorFromUsbToDownloadsFlatNewUX"),
FILE_TRANSFER_TEST_CASE_NEW_UX(
"transferConnectorFromUsbToDownloadsDeepMoveNewUX"),
FILE_TRANSFER_TEST_CASE_NEW_UX(
"transferConnectorFromUsbToDownloadsFlatMoveNewUX"),
FILE_TRANSFER_TEST_CASE_NEW_UX(
"transferConnectorFromUsbToDownloadsFlatWarnProceedNewUX"),
FILE_TRANSFER_TEST_CASE_NEW_UX("transferConnectorFromUsbToDownloadsFlat"
"WarnProceedWithJustificationNewUX")
.BypassRequiresJustification(),
FILE_TRANSFER_TEST_CASE_NEW_UX(
"transferConnectorFromUsbToDownloadsDeepWarnProceedNewUX"),
FILE_TRANSFER_TEST_CASE_NEW_UX("transferConnectorFromUsbToDownloadsDeep"
"WarnProceedWithJustificationNewUX")
.BypassRequiresJustification(),
FILE_TRANSFER_TEST_CASE_NEW_UX(
"transferConnectorFromUsbToDownloadsFlatWarnCancelNewUX"),
FILE_TRANSFER_TEST_CASE_NEW_UX(
"transferConnectorFromUsbToDownloadsDeepWarnCancelNewUX")));
WRAPPED_INSTANTIATE_TEST_SUITE_P(
DlpEntrepriseConnectors, /* dlp_enterprise_connectors.ts */
DlpAndEnterpriseConnectorsFilesAppBrowserTest,
::testing::Values(
FILE_TRANSFER_TEST_CASE_NEW_UX("twoWarningsProceeded"),
FILE_TRANSFER_TEST_CASE_NEW_UX("differentBlockPolicies")));
#undef FILE_TRANSFER_TEST_CASE
#undef FILE_TRANSFER_TEST_CASE_NEW_UX
} // namespace file_manager