// Copyright 2022 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/analysis/file_transfer_analysis_delegate.h"

#include <map>
#include <optional>
#include <set>
#include <string>
#include <vector>

#include "base/containers/span.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/path_service.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_future.h"
#include "chrome/browser/enterprise/connectors/analysis/files_request_handler.h"
#include "chrome/browser/enterprise/connectors/analysis/source_destination_test_util.h"
#include "chrome/browser/enterprise/connectors/common.h"
#include "chrome/browser/enterprise/connectors/connectors_service.h"
#include "chrome/browser/enterprise/connectors/reporting/realtime_reporting_client.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/policy/dm_token_utils.h"
#include "chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils.h"
#include "chrome/browser/safe_browsing/test_extension_event_observer.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile.h"
#include "chrome/test/base/testing_profile_manager.h"
#include "components/enterprise/common/proto/connectors.pb.h"
#include "components/enterprise/common/proto/synced/browser_events.pb.h"
#include "components/enterprise/connectors/core/cloud_content_scanning/common.h"
#include "components/enterprise/connectors/core/common.h"
#include "components/enterprise/connectors/core/reporting_constants.h"
#include "components/policy/core/common/cloud/mock_cloud_policy_client.h"
#include "components/policy/core/common/cloud/realtime_reporting_job_configuration.h"
#include "components/prefs/testing_pref_service.h"
#include "components/signin/public/identity_manager/identity_test_environment.h"
#include "content/public/test/browser_task_environment.h"
#include "content/public/test/test_utils.h"
#include "storage/browser/file_system/file_system_context.h"
#include "storage/browser/file_system/file_system_url.h"
#include "storage/browser/quota/quota_manager_proxy.h"
#include "storage/browser/test/test_file_system_context.h"
#include "storage/common/file_system/file_system_types.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace enterprise_connectors {

namespace {

constexpr char kUserName[] = "test@chromium.org";

constexpr char kDmToken[] = "dm_token";

constexpr char16_t kUserJustification[] = u"User justification";

base::TimeDelta kResponseDelay = base::Seconds(0);

storage::FileSystemURL GetEmptyTestSrcUrl() {
  return storage::FileSystemURL();
}
storage::FileSystemURL GetEmptyTestDestUrl() {
  return storage::FileSystemURL();
}

constexpr char kBlockingScansForDlpAndMalware[] = R"(
{
  "service_provider": "google",
  "enable": [
    {
      "source_destination_list": [
        {
          "sources": [{
            "file_system_type": "*"
          }],
          "destinations": [{
            "file_system_type": "*"
          }]
        }
      ],
      "tags": ["dlp", "malware"]
    }
  ],
  "block_until_verdict": 1,
  "block_large_files": 0
})";

constexpr char kBlockingScansForDlpAndMalwareReportOnly[] = R"(
{
  "service_provider": "google",
  "enable": [
    {
      "source_destination_list": [
        {
          "sources": [{
            "file_system_type": "*"
          }],
          "destinations": [{
            "file_system_type": "*"
          }]
        }
      ],
      "tags": ["dlp", "malware"]
    }
  ],
  "block_until_verdict": 0,
  "block_large_files": 0
})";

constexpr char kBlockingScansForDlp[] = R"(
{
  "service_provider": "google",
  "enable": [
    {
      "source_destination_list": [
        {
          "sources": [{
            "file_system_type": "*"
          }],
          "destinations": [{
            "file_system_type": "*"
          }]
        }
      ],
      "tags": ["dlp"]
    }
  ],
  "block_until_verdict": 1
})";

constexpr char kBlockingScansForMalware[] = R"(
{
  "service_provider": "google",
  "enable": [
    {
      "source_destination_list": [
        {
          "sources": [{
            "file_system_type": "*"
          }],
          "destinations": [{
            "file_system_type": "*"
          }]
        }
      ],
      "tags": ["malware"]
    }
  ],
  "block_until_verdict": 1
})";

constexpr char kNothingEnabled[] = R"({ "service_provider": "google" })";

chrome::cros::reporting::proto::DlpSensitiveDataEvent
CreateDlpSensitiveDataEvent(
    const std::string& profile_identifier,
    const std::string& file_name,
    const std::string& source,
    const std::string& destination,
    const std::string& sha256,
    const std::string& mimetype,
    int32_t content_size,
    chrome::cros::reporting::proto::TriggeredRuleInfo::Action rule_action,
    chrome::cros::reporting::proto::EventResult event_result,
    std::optional<std::string> user_justification = std::nullopt) {
  chrome::cros::reporting::proto::DlpSensitiveDataEvent event;

  event.set_url("");
  event.set_tab_url("");
  event.set_profile_user_name(kUserName);
  event.set_scan_id("scan_id");

  event.set_profile_identifier(profile_identifier);
  event.set_file_name(file_name);
  event.set_source(source);
  event.set_destination(destination);
  event.set_download_digest_sha_256(sha256);
  event.set_content_type(mimetype);
  event.set_content_size(content_size);
  event.set_trigger(
      chrome::cros::reporting::proto::DataTransferEventTrigger::FILE_TRANSFER);
  event.set_event_result(event_result);

  event.set_clicked_through(
      event_result ==
      chrome::cros::reporting::proto::EventResult::EVENT_RESULT_BYPASSED);

  if (user_justification.has_value()) {
    event.set_user_justification(user_justification.value());
  }

  auto* triggered_rule = event.add_triggered_rule_info();
  triggered_rule->set_rule_name("rule");
  triggered_rule->set_action(rule_action);

  return event;
}

class ScopedSetDMToken {
 public:
  explicit ScopedSetDMToken(const policy::DMToken& dm_token) {
    SetDMTokenForTesting(dm_token);
  }
  ~ScopedSetDMToken() {
    SetDMTokenForTesting(policy::DMToken::CreateEmptyToken());
  }
};

using VolumeInfo = SourceDestinationTestingHelper::VolumeInfo;

constexpr std::initializer_list<VolumeInfo> kVolumeInfos{
    {file_manager::VOLUME_TYPE_TESTING, std::nullopt, "TESTING"},
    {file_manager::VOLUME_TYPE_GOOGLE_DRIVE, std::nullopt, "GOOGLE_DRIVE"},
    {file_manager::VOLUME_TYPE_DOWNLOADS_DIRECTORY, std::nullopt, "MY_FILES"},
    {file_manager::VOLUME_TYPE_REMOVABLE_DISK_PARTITION, std::nullopt,
     "REMOVABLE"},
    {file_manager::VOLUME_TYPE_MOUNTED_ARCHIVE_FILE, std::nullopt, "TESTING"},
    {file_manager::VOLUME_TYPE_PROVIDED, std::nullopt, "PROVIDED"},
    {file_manager::VOLUME_TYPE_MTP, std::nullopt, "DEVICE_MEDIA_STORAGE"},
    {file_manager::VOLUME_TYPE_MEDIA_VIEW, std::nullopt, "ARC"},
    {file_manager::VOLUME_TYPE_CROSTINI, std::nullopt, "CROSTINI"},
    {file_manager::VOLUME_TYPE_ANDROID_FILES, std::nullopt, "ARC"},
    {file_manager::VOLUME_TYPE_DOCUMENTS_PROVIDER, std::nullopt, "ARC"},
    {file_manager::VOLUME_TYPE_SMB, std::nullopt, "SMB"},
    {file_manager::VOLUME_TYPE_SYSTEM_INTERNAL, std::nullopt, "UNKNOWN"},
    {file_manager::VOLUME_TYPE_GUEST_OS, guest_os::VmType::TERMINA, "CROSTINI"},
    {file_manager::VOLUME_TYPE_GUEST_OS, guest_os::VmType::PLUGIN_VM,
     "PLUGIN_VM"},
    {file_manager::VOLUME_TYPE_GUEST_OS, guest_os::VmType::BOREALIS,
     "BOREALIS"},
    {file_manager::VOLUME_TYPE_GUEST_OS, guest_os::VmType::BRUSCHETTA,
     "BRUSCHETTA"},
    {file_manager::VOLUME_TYPE_GUEST_OS, guest_os::VmType::UNKNOWN,
     "UNKNOWN_VM"},
    {file_manager::VOLUME_TYPE_GUEST_OS, std::nullopt, "UNKNOWN_VM"},
    {file_manager::VOLUME_TYPE_GUEST_OS, guest_os::VmType::ARCVM, "ARC"},
};

VolumeInfo GetAnyOtherVolume(const VolumeInfo& volume) {
  for (const auto& volume_i : kVolumeInfos) {
    if (std::string(volume_i.fs_config_string) !=
        std::string(volume.fs_config_string)) {
      return volume_i;
    }
  }
  return {};
}

class BaseTest : public testing::Test {
 public:
  BaseTest() : profile_manager_(TestingBrowserProcess::GetGlobal()) {
    EXPECT_TRUE(profile_manager_.SetUp());
    profile_ = profile_manager_.CreateTestingProfile("test-user");

    source_destination_testing_helper_ =
        std::make_unique<SourceDestinationTestingHelper>(profile_,
                                                         kVolumeInfos);

    file_system_context_ = storage::CreateFileSystemContextForTesting(
        nullptr, source_destination_testing_helper_->GetTempDirPath());
  }

  ~BaseTest() override {
    // This deletion has to happen before source_destination_testing_helper_ is
    // destroyed.
    profile_manager_.DeleteAllTestingProfiles();
  }

  storage::FileSystemURL PathToFileSystemURL(base::FilePath path) {
    return storage::FileSystemURL::CreateForTest(
        kTestStorageKey, storage::kFileSystemTypeLocal, path);
  }

  storage::FileSystemURL GetTestFileSystemURLForVolume(VolumeInfo volume_info) {
    return source_destination_testing_helper_->GetTestFileSystemURLForVolume(
        volume_info);
  }

  Profile* profile() { return profile_; }

  void ValidateIsEnabled(const storage::FileSystemURL& src_url,
                         const storage::FileSystemURL& dest_url,
                         bool expect_dlp,
                         bool expect_malware) {
    auto settings = FileTransferAnalysisDelegate::IsEnabledVec(
        profile(), {src_url}, dest_url);
    ASSERT_EQ(expect_dlp || expect_malware, !settings.empty());
    if (expect_dlp || expect_malware) {
      ASSERT_EQ(settings.size(), 1u);
      ASSERT_TRUE(settings[0].has_value());
      EXPECT_EQ(expect_dlp, settings[0].value().tags.count("dlp"));
      EXPECT_EQ(expect_malware, settings[0].value().tags.count("malware"));
    }
  }

  void ValidateIsEnabled(VolumeInfo src_volume_info,
                         VolumeInfo dest_volume_info,
                         bool expect_dlp,
                         bool expect_malware) {
    ValidateIsEnabled(GetTestFileSystemURLForVolume(src_volume_info),
                      GetTestFileSystemURLForVolume(dest_volume_info),
                      expect_dlp, expect_malware);
  }

 protected:
  content::BrowserTaskEnvironment task_environment_;
  base::test::ScopedFeatureList scoped_feature_list_;
  TestingPrefServiceSimple pref_service_;
  TestingProfileManager profile_manager_;
  raw_ptr<TestingProfile, DanglingUntriaged> profile_;
  const blink::StorageKey kTestStorageKey =
      blink::StorageKey::CreateFromStringForTesting("chrome://abc");
  std::unique_ptr<SourceDestinationTestingHelper>
      source_destination_testing_helper_;
  scoped_refptr<storage::FileSystemContext> file_system_context_;
};

enum PrefState {
  NO_PREF,
  NOTHING_ENABLED_PREF,
  DLP_PREF,
  MALWARE_PREF,
  DLP_MALWARE_PREF,
};

using TestingTuple = std::tuple</*Token valid*/ bool,
                                /*Pref State*/ PrefState,
                                /*Enable unrelated Pref*/ bool>;

static auto testingTupleToString = [](const auto& info) {
  // Can use info.param here to generate the test suffix
  std::string name;
  auto [token_valid, pref_state, unrelated_pref] = info.param;
  if (!token_valid) {
    name += "TokenInvalid";
  }
  switch (pref_state) {
    case NO_PREF:
      name += "NoPref";
      break;
    case NOTHING_ENABLED_PREF:
      name += "NotEnabledPref";
      break;
    case DLP_PREF:
      name += "DLPPref";
      break;
    case MALWARE_PREF:
      name += "MalwarePref";
      break;
    case DLP_MALWARE_PREF:
      name += "DLPMalwarePref";
      break;
  }
  if (unrelated_pref) {
    name += "WithUnrelatedPref";
  }
  return name;
};

}  // namespace

class FileTransferAnalysisDelegateIsEnabledTest
    : public BaseTest,
      public ::testing::WithParamInterface<TestingTuple> {
 protected:
  bool GetTokenValid() { return std::get<0>(GetParam()); }
  PrefState GetPrefState() { return std::get<1>(GetParam()); }
  bool GetUnrelatedPrefEnabled() { return std::get<2>(GetParam()); }
};

INSTANTIATE_TEST_SUITE_P(,
                         FileTransferAnalysisDelegateIsEnabledTest,
                         testing::Combine(testing::Bool(),
                                          testing::Values(NO_PREF,
                                                          NOTHING_ENABLED_PREF,
                                                          DLP_PREF,
                                                          MALWARE_PREF,
                                                          DLP_MALWARE_PREF),
                                          testing::Bool()),
                         testingTupleToString);

TEST_P(FileTransferAnalysisDelegateIsEnabledTest, Enabled) {
  ScopedSetDMToken scoped_dm_token(
      GetTokenValid() ? policy::DMToken::CreateValidToken(kDmToken)
                      : policy::DMToken::CreateInvalidToken());
  switch (GetPrefState()) {
    case NO_PREF:
      break;
    case NOTHING_ENABLED_PREF:
      enterprise_connectors::test::SetAnalysisConnector(
          profile_->GetPrefs(), FILE_TRANSFER, kNothingEnabled);
      break;
    case DLP_PREF:
      enterprise_connectors::test::SetAnalysisConnector(
          profile_->GetPrefs(), FILE_TRANSFER, kBlockingScansForDlp);
      break;
    case MALWARE_PREF:
      enterprise_connectors::test::SetAnalysisConnector(
          profile_->GetPrefs(), FILE_TRANSFER, kBlockingScansForMalware);
      break;
    case DLP_MALWARE_PREF:
      enterprise_connectors::test::SetAnalysisConnector(
          profile_->GetPrefs(), FILE_TRANSFER, kBlockingScansForDlpAndMalware);
      break;
  }
  if (GetUnrelatedPrefEnabled()) {
    // Set for wrong policy (FILE_DOWNLOADED instead of FILE_TRANSFER)!
    enterprise_connectors::test::SetAnalysisConnector(
        profile_->GetPrefs(), FILE_DOWNLOADED, kBlockingScansForDlpAndMalware);
  }

  auto settings = FileTransferAnalysisDelegate::IsEnabledVec(
      profile(), {GetEmptyTestSrcUrl()}, GetEmptyTestDestUrl());

  if (!GetTokenValid() || GetPrefState() == NO_PREF ||
      GetPrefState() == NOTHING_ENABLED_PREF) {
    EXPECT_TRUE(settings.empty());
  } else {
    ASSERT_EQ(settings.size(), 1u);
    ASSERT_TRUE(settings[0].has_value());
    if (GetPrefState() == DLP_PREF || GetPrefState() == DLP_MALWARE_PREF) {
      EXPECT_TRUE(settings[0].value().tags.count("dlp"));
    }
    if (GetPrefState() == MALWARE_PREF || GetPrefState() == DLP_MALWARE_PREF) {
      EXPECT_TRUE(settings[0].value().tags.count("malware"));
    }
  }
}

using FileTransferAnalysisDelegateIsEnabledTestSameFileSystem = BaseTest;

// Test for FileSystemURL that's not registered with a volume.
TEST_F(FileTransferAnalysisDelegateIsEnabledTestSameFileSystem,
       DlpMalwareDisabledForSameFileSystem) {
  ScopedSetDMToken scoped_dm_token(policy::DMToken::CreateValidToken(kDmToken));
  enterprise_connectors::test::SetAnalysisConnector(
      profile_->GetPrefs(), FILE_TRANSFER, kBlockingScansForDlpAndMalware);

  auto settings = FileTransferAnalysisDelegate::IsEnabledVec(
      profile(),
      {PathToFileSystemURL(
          source_destination_testing_helper_->GetTempDirPath())},
      PathToFileSystemURL(
          source_destination_testing_helper_->GetTempDirPath()));

  EXPECT_TRUE(settings.empty());
}

using FileTransferAnalysisDelegateIsEnabledTestMultiple = BaseTest;

// Test using multiple source urls.
TEST_F(FileTransferAnalysisDelegateIsEnabledTestMultiple, Test) {
  ScopedSetDMToken scoped_dm_token(policy::DMToken::CreateValidToken(kDmToken));

  enterprise_connectors::test::SetAnalysisConnector(profile_->GetPrefs(),
                                                    FILE_TRANSFER,
                                                    R"({
          "service_provider": "google",
          "enable": [
            {
              "source_destination_list": [
                {
                  "sources": [{
                    "file_system_type": "REMOVABLE"
                  },
                  {
                    "file_system_type": "CROSTINI"
                  }
                  ],
                  "destinations": [{
                    "file_system_type": "ANY"
                  }]
                }
              ],
              "tags": ["malware"]
            }
          ],
          "block_until_verdict": 1
        })");

  std::vector<VolumeInfo> volume_infos{kVolumeInfos};

  std::vector<storage::FileSystemURL> source_urls;
  for (auto&& volume_info : volume_infos) {
    source_urls.push_back(GetTestFileSystemURLForVolume(volume_info));
  }

  storage::FileSystemURL dest_url =
      GetTestFileSystemURLForVolume(*kVolumeInfos.begin());

  auto settings = FileTransferAnalysisDelegate::IsEnabledVec(
      profile(), source_urls, dest_url);
  ASSERT_EQ(settings.size(), source_urls.size());
  for (size_t i = 0; i < volume_infos.size(); ++i) {
    std::string source_string = volume_infos[i].fs_config_string;
    bool should_be_enabled =
        source_string == "REMOVABLE" || source_string == "CROSTINI";
    EXPECT_EQ(should_be_enabled, settings[i].has_value());
    if (settings[i].has_value()) {
      EXPECT_FALSE(settings[i].value().tags.count("dlp"));
      EXPECT_TRUE(settings[i].value().tags.count("malware"));
    }
  }
}

class FileTransferAnalysisDelegateIsEnabledParamTest
    : public BaseTest,
      public testing::WithParamInterface<VolumeInfo> {};

TEST_P(FileTransferAnalysisDelegateIsEnabledParamTest,
       DlpAndMalwareDisabledForSameVolume) {
  ScopedSetDMToken scoped_dm_token(policy::DMToken::CreateValidToken(kDmToken));

  VolumeInfo source_volume = GetParam();

  enterprise_connectors::test::SetAnalysisConnector(
      profile_->GetPrefs(), FILE_TRANSFER, kBlockingScansForDlpAndMalware);

  VolumeInfo dest_volume = GetParam();

  ValidateIsEnabled(source_volume, dest_volume,
                    /*dlp*/ false,
                    /*malware*/ false);
  ValidateIsEnabled(dest_volume, source_volume,
                    /*dlp*/ false,
                    /*malware*/ false);
}

TEST_P(FileTransferAnalysisDelegateIsEnabledParamTest,
       DlpDisabledByPatternInSource) {
  ScopedSetDMToken scoped_dm_token(policy::DMToken::CreateValidToken(kDmToken));

  VolumeInfo source_volume = GetParam();

  enterprise_connectors::test::SetAnalysisConnector(
      profile_->GetPrefs(), FILE_TRANSFER,
      base::StringPrintf(R"(
        {
          "service_provider": "google",
          "enable": [
            {
              "source_destination_list": [
                {
                  "sources": [{
                    "file_system_type": "*"
                  }],
                  "destinations": [{
                    "file_system_type": "*"
                  }]
                }
              ],
              "tags": ["dlp"]
            }
          ],
          "disable": [
            {
              "source_destination_list": [
                {
                  "sources": [{
                    "file_system_type": "%s"
                  }],
                  "destinations": [{
                    "file_system_type": "*"
                  }]
                }
              ],
              "tags": ["dlp"]
            }
          ],
          "block_until_verdict": 1
        })",
                         source_volume.fs_config_string));

  VolumeInfo dest_volume = GetAnyOtherVolume(source_volume);

  ValidateIsEnabled(source_volume, dest_volume,
                    /*dlp*/ false,
                    /*malware*/ false);
  ValidateIsEnabled(dest_volume, source_volume,
                    /*dlp*/ true,
                    /*malware*/ false);
}

TEST_P(FileTransferAnalysisDelegateIsEnabledParamTest,
       DlpDisabledByPatternInDestination) {
  ScopedSetDMToken scoped_dm_token(policy::DMToken::CreateValidToken(kDmToken));

  VolumeInfo dest_volume = GetParam();

  enterprise_connectors::test::SetAnalysisConnector(
      profile_->GetPrefs(), FILE_TRANSFER,
      base::StringPrintf(R"(
        {
          "service_provider": "google",
          "enable": [
            {
              "source_destination_list": [
                {
                  "sources": [{
                    "file_system_type": "*"
                  }],
                  "destinations": [{
                    "file_system_type": "*"
                  }]
                }
              ],
              "tags": ["dlp"]
            }
          ],
          "disable": [
            {
              "source_destination_list": [
                {
                  "sources": [{
                    "file_system_type": "*"
                  }],
                  "destinations": [{
                    "file_system_type": "%s"
                  }]
                }
              ],
              "tags": ["dlp"]
            }
          ],
          "block_until_verdict": 1
        })",
                         dest_volume.fs_config_string));

  VolumeInfo source_volume = GetAnyOtherVolume(dest_volume);

  ValidateIsEnabled(source_volume, dest_volume,
                    /*dlp*/ false,
                    /*malware*/ false);
  ValidateIsEnabled(dest_volume, source_volume,
                    /*dlp*/ true,
                    /*malware*/ false);
}

TEST_P(FileTransferAnalysisDelegateIsEnabledParamTest,
       MalwareEnabledWithPatternInSource) {
  ScopedSetDMToken scoped_dm_token(policy::DMToken::CreateValidToken(kDmToken));

  VolumeInfo source_volume = GetParam();

  enterprise_connectors::test::SetAnalysisConnector(
      profile_->GetPrefs(), FILE_TRANSFER,
      base::StringPrintf(
          R"({
          "service_provider": "google",
          "enable": [
            {
              "source_destination_list": [
                {
                  "sources": [{
                    "file_system_type": "%s"
                  }],
                  "destinations": [{
                    "file_system_type": "ANY"
                  }]
                }
              ],
              "tags": ["malware"]
            }
          ],
          "block_until_verdict": 1
        })",
          source_volume.fs_config_string));

  VolumeInfo dest_volume = GetAnyOtherVolume(source_volume);

  ValidateIsEnabled(source_volume, dest_volume,
                    /*dlp*/ false,
                    /*malware*/ true);
  ValidateIsEnabled(dest_volume, source_volume,
                    /*dlp*/ false,
                    /*malware*/ false);
}

TEST_P(FileTransferAnalysisDelegateIsEnabledParamTest,
       MalwareEnabledWithPatternsInDestination) {
  ScopedSetDMToken scoped_dm_token(policy::DMToken::CreateValidToken(kDmToken));

  VolumeInfo dest_volume = GetParam();

  enterprise_connectors::test::SetAnalysisConnector(
      profile_->GetPrefs(), FILE_TRANSFER,
      base::StringPrintf(
          R"({
          "service_provider": "google",
          "enable": [
            {
              "source_destination_list": [
                {
                  "sources": [{
                    "file_system_type": "ANY"
                  }],
                  "destinations": [{
                    "file_system_type": "%s"
                  }]
                }
              ],
              "tags": ["malware"]
            }
          ],
          "block_until_verdict": 1
        })",
          dest_volume.fs_config_string));

  VolumeInfo source_volume = GetAnyOtherVolume(dest_volume);

  ValidateIsEnabled(source_volume, dest_volume,
                    /*dlp*/ false,
                    /*malware*/ true);
  ValidateIsEnabled(dest_volume, source_volume,
                    /*dlp*/ false,
                    /*malware*/ false);
}

INSTANTIATE_TEST_SUITE_P(,
                         FileTransferAnalysisDelegateIsEnabledParamTest,
                         testing::ValuesIn(kVolumeInfos));

class FileTransferAnalysisDelegateAuditOnlyTest : public BaseTest {
 public:
  FileTransferAnalysisDelegateAuditOnlyTest() = default;

 protected:
  void SetUp() override {
    BaseTest::SetUp();

    enterprise_connectors::test::SetAnalysisConnector(
        profile_->GetPrefs(), FILE_TRANSFER, kBlockingScansForDlpAndMalware);

    // Setup reporting:
    test::SetOnSecurityEventReporting(profile()->GetPrefs(),
                                      /*enabled*/ true,
                                      /*enabled_event_names*/ {},
                                      /*enabled_opt_in_events*/ {},
                                      /*machine_scope*/ false);
    cloud_policy_client_ = std::make_unique<policy::MockCloudPolicyClient>();
    cloud_policy_client_->SetDMToken(kDmToken);
    RealtimeReportingClientFactory::GetInstance()->SetTestingFactory(
        profile_,
        base::BindRepeating(
            &enterprise_connectors::test::BuildRealtimeReportingClient));
    RealtimeReportingClientFactory::GetForProfile(profile())
        ->SetBrowserCloudPolicyClientForTesting(cloud_policy_client_.get());
    identity_test_environment_ =
        std::make_unique<signin::IdentityTestEnvironment>();
    identity_test_environment_->MakePrimaryAccountAvailable(
        kUserName, signin::ConsentLevel::kSignin);
    RealtimeReportingClientFactory::GetForProfile(profile())
        ->SetIdentityManagerForTesting(
            identity_test_environment_->identity_manager());

    FilesRequestHandler::SetFactoryForTesting(base::BindRepeating(
        &test::FakeFilesRequestHandler::Create,
        base::BindRepeating(
            &FileTransferAnalysisDelegateAuditOnlyTest::FakeFileUploadCallback,
            base::Unretained(this))));

    source_directory_url_ =
        source_destination_testing_helper_->GetTestFileSystemURLForVolume(
            /*VolumeInfo*/ kSourceVolumeInfo, "source");
    ASSERT_TRUE(base::CreateDirectory(source_directory_url_.path()));
    destination_directory_url_ =
        source_destination_testing_helper_->GetTestFileSystemURLForVolume(
            /*VolumeInfo*/ kDestinationVolumeInfo, "destination");
    ASSERT_TRUE(base::CreateDirectory(destination_directory_url_.path()));
  }

  void TearDown() override {
    // Needs to be called before destructor of cloud_policy_client_.
    RealtimeReportingClientFactory::GetForProfile(profile())
        ->SetBrowserCloudPolicyClientForTesting(nullptr);

    BaseTest::TearDown();
  }

  void ScanUpload(const storage::FileSystemURL& source_url,
                  const storage::FileSystemURL& destination_url) {
    source_url_ = source_url;
    destination_url_ = destination_url;
    // The access point is only used for metrics, so its value doesn't affect
    // the tests in this file and can always be the same.
    file_transfer_analysis_delegate_ = FileTransferAnalysisDelegate::Create(
        DeepScanAccessPoint::FILE_TRANSFER, source_url, destination_url,
        profile_, file_system_context_.get(), GetSettings());

    base::test::TestFuture<void> future;
    file_transfer_analysis_delegate_->UploadData(future.GetCallback());
    EXPECT_TRUE(future.Wait());
  }

  AnalysisSettings GetSettings() {
    auto* service = ConnectorsServiceFactory::GetForBrowserContext(profile());
    // If the corresponding Connector policy isn't set, no scans can be
    // performed.
    EXPECT_TRUE(service);
    EXPECT_TRUE(service->IsConnectorEnabled(AnalysisConnector::FILE_TRANSFER));

    // Get settings.
    auto settings = service->GetAnalysisSettings(
        GetEmptyTestSrcUrl(), GetEmptyTestDestUrl(),
        AnalysisConnector::FILE_TRANSFER);
    EXPECT_TRUE(settings.has_value());
    return std::move(settings.value());
  }

  void SetDLPResponse(ContentAnalysisResponse response) {
    dlp_response_ = std::move(response);
  }

  void PathFailsDeepScan(base::FilePath path,
                         ContentAnalysisResponse response) {
    failures_.insert({std::move(path), std::move(response)});
  }

  void FakeFileUploadCallback(
      ScanRequestUploadResult result,
      const base::FilePath& path,
      std::unique_ptr<BinaryUploadRequest> request,
      test::FakeFilesRequestHandler::FakeFileRequestCallback callback) {
    EXPECT_FALSE(path.empty());
    EXPECT_EQ(request->device_token(), kDmToken);

    EXPECT_EQ(request->content_analysis_request().request_data().source(),
              kSourceVolumeInfo.fs_config_string);
    EXPECT_EQ(request->content_analysis_request().request_data().destination(),
              kDestinationVolumeInfo.fs_config_string);

    // Simulate a response.
    base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
        FROM_HERE,
        base::BindOnce(std::move(callback), path,
                       ScanRequestUploadResult::kSuccess,
                       ConnectorStatusCallback(path)),
        kResponseDelay);
  }

  ContentAnalysisResponse ConnectorStatusCallback(const base::FilePath& path) {
    // The path succeeds if it is not in the `failures_` maps.
    auto it = failures_.find(path);
    ContentAnalysisResponse response =
        it != failures_.end()
            ? it->second
            : test::FakeContentAnalysisDelegate::SuccessfulResponse([this]() {
                std::set<std::string> tags;
                if (!dlp_response_.has_value()) {
                  tags.insert("dlp");
                }
                tags.insert("malware");
                return tags;
              }());

    if (dlp_response_.has_value()) {
      *response.add_results() = dlp_response_.value().results(0);
      if (dlp_response_.value().has_request_token()) {
        response.set_request_token(dlp_response_.value().request_token());
      }
    }

    return response;
  }

  [[nodiscard]] std::vector<base::FilePath> CreateFilesForTest(
      const std::vector<base::FilePath::StringType>& file_names,
      const base::FilePath prefix_path) {
    const std::string content = "content";
    std::vector<base::FilePath> paths;
    for (const auto& file_name : file_names) {
      base::FilePath path = prefix_path.Append(file_name);
      if (!base::DirectoryExists(path.DirName())) {
        base::CreateDirectory(path.DirName());
      }
      base::File file(path, base::File::FLAG_CREATE | base::File::FLAG_WRITE);
      file.WriteAtCurrentPos(base::as_byte_span(content));
      paths.emplace_back(path);
    }
    return paths;
  }

  policy::MockCloudPolicyClient* cloud_policy_client() {
    return cloud_policy_client_.get();
  }

 protected:
  std::unique_ptr<FileTransferAnalysisDelegate>
      file_transfer_analysis_delegate_;

  storage::FileSystemURL source_directory_url_;
  storage::FileSystemURL destination_directory_url_;
  VolumeInfo kSourceVolumeInfo{file_manager::VOLUME_TYPE_DOWNLOADS_DIRECTORY,
                               std::nullopt, "MY_FILES"};
  VolumeInfo kDestinationVolumeInfo{
      file_manager::VOLUME_TYPE_REMOVABLE_DISK_PARTITION, std::nullopt,
      "REMOVABLE"};

 private:
  // Used to test reporting.
  std::unique_ptr<policy::MockCloudPolicyClient> cloud_policy_client_;
  // Needed to check username in reports.
  std::unique_ptr<signin::IdentityTestEnvironment> identity_test_environment_;

  ScopedSetDMToken scoped_dm_token_{
      policy::DMToken::CreateValidToken(kDmToken)};

  // Paths in this map will be consider to have failed deep scan checks.
  // The actual failure response is given for each path.
  std::map<base::FilePath, ContentAnalysisResponse> failures_;

  // DLP response to ovewrite in the callback if present.
  std::optional<ContentAnalysisResponse> dlp_response_;

  // URLs to verify source and destination.
  storage::FileSystemURL source_url_;
  storage::FileSystemURL destination_url_;
};

TEST_F(FileTransferAnalysisDelegateAuditOnlyTest, InvalidPath) {
  storage::FileSystemURL source_url = GetEmptyTestSrcUrl();
  storage::FileSystemURL destination_url = GetEmptyTestDestUrl();

  test::EventReportValidator validator(cloud_policy_client());
  validator.ExpectNoReport();

  ScanUpload(source_url, destination_url);

  EXPECT_TRUE(
      file_transfer_analysis_delegate_->GetAnalysisResultAfterScan(source_url)
          .IsUnknown());
  // Checks that there was an early return.
  EXPECT_FALSE(
      file_transfer_analysis_delegate_->GetFilesRequestHandlerForTesting());
}

TEST_F(FileTransferAnalysisDelegateAuditOnlyTest, NonExistingFile) {
  storage::FileSystemURL source_url = PathToFileSystemURL(
      source_directory_url_.path().Append("does_not_exist"));

  test::EventReportValidator validator(cloud_policy_client());
  validator.ExpectNoReport();

  ScanUpload(source_url, destination_directory_url_);

  // Directories should always be unknown!
  EXPECT_TRUE(
      file_transfer_analysis_delegate_->GetAnalysisResultAfterScan(source_url)
          .IsUnknown());
  // Checks that there was an early return.
  EXPECT_FALSE(
      file_transfer_analysis_delegate_->GetFilesRequestHandlerForTesting());
}

TEST_F(FileTransferAnalysisDelegateAuditOnlyTest, EmptyDirectory) {
  test::EventReportValidator validator(cloud_policy_client());
  validator.ExpectNoReport();

  ScanUpload(source_directory_url_, destination_directory_url_);

  // Directories should always be unknown!
  EXPECT_TRUE(file_transfer_analysis_delegate_
                  ->GetAnalysisResultAfterScan(source_directory_url_)
                  .IsUnknown());
  // Checks that there was an early return.
  EXPECT_FALSE(
      file_transfer_analysis_delegate_->GetFilesRequestHandlerForTesting());
}

TEST_F(FileTransferAnalysisDelegateAuditOnlyTest, SingleFileAllowed) {
  std::vector<base::FilePath> paths = CreateFilesForTest(
      {FILE_PATH_LITERAL("foo.doc")}, source_directory_url_.path());

  storage::FileSystemURL source_url = PathToFileSystemURL(paths[0]);

  test::EventReportValidator validator(cloud_policy_client());
  validator.ExpectNoReport();

  ScanUpload(source_url, destination_directory_url_);

  EXPECT_TRUE(file_transfer_analysis_delegate_
                  ->GetAnalysisResultAfterScan(source_directory_url_)
                  .IsUnknown());
  EXPECT_TRUE(
      file_transfer_analysis_delegate_->GetAnalysisResultAfterScan(source_url)
          .IsAllowed());
  // Checks that some scanning was performed.
  EXPECT_TRUE(
      file_transfer_analysis_delegate_->GetFilesRequestHandlerForTesting());
}

TEST_F(FileTransferAnalysisDelegateAuditOnlyTest, SingleFileBlockedDlp) {
  std::vector<base::FilePath> paths = CreateFilesForTest(
      {FILE_PATH_LITERAL("foo.doc")}, source_directory_url_.path());

  // Mark all files and text with failed scans.
  std::string scan_id = "scan_id";
  ContentAnalysisResponse response =
      test::FakeContentAnalysisDelegate::DlpResponse(
          ContentAnalysisResponse::Result::SUCCESS, "rule",
          TriggeredRule::BLOCK);
  response.set_request_token(scan_id);

  SetDLPResponse(response);

  storage::FileSystemURL source_url = PathToFileSystemURL(paths[0]);

  // Check reporting.
  test::EventReportValidator validator(cloud_policy_client());
  base::RunLoop run_loop;
  validator.SetDoneClosure(run_loop.QuitClosure());

  auto expected_event = CreateDlpSensitiveDataEvent(
      /*profile_identifier=*/profile_->GetPath().AsUTF8Unsafe(),
      /*file_name=*/"foo.doc",
      /*source=*/kSourceVolumeInfo.fs_config_string,
      /*destination=*/kDestinationVolumeInfo.fs_config_string,
      /*sha256=*/
      "ED7002B439E9AC845F22357D822BAC1444730FBDB6016D3EC9432297B9EC9F73",
      /*mimetype=*/"application/msword",
      /*content_size=*/std::string("content").size(),
      /*rule_action=*/
      chrome::cros::reporting::proto::TriggeredRuleInfo::BLOCK,
      /*event_result=*/
      chrome::cros::reporting::proto::EventResult::EVENT_RESULT_BLOCKED,
      /*user_justification=*/std::nullopt);

  validator.ExpectSensitiveDataEvent(std::move(expected_event));

  ScanUpload(source_url, destination_directory_url_);
  run_loop.Run();

  EXPECT_TRUE(file_transfer_analysis_delegate_
                  ->GetAnalysisResultAfterScan(source_directory_url_)
                  .IsUnknown());
  EXPECT_TRUE(
      file_transfer_analysis_delegate_->GetAnalysisResultAfterScan(source_url)
          .IsBlocked());
  // Checks that some scanning was performed.
  EXPECT_TRUE(
      file_transfer_analysis_delegate_->GetFilesRequestHandlerForTesting());
}

TEST_F(FileTransferAnalysisDelegateAuditOnlyTest, SingleFileWarnDlp) {
  std::vector<base::FilePath> paths = CreateFilesForTest(
      {FILE_PATH_LITERAL("foo.doc")}, source_directory_url_.path());

  // Mark all files and text with failed scans.
  std::string scan_id = "scan_id";
  ContentAnalysisResponse response =
      test::FakeContentAnalysisDelegate::DlpResponse(
          ContentAnalysisResponse::Result::SUCCESS, "rule",
          TriggeredRule::WARN);
  response.set_request_token(scan_id);

  SetDLPResponse(response);

  storage::FileSystemURL source_url = PathToFileSystemURL(paths[0]);

  {
    // Check reporting.
    test::EventReportValidator validator(cloud_policy_client());
    base::RunLoop run_loop;
    validator.SetDoneClosure(run_loop.QuitClosure());

    auto expected_event = CreateDlpSensitiveDataEvent(
        /*profile_identifier=*/profile_->GetPath().AsUTF8Unsafe(),
        /*file_name=*/"foo.doc",
        /*source=*/kSourceVolumeInfo.fs_config_string,
        /*destination=*/kDestinationVolumeInfo.fs_config_string,
        /*sha256=*/
        "ED7002B439E9AC845F22357D822BAC1444730FBDB6016D3EC9432297B9EC9F73",
        /*mimetype=*/"application/msword",
        /*content_size=*/std::string("content").size(),
        /*rule_action=*/
        chrome::cros::reporting::proto::TriggeredRuleInfo::WARN,
        /*event_result=*/
        chrome::cros::reporting::proto::EventResult::EVENT_RESULT_WARNED,
        /*user_justification=*/std::nullopt);

    validator.ExpectSensitiveDataEvent(std::move(expected_event));

    ScanUpload(source_url, destination_directory_url_);
    run_loop.Run();
  }

  EXPECT_TRUE(file_transfer_analysis_delegate_
                  ->GetAnalysisResultAfterScan(source_directory_url_)
                  .IsUnknown());

  // AnalysisResult should be blocked as the warning isn't bypassed.
  EXPECT_TRUE(
      file_transfer_analysis_delegate_->GetAnalysisResultAfterScan(source_url)
          .IsBlocked());
  // Checks that some scanning was performed.
  EXPECT_TRUE(
      file_transfer_analysis_delegate_->GetFilesRequestHandlerForTesting());

  auto warned_files = file_transfer_analysis_delegate_->GetWarnedFiles();
  ASSERT_EQ(warned_files.size(), 1ul);
  EXPECT_EQ(paths[0], warned_files[0].path());
}

TEST_F(FileTransferAnalysisDelegateAuditOnlyTest, SingleFileWarnDlpBypassed) {
  std::vector<base::FilePath> paths = CreateFilesForTest(
      {FILE_PATH_LITERAL("foo.doc")}, source_directory_url_.path());

  // Mark all files and text with failed scans.
  std::string scan_id = "scan_id";
  ContentAnalysisResponse response =
      test::FakeContentAnalysisDelegate::DlpResponse(
          ContentAnalysisResponse::Result::SUCCESS, "rule",
          TriggeredRule::WARN);
  response.set_request_token(scan_id);

  SetDLPResponse(response);

  storage::FileSystemURL source_url = PathToFileSystemURL(paths[0]);

  {
    // Check reporting.
    test::EventReportValidator validator(cloud_policy_client());
    base::RunLoop run_loop;
    validator.SetDoneClosure(run_loop.QuitClosure());

    auto expected_event = CreateDlpSensitiveDataEvent(
        /*profile_identifier=*/profile_->GetPath().AsUTF8Unsafe(),
        /*file_name=*/"foo.doc",
        /*source=*/kSourceVolumeInfo.fs_config_string,
        /*destination=*/kDestinationVolumeInfo.fs_config_string,
        /*sha256=*/
        "ED7002B439E9AC845F22357D822BAC1444730FBDB6016D3EC9432297B9EC9F73",
        /*mimetype=*/"application/msword",
        /*content_size=*/std::string("content").size(),
        /*rule_action=*/
        chrome::cros::reporting::proto::TriggeredRuleInfo::WARN,
        /*event_result=*/
        chrome::cros::reporting::proto::EventResult::EVENT_RESULT_WARNED,
        /*user_justification=*/std::nullopt);

    validator.ExpectSensitiveDataEvent(std::move(expected_event));

    ScanUpload(source_url, destination_directory_url_);
    run_loop.Run();
  }

  EXPECT_TRUE(file_transfer_analysis_delegate_
                  ->GetAnalysisResultAfterScan(source_directory_url_)
                  .IsUnknown());

  // Checks that some scanning was performed.
  EXPECT_TRUE(
      file_transfer_analysis_delegate_->GetFilesRequestHandlerForTesting());

  auto warned_files = file_transfer_analysis_delegate_->GetWarnedFiles();
  ASSERT_EQ(warned_files.size(), 1ul);
  EXPECT_EQ(paths[0], warned_files[0].path());

  {
    // Check reporting of bypass.
    test::EventReportValidator validator(cloud_policy_client());
    base::RunLoop run_loop;
    validator.SetDoneClosure(run_loop.QuitClosure());

    auto expected_event = CreateDlpSensitiveDataEvent(
        /*profile_identifier=*/profile_->GetPath().AsUTF8Unsafe(),
        /*file_name=*/"foo.doc",
        /*source=*/kSourceVolumeInfo.fs_config_string,
        /*destination=*/kDestinationVolumeInfo.fs_config_string,
        /*sha256=*/
        "ED7002B439E9AC845F22357D822BAC1444730FBDB6016D3EC9432297B9EC9F73",
        /*mimetype=*/"application/msword",
        /*content_size=*/std::string("content").size(),
        /*rule_action=*/
        chrome::cros::reporting::proto::TriggeredRuleInfo::WARN,
        /*event_result=*/
        chrome::cros::reporting::proto::EventResult::EVENT_RESULT_BYPASSED,
        /*user_justification=*/"User justification");

    validator.ExpectSensitiveDataEvent(std::move(expected_event));

    file_transfer_analysis_delegate_->BypassWarnings(kUserJustification);
    run_loop.Run();
  }
}

TEST_F(FileTransferAnalysisDelegateAuditOnlyTest, CustomWarningSettingsUnset) {
  // By default the custom warning message and the learn more URL are not set,
  // and a user justification is not required to bypass a warning.
  std::vector<base::FilePath> paths = CreateFilesForTest(
      {FILE_PATH_LITERAL("foo.doc")}, source_directory_url_.path());

  // Mark all files and text with failed scans.
  std::string scan_id = "scan_id";
  ContentAnalysisResponse response =
      test::FakeContentAnalysisDelegate::DlpResponse(
          ContentAnalysisResponse::Result::SUCCESS, "rule",
          TriggeredRule::WARN);
  response.set_request_token(scan_id);

  SetDLPResponse(response);

  storage::FileSystemURL source_url = PathToFileSystemURL(paths[0]);
  ScanUpload(source_url, destination_directory_url_);

  ASSERT_EQ(
      file_transfer_analysis_delegate_->BypassRequiresJustification(kDlpTag),
      false);
  ASSERT_FALSE(
      file_transfer_analysis_delegate_->GetCustomMessage(kDlpTag).has_value());
  ASSERT_FALSE(file_transfer_analysis_delegate_->GetCustomLearnMoreUrl(kDlpTag)
                   .has_value());

  ASSERT_EQ(file_transfer_analysis_delegate_->BypassRequiresJustification(
                kMalwareTag),
            false);
  ASSERT_FALSE(file_transfer_analysis_delegate_->GetCustomMessage(kMalwareTag)
                   .has_value());
  ASSERT_FALSE(
      file_transfer_analysis_delegate_->GetCustomLearnMoreUrl(kMalwareTag)
          .has_value());
}

TEST_F(FileTransferAnalysisDelegateAuditOnlyTest, CustomWarningSettingsSet) {
  std::vector<base::FilePath> paths = CreateFilesForTest(
      {FILE_PATH_LITERAL("foo.doc")}, source_directory_url_.path());

  // Setup a policy that sets the custom warning message, the learn more URL,
  // and requires a user justification to bypass warnings.
  enterprise_connectors::test::SetAnalysisConnector(
      profile_->GetPrefs(), enterprise_connectors::FILE_TRANSFER,
      R"(
        {
          "service_provider": "google",
          "enable": [
            {
              "source_destination_list": [
                {
                  "sources": [{
                    "file_system_type": "*"
                  }],
                  "destinations": [{
                    "file_system_type": "*"
                  }]
                }
              ],
              "tags": ["dlp", "malware"]
            }
          ],
          "block_until_verdict": 1,
          "custom_messages" : [
            {
              "learn_more_url": "https://learnmore-dlp.com",
              "message": "Custom message dlp",
              "tag": "dlp"
            }, {
              "learn_more_url": "https://learnmore-malware.com",
              "message": "Custom message malware",
              "tag": "malware"
            }
          ],
          "require_justification_tags": [
            "dlp",
            "malware"
          ]
        }
      )");

  // Mark all files and text with failed scans.
  std::string scan_id = "scan_id";
  ContentAnalysisResponse response =
      test::FakeContentAnalysisDelegate::DlpResponse(
          ContentAnalysisResponse::Result::SUCCESS, "rule",
          TriggeredRule::WARN);
  response.set_request_token(scan_id);

  SetDLPResponse(response);

  storage::FileSystemURL source_url = PathToFileSystemURL(paths[0]);
  ScanUpload(source_url, destination_directory_url_);

  ASSERT_EQ(
      file_transfer_analysis_delegate_->BypassRequiresJustification(kDlpTag),
      true);
  ASSERT_EQ(file_transfer_analysis_delegate_->GetCustomMessage(kDlpTag),
            u"Custom message dlp");
  ASSERT_EQ(file_transfer_analysis_delegate_->GetCustomLearnMoreUrl(kDlpTag),
            std::optional<GURL>("https://learnmore-dlp.com"));

  ASSERT_EQ(file_transfer_analysis_delegate_->BypassRequiresJustification(
                kMalwareTag),
            true);
  ASSERT_EQ(file_transfer_analysis_delegate_->GetCustomMessage(kMalwareTag),
            u"Custom message malware");
  ASSERT_EQ(
      file_transfer_analysis_delegate_->GetCustomLearnMoreUrl(kMalwareTag),
      std::optional<GURL>("https://learnmore-malware.com"));

  const std::string wrong_tag = "wrong-tag";
  ASSERT_EQ(
      file_transfer_analysis_delegate_->BypassRequiresJustification(wrong_tag),
      false);
  ASSERT_FALSE(file_transfer_analysis_delegate_->GetCustomMessage(wrong_tag)
                   .has_value());
  ASSERT_FALSE(
      file_transfer_analysis_delegate_->GetCustomLearnMoreUrl(wrong_tag)
          .has_value());
}

TEST_F(FileTransferAnalysisDelegateAuditOnlyTest,
       SingleFileBlockedDlpReportOnly) {
  enterprise_connectors::test::SetAnalysisConnector(
      profile_->GetPrefs(), FILE_TRANSFER,
      kBlockingScansForDlpAndMalwareReportOnly);

  // For report-only mode, the destination is scanned, because we perform the
  // scan after a transfer. So we create the file at the destination.
  std::vector<base::FilePath> paths = CreateFilesForTest(
      {FILE_PATH_LITERAL("foo.doc")}, destination_directory_url_.path());

  // Mark all files and text with failed scans.
  std::string scan_id = "scan_id";
  ContentAnalysisResponse response =
      test::FakeContentAnalysisDelegate::DlpResponse(
          ContentAnalysisResponse::Result::SUCCESS, "rule",
          TriggeredRule::BLOCK);
  response.set_request_token(scan_id);

  SetDLPResponse(response);

  storage::FileSystemURL source_url = PathToFileSystemURL(
      source_directory_url_.path().Append(FILE_PATH_LITERAL("foo.doc")));
  storage::FileSystemURL destination_url = PathToFileSystemURL(paths[0]);

  // Check reporting.
  test::EventReportValidator validator(cloud_policy_client());
  base::RunLoop run_loop;
  validator.SetDoneClosure(run_loop.QuitClosure());

  auto expected_event = CreateDlpSensitiveDataEvent(
      /*profile_identifier=*/profile_->GetPath().AsUTF8Unsafe(),
      /*file_name=*/"foo.doc",
      /*source=*/kSourceVolumeInfo.fs_config_string,
      /*destination=*/kDestinationVolumeInfo.fs_config_string,
      /*sha256=*/
      "ED7002B439E9AC845F22357D822BAC1444730FBDB6016D3EC9432297B9EC9F73",
      /*mimetype=*/"application/msword",
      /*content_size=*/std::string("content").size(),
      /*rule_action=*/
      chrome::cros::reporting::proto::TriggeredRuleInfo::BLOCK,
      /*event_result=*/
      chrome::cros::reporting::proto::EventResult::EVENT_RESULT_ALLOWED,
      /*user_justification=*/std::nullopt);

  validator.ExpectSensitiveDataEvent(std::move(expected_event));

  ScanUpload(source_url, destination_url);
  run_loop.Run();

  // No checks for GetAnalysisResultAfterScan, because it's not allowed to be
  // called for report-only mode.

  // Checks that some scanning was performed.
  EXPECT_TRUE(
      file_transfer_analysis_delegate_->GetFilesRequestHandlerForTesting());
}

TEST_F(FileTransferAnalysisDelegateAuditOnlyTest, SingleFileBlockedMalware) {
  std::vector<base::FilePath> paths = CreateFilesForTest(
      {FILE_PATH_LITERAL("foo.doc")}, source_directory_url_.path());

  // Mark all files and text with failed scans.
  std::string scan_id = "scan_id";
  ContentAnalysisResponse response =
      test::FakeContentAnalysisDelegate::MalwareResponse(TriggeredRule::BLOCK);

  // Setting the rule_name is required for a correct value of thread_type in the
  // report.
  response.mutable_results()
      ->at(0)
      .mutable_triggered_rules()
      ->at(0)
      .set_rule_name("malware");
  response.set_request_token(scan_id);
  PathFailsDeepScan(paths[0], response);

  storage::FileSystemURL source_url = PathToFileSystemURL(paths[0]);

  // Check reporting.
  test::EventReportValidator validator(cloud_policy_client());
  base::RunLoop run_loop;
  validator.SetDoneClosure(run_loop.QuitClosure());

  chrome::cros::reporting::proto::SafeBrowsingDangerousDownloadEvent
      expected_event;

  expected_event.set_url("");
  expected_event.set_tab_url("");
  expected_event.set_source(kSourceVolumeInfo.fs_config_string);
  expected_event.set_destination(kDestinationVolumeInfo.fs_config_string);
  expected_event.set_download_digest_sha256(
      "ED7002B439E9AC845F22357D822BAC1444730FBDB6016D3EC9432297B9EC9F73");
  expected_event.set_threat_type(
      chrome::cros::reporting::proto::SafeBrowsingDangerousDownloadEvent::
          DANGEROUS);
  expected_event.set_file_name("foo.doc");
  expected_event.set_content_type("application/msword");
  expected_event.set_content_size(std::string("content").size());
  expected_event.set_scan_id(scan_id);
  expected_event.set_trigger(
      chrome::cros::reporting::proto::DataTransferEventTrigger::FILE_TRANSFER);
  expected_event.set_event_result(
      chrome::cros::reporting::proto::EventResult::EVENT_RESULT_BLOCKED);
  expected_event.set_clicked_through(false);

  expected_event.set_profile_identifier(profile_->GetPath().AsUTF8Unsafe());
  expected_event.set_profile_user_name(kUserName);

  validator.ExpectDangerousDownloadEvent(std::move(expected_event));

  ScanUpload(source_url, destination_directory_url_);
  run_loop.Run();

  EXPECT_TRUE(file_transfer_analysis_delegate_
                  ->GetAnalysisResultAfterScan(source_directory_url_)
                  .IsUnknown());
  EXPECT_TRUE(
      file_transfer_analysis_delegate_->GetAnalysisResultAfterScan(source_url)
          .IsBlocked());
  // Checks that some scanning was performed.
  EXPECT_TRUE(
      file_transfer_analysis_delegate_->GetFilesRequestHandlerForTesting());
}

TEST_F(FileTransferAnalysisDelegateAuditOnlyTest, SingleFileAllowedEncryptedd) {
  // UtilityThreadHelper needed to verify that the file is encrypted.
  content::InProcessUtilityThreadHelper in_process_utility_thread_helper;

  base::FilePath test_zip;
  EXPECT_TRUE(base::PathService::Get(chrome::DIR_TEST_DATA, &test_zip));
  test_zip = test_zip.AppendASCII("safe_browsing")
                 .AppendASCII("download_protection")
                 .AppendASCII("encrypted.zip");
  base::FilePath path = source_directory_url_.path().Append("encrypted.zip");
  base::CopyFile(test_zip, path);

  // Mark all files and text with successful scans.
  std::string scan_id = "scan_id";
  ContentAnalysisResponse response =
      test::FakeContentAnalysisDelegate::DlpResponse(
          ContentAnalysisResponse::Result::SUCCESS, "rule",
          TriggeredRule::REPORT_ONLY);
  response.set_request_token(scan_id);

  SetDLPResponse(response);

  storage::FileSystemURL source_url = PathToFileSystemURL(path);

  // Check reporting.
  test::EventReportValidator validator(cloud_policy_client());
  base::RunLoop run_loop;
  validator.SetDoneClosure(run_loop.QuitClosure());
  // When resumable upload is in use and the policy does not block encrypted
  // files by default, the file's metadata is uploaded for scanning.
  auto expected_event = CreateDlpSensitiveDataEvent(
      /*profile_identifier=*/profile_->GetPath().AsUTF8Unsafe(),
      /*file_name=*/"encrypted.zip",
      /*source=*/kSourceVolumeInfo.fs_config_string,
      /*destination=*/kDestinationVolumeInfo.fs_config_string,
      /*sha256=*/
      "701FCEA8B2112FFAB257A8A8DFD3382ABCF047689AB028D42903E3B3AA488D9A",
      /*mimetype=*/"application/zip",
      /*content_size=*/20015,
      /*rule_action=*/
      chrome::cros::reporting::proto::TriggeredRuleInfo::REPORT_ONLY,
      /*event_result=*/
      chrome::cros::reporting::proto::EventResult::EVENT_RESULT_ALLOWED,
      /*user_justification=*/std::nullopt);

  validator.ExpectSensitiveDataEvent(std::move(expected_event));

  ScanUpload(source_url, destination_directory_url_);
  run_loop.Run();

  EXPECT_TRUE(file_transfer_analysis_delegate_
                  ->GetAnalysisResultAfterScan(source_directory_url_)
                  .IsUnknown());
  EXPECT_TRUE(
      file_transfer_analysis_delegate_->GetAnalysisResultAfterScan(source_url)
          .IsAllowed());
  // Checks that some scanning was performed.
  EXPECT_TRUE(
      file_transfer_analysis_delegate_->GetFilesRequestHandlerForTesting());
}

TEST_F(FileTransferAnalysisDelegateAuditOnlyTest,
       DirectoryWithSingleFileAllowed) {
  std::vector<base::FilePath> paths = CreateFilesForTest(
      {FILE_PATH_LITERAL("foo.doc")}, source_directory_url_.path());

  storage::FileSystemURL source_url = PathToFileSystemURL(paths[0]);

  test::EventReportValidator validator(cloud_policy_client());
  validator.ExpectNoReport();

  ScanUpload(source_directory_url_, destination_directory_url_);

  EXPECT_TRUE(file_transfer_analysis_delegate_
                  ->GetAnalysisResultAfterScan(source_directory_url_)
                  .IsUnknown());
  EXPECT_TRUE(
      file_transfer_analysis_delegate_->GetAnalysisResultAfterScan(source_url)
          .IsAllowed());
  // Checks that some scanning was performed.
  EXPECT_TRUE(
      file_transfer_analysis_delegate_->GetFilesRequestHandlerForTesting());
}

TEST_F(FileTransferAnalysisDelegateAuditOnlyTest,
       DirectoryWithSingleFileBlocked) {
  std::vector<base::FilePath> paths = CreateFilesForTest(
      {FILE_PATH_LITERAL("foo.doc")}, source_directory_url_.path());

  // Mark all files and text with failed scans.
  std::string scan_id = "scan_id";
  ContentAnalysisResponse response =
      test::FakeContentAnalysisDelegate::DlpResponse(
          ContentAnalysisResponse::Result::SUCCESS, "rule",
          TriggeredRule::BLOCK);
  response.set_request_token(scan_id);
  SetDLPResponse(response);

  // Check reporting.
  test::EventReportValidator validator(cloud_policy_client());
  base::RunLoop run_loop;
  validator.SetDoneClosure(run_loop.QuitClosure());

  auto expected_event = CreateDlpSensitiveDataEvent(
      /*profile_identifier=*/profile_->GetPath().AsUTF8Unsafe(),
      /*file_name=*/"foo.doc",
      /*source=*/kSourceVolumeInfo.fs_config_string,
      /*destination=*/kDestinationVolumeInfo.fs_config_string,
      /*sha256=*/
      "ED7002B439E9AC845F22357D822BAC1444730FBDB6016D3EC9432297B9EC9F73",
      /*mimetype=*/"application/msword",
      /*content_size=*/std::string("content").size(),
      /*rule_action=*/
      chrome::cros::reporting::proto::TriggeredRuleInfo::BLOCK,
      /*event_result=*/
      chrome::cros::reporting::proto::EventResult::EVENT_RESULT_BLOCKED,
      /*user_justification=*/std::nullopt);

  validator.ExpectSensitiveDataEvent(std::move(expected_event));

  ScanUpload(source_directory_url_, destination_directory_url_);
  run_loop.Run();

  EXPECT_TRUE(file_transfer_analysis_delegate_
                  ->GetAnalysisResultAfterScan(source_directory_url_)
                  .IsUnknown());
  EXPECT_TRUE(file_transfer_analysis_delegate_
                  ->GetAnalysisResultAfterScan(PathToFileSystemURL(paths[0]))
                  .IsBlocked());
  // Checks that some scanning was performed.
  EXPECT_TRUE(
      file_transfer_analysis_delegate_->GetFilesRequestHandlerForTesting());
}

TEST_F(FileTransferAnalysisDelegateAuditOnlyTest,
       DirectoryWithMultipleFilesAllAllowed) {
  std::vector<base::FilePath> paths = CreateFilesForTest(
      {FILE_PATH_LITERAL("foo.doc"), FILE_PATH_LITERAL("baa.doc"),
       FILE_PATH_LITERAL("blub.doc")},
      source_directory_url_.path());

  test::EventReportValidator validator(cloud_policy_client());
  validator.ExpectNoReport();

  ScanUpload(source_directory_url_, destination_directory_url_);

  EXPECT_TRUE(file_transfer_analysis_delegate_
                  ->GetAnalysisResultAfterScan(source_directory_url_)
                  .IsUnknown());
  for (const auto& path : paths) {
    EXPECT_TRUE(file_transfer_analysis_delegate_
                    ->GetAnalysisResultAfterScan(PathToFileSystemURL(path))
                    .IsAllowed());
  }
  // Checks that some scanning was performed.
  EXPECT_TRUE(
      file_transfer_analysis_delegate_->GetFilesRequestHandlerForTesting());
}

TEST_F(FileTransferAnalysisDelegateAuditOnlyTest,
       DirectoryWithMultipleFilesAllBlocked) {
  std::vector<base::FilePath> paths = CreateFilesForTest(
      {FILE_PATH_LITERAL("foo.doc"), FILE_PATH_LITERAL("baa.doc"),
       FILE_PATH_LITERAL("blub.doc")},
      source_directory_url_.path());

  // Mark all files and text with failed scans.
  std::string scan_id = "scan_id";
  ContentAnalysisResponse response =
      test::FakeContentAnalysisDelegate::DlpResponse(
          ContentAnalysisResponse::Result::SUCCESS, "rule",
          TriggeredRule::BLOCK);
  response.set_request_token(scan_id);
  SetDLPResponse(response);

  storage::FileSystemURL source_url = PathToFileSystemURL(paths[0]);

  test::EventReportValidator validator(cloud_policy_client());
  base::RunLoop run_loop;
  validator.SetDoneClosure(run_loop.QuitClosure());

  std::vector<chrome::cros::reporting::proto::DlpSensitiveDataEvent>
      expected_events;

  for (size_t i = 0; i < paths.size(); ++i) {
    chrome::cros::reporting::proto::DlpSensitiveDataEvent expected_event;

    expected_event.set_url("");
    expected_event.set_tab_url("");
    expected_event.set_source(kSourceVolumeInfo.fs_config_string);
    expected_event.set_destination(kDestinationVolumeInfo.fs_config_string);

    expected_event.set_content_type("application/msword");
    expected_event.set_content_size(std::string("content").size());
    expected_event.set_trigger(chrome::cros::reporting::proto::
                                   DataTransferEventTrigger::FILE_TRANSFER);
    expected_event.set_clicked_through(false);

    auto* triggered_rule = expected_event.add_triggered_rule_info();
    triggered_rule->set_rule_name("rule");
    triggered_rule->set_action(
        chrome::cros::reporting::proto::TriggeredRuleInfo::BLOCK);

    expected_event.set_profile_identifier(profile_->GetPath().AsUTF8Unsafe());
    expected_event.set_profile_user_name(kUserName);

    expected_events.emplace_back(expected_event);
  }

  validator.ExpectSensitiveDataEvents(
      std::move(expected_events), {"foo.doc", "baa.doc", "blub.doc"},
      {"ED7002B439E9AC845F22357D822BAC1444730FBDB6016D3EC9432297B9EC9F73",
       "ED7002B439E9AC845F22357D822BAC1444730FBDB6016D3EC9432297B9EC9F73",
       "ED7002B439E9AC845F22357D822BAC1444730FBDB6016D3EC9432297B9EC9F73"},
      {EventResultToString(EventResult::BLOCKED),
       EventResultToString(EventResult::BLOCKED),
       EventResultToString(EventResult::BLOCKED)},
      {scan_id, scan_id, scan_id});

  ScanUpload(source_directory_url_, destination_directory_url_);
  run_loop.Run();

  EXPECT_TRUE(file_transfer_analysis_delegate_
                  ->GetAnalysisResultAfterScan(source_directory_url_)
                  .IsUnknown());
  for (const auto& path : paths) {
    EXPECT_TRUE(file_transfer_analysis_delegate_
                    ->GetAnalysisResultAfterScan(PathToFileSystemURL(path))
                    .IsBlocked());
  }
  // Checks that some scanning was performed.
  EXPECT_TRUE(
      file_transfer_analysis_delegate_->GetFilesRequestHandlerForTesting());
}

TEST_F(FileTransferAnalysisDelegateAuditOnlyTest,
       DirectoryWithMultipleFilesSomeBlocked) {
  std::vector<base::FilePath> paths = CreateFilesForTest(
      {FILE_PATH_LITERAL("good1.doc"), FILE_PATH_LITERAL("good2.doc"),
       FILE_PATH_LITERAL("bad1.doc"), FILE_PATH_LITERAL("bad2.doc"),
       FILE_PATH_LITERAL("a_good1.doc")},
      source_directory_url_.path());

  std::string scan_id = "scan_id";
  ContentAnalysisResponse::Result result;
  // Mark all files and text with failed scans.
  for (const auto& path : paths) {
    if (path.value().find("bad") != std::string::npos) {
      ContentAnalysisResponse response =
          test::FakeContentAnalysisDelegate::DlpResponse(
              ContentAnalysisResponse::Result::SUCCESS, "rule",
              TriggeredRule::BLOCK);
      response.set_request_token(scan_id);
      PathFailsDeepScan(path, response);
      result = response.results()[0];
    }
  }

  storage::FileSystemURL source_url = PathToFileSystemURL(paths[0]);

  test::EventReportValidator validator(cloud_policy_client());
  base::RunLoop run_loop;
  validator.SetDoneClosure(run_loop.QuitClosure());

  std::vector<chrome::cros::reporting::proto::DlpSensitiveDataEvent>
      expected_events;

  for (size_t i = 0; i < 2; ++i) {
    chrome::cros::reporting::proto::DlpSensitiveDataEvent expected_event;

    expected_event.set_url("");
    expected_event.set_tab_url("");
    expected_event.set_source(kSourceVolumeInfo.fs_config_string);
    expected_event.set_destination(kDestinationVolumeInfo.fs_config_string);

    expected_event.set_content_type("application/msword");
    expected_event.set_content_size(std::string("content").size());
    expected_event.set_trigger(chrome::cros::reporting::proto::
                                   DataTransferEventTrigger::FILE_TRANSFER);
    expected_event.set_clicked_through(false);

    auto* triggered_rule = expected_event.add_triggered_rule_info();
    triggered_rule->set_rule_name("rule");
    triggered_rule->set_action(
        chrome::cros::reporting::proto::TriggeredRuleInfo::BLOCK);

    expected_event.set_profile_identifier(profile_->GetPath().AsUTF8Unsafe());
    expected_event.set_profile_user_name(kUserName);

    expected_events.emplace_back(expected_event);
  }

  validator.ExpectSensitiveDataEvents(
      std::move(expected_events), {"bad1.doc", "bad2.doc"},
      {"ED7002B439E9AC845F22357D822BAC1444730FBDB6016D3EC9432297B9EC9F73",
       "ED7002B439E9AC845F22357D822BAC1444730FBDB6016D3EC9432297B9EC9F73"},
      {EventResultToString(EventResult::BLOCKED),
       EventResultToString(EventResult::BLOCKED)},
      {scan_id, scan_id});

  ScanUpload(source_directory_url_, destination_directory_url_);
  run_loop.Run();

  EXPECT_TRUE(file_transfer_analysis_delegate_
                  ->GetAnalysisResultAfterScan(source_directory_url_)
                  .IsUnknown());
  for (const auto& path : paths) {
    if (path.value().find("bad") != std::string::npos) {
      EXPECT_TRUE(file_transfer_analysis_delegate_
                      ->GetAnalysisResultAfterScan(PathToFileSystemURL(path))
                      .IsBlocked());
    } else {
      EXPECT_TRUE(file_transfer_analysis_delegate_
                      ->GetAnalysisResultAfterScan(PathToFileSystemURL(path))
                      .IsAllowed());
    }
  }

  // Checks that some scanning was performed.
  EXPECT_TRUE(
      file_transfer_analysis_delegate_->GetFilesRequestHandlerForTesting());
}

TEST_F(FileTransferAnalysisDelegateAuditOnlyTest, DirectoryTreeSomeBlocked) {
  std::vector<base::FilePath> paths = CreateFilesForTest(
      {FILE_PATH_LITERAL("good1.doc"), FILE_PATH_LITERAL("good2.doc"),
       FILE_PATH_LITERAL("bad1.doc"), FILE_PATH_LITERAL("bad2.doc"),
       FILE_PATH_LITERAL("a_good1.doc"), FILE_PATH_LITERAL("a/good1.doc"),
       FILE_PATH_LITERAL("a/a_good1.doc"), FILE_PATH_LITERAL("a/e/bad2b.doc"),
       FILE_PATH_LITERAL("a/e/a_good1.doc"),
       FILE_PATH_LITERAL("a/e/a_bad1.doc"), FILE_PATH_LITERAL("b/good2.doc"),
       FILE_PATH_LITERAL("b/bad1b.doc")},
      source_directory_url_.path());

  std::vector<std::string> expected_filenames;
  std::vector<std::string> expected_shas;
  std::vector<ContentAnalysisResponse::Result> expected_dlp_verdicts;
  std::vector<std::string> expected_results;
  std::vector<std::string> expected_scan_ids;
  std::vector<std::string> expected_rule_names;

  // Mark all files and text with failed scans.
  for (size_t i = 0; i < paths.size(); ++i) {
    auto&& path = paths[i];
    if (path.value().find("bad") != std::string::npos) {
      ContentAnalysisResponse response =
          test::FakeContentAnalysisDelegate::DlpResponse(
              ContentAnalysisResponse::Result::SUCCESS,
              base::StrCat({"rule", base::NumberToString(i)}),
              TriggeredRule::BLOCK);
      std::string request_token =
          base::StrCat({"scan_id", base::NumberToString(i)});
      response.set_request_token(request_token);
      PathFailsDeepScan(path, response);

      expected_filenames.push_back(path.BaseName().AsUTF8Unsafe());
      expected_shas.push_back(
          "ED7002B439E9AC845F22357D822BAC1444730FBDB6016D3EC9432297B9EC9F73");
      expected_dlp_verdicts.push_back(response.results()[0]);
      expected_results.push_back(EventResultToString(EventResult::BLOCKED));
      expected_scan_ids.push_back(request_token);
      expected_rule_names.push_back(
          base::StrCat({"rule", base::NumberToString(i)}));
    }
  }

  storage::FileSystemURL source_url = PathToFileSystemURL(paths[0]);

  test::EventReportValidator validator(cloud_policy_client());
  base::RunLoop run_loop;
  validator.SetDoneClosure(run_loop.QuitClosure());

  std::vector<chrome::cros::reporting::proto::DlpSensitiveDataEvent>
      expected_events;

  for (size_t i = 0; i < expected_filenames.size(); ++i) {
    chrome::cros::reporting::proto::DlpSensitiveDataEvent expected_event;

    expected_event.set_url("");
    expected_event.set_tab_url("");
    expected_event.set_source(kSourceVolumeInfo.fs_config_string);
    expected_event.set_destination(kDestinationVolumeInfo.fs_config_string);

    expected_event.set_content_type("application/msword");
    expected_event.set_content_size(std::string("content").size());
    expected_event.set_trigger(chrome::cros::reporting::proto::
                                   DataTransferEventTrigger::FILE_TRANSFER);
    expected_event.set_clicked_through(false);

    auto* triggered_rule = expected_event.add_triggered_rule_info();
    triggered_rule->set_rule_name(expected_rule_names[i]);
    triggered_rule->set_action(
        chrome::cros::reporting::proto::TriggeredRuleInfo::BLOCK);

    expected_event.set_profile_identifier(profile_->GetPath().AsUTF8Unsafe());
    expected_event.set_profile_user_name(kUserName);

    expected_events.emplace_back(expected_event);
  }

  validator.ExpectSensitiveDataEvents(std::move(expected_events),
                                      expected_filenames, expected_shas,
                                      expected_results, expected_scan_ids);

  ScanUpload(source_directory_url_, destination_directory_url_);
  run_loop.Run();

  EXPECT_TRUE(file_transfer_analysis_delegate_
                  ->GetAnalysisResultAfterScan(source_directory_url_)
                  .IsUnknown());
  for (const auto& path : paths) {
    if (path.value().find("bad") != std::string::npos) {
      EXPECT_TRUE(file_transfer_analysis_delegate_
                      ->GetAnalysisResultAfterScan(PathToFileSystemURL(path))
                      .IsBlocked());
    } else {
      EXPECT_TRUE(file_transfer_analysis_delegate_
                      ->GetAnalysisResultAfterScan(PathToFileSystemURL(path))
                      .IsAllowed());
    }
  }
  // Checks that some scanning was performed.
  EXPECT_TRUE(
      file_transfer_analysis_delegate_->GetFilesRequestHandlerForTesting());
}

TEST_F(FileTransferAnalysisDelegateAuditOnlyTest,
       DirectoryTreeSomeBlockedSomeWarned) {
  std::vector<base::FilePath> paths = CreateFilesForTest(
      {FILE_PATH_LITERAL("good1.doc"), FILE_PATH_LITERAL("good2.doc"),
       FILE_PATH_LITERAL("bad1.doc"), FILE_PATH_LITERAL("bad2.doc"),
       FILE_PATH_LITERAL("warn1.doc"), FILE_PATH_LITERAL("warn2.doc"),
       FILE_PATH_LITERAL("a_good1.doc"), FILE_PATH_LITERAL("a/good1.doc"),
       FILE_PATH_LITERAL("a/a_good1.doc"), FILE_PATH_LITERAL("a/e/bad2b.doc"),
       FILE_PATH_LITERAL("a/e/a_good1.doc"),
       FILE_PATH_LITERAL("a/e/a_bad1.doc"),
       FILE_PATH_LITERAL("a/e/a_warn1.doc"),
       FILE_PATH_LITERAL("a/e/a_warn2.doc"), FILE_PATH_LITERAL("b/good2.doc"),
       FILE_PATH_LITERAL("b/bad1b.doc")},
      source_directory_url_.path());

  std::vector<storage::FileSystemURL> expected_warned_files;

  {
    std::vector<std::string> expected_filenames;
    std::vector<std::string> expected_shas;
    std::vector<ContentAnalysisResponse::Result> expected_dlp_verdicts;
    std::vector<std::string> expected_results;
    std::vector<std::string> expected_scan_ids;
    std::vector<std::string> expected_rule_names;
    std::vector<::chrome::cros::reporting::proto::TriggeredRuleInfo::Action>
        expected_rule_actions;

    // Mark all files and text with failed scans.
    for (size_t i = 0; i < paths.size(); ++i) {
      auto&& path = paths[i];
      bool should_block = path.value().find("bad") != std::string::npos;
      bool should_warn = path.value().find("warn") != std::string::npos;
      if (should_block || should_warn) {
        ContentAnalysisResponse response =
            test::FakeContentAnalysisDelegate::DlpResponse(
                ContentAnalysisResponse::Result::SUCCESS,
                base::StrCat({"rule", base::NumberToString(i)}),
                should_block ? TriggeredRule::BLOCK : TriggeredRule::WARN);
        std::string request_token =
            base::StrCat({"scan_id", base::NumberToString(i)});
        response.set_request_token(request_token);
        PathFailsDeepScan(path, response);

        expected_filenames.push_back(path.BaseName().AsUTF8Unsafe());
        expected_shas.push_back(
            "ED7002B439E9AC845F22357D822BAC1444730FBDB6016D3EC9432297B9EC9F73");
        expected_dlp_verdicts.push_back(response.results()[0]);
        expected_rule_names.push_back(
            base::StrCat({"rule", base::NumberToString(i)}));
        if (should_block) {
          expected_results.push_back(EventResultToString(EventResult::BLOCKED));
          expected_rule_actions.push_back(
              ::chrome::cros::reporting::proto::TriggeredRuleInfo::BLOCK);
        } else {
          ASSERT_TRUE(should_warn);
          expected_results.push_back(EventResultToString(EventResult::WARNED));
          expected_rule_actions.push_back(
              ::chrome::cros::reporting::proto::TriggeredRuleInfo::WARN);
        }
        expected_scan_ids.push_back(request_token);
      }

      if (should_warn) {
        expected_warned_files.emplace_back(PathToFileSystemURL(path));
      }
    }

    test::EventReportValidator validator(cloud_policy_client());
    base::RunLoop run_loop;
    validator.SetDoneClosure(run_loop.QuitClosure());

    std::vector<chrome::cros::reporting::proto::DlpSensitiveDataEvent>
        expected_events;
    for (size_t i = 0; i < expected_filenames.size(); ++i) {
      chrome::cros::reporting::proto::DlpSensitiveDataEvent expected_event;

      expected_event.set_url("");
      expected_event.set_tab_url("");
      expected_event.set_source(kSourceVolumeInfo.fs_config_string);
      expected_event.set_destination(kDestinationVolumeInfo.fs_config_string);

      expected_event.set_content_type("application/msword");
      expected_event.set_content_size(std::string("content").size());
      expected_event.set_trigger(chrome::cros::reporting::proto::
                                     DataTransferEventTrigger::FILE_TRANSFER);
      expected_event.set_clicked_through(false);

      auto* triggered_rule = expected_event.add_triggered_rule_info();
      triggered_rule->set_rule_name(expected_rule_names[i]);
      triggered_rule->set_action(expected_rule_actions[i]);

      expected_event.set_profile_identifier(profile_->GetPath().AsUTF8Unsafe());
      expected_event.set_profile_user_name(kUserName);

      expected_events.emplace_back(expected_event);
    }

    validator.ExpectSensitiveDataEvents(std::move(expected_events),
                                        expected_filenames, expected_shas,
                                        expected_results, expected_scan_ids);
    ScanUpload(source_directory_url_, destination_directory_url_);
    run_loop.Run();
  }

  auto warned_files = file_transfer_analysis_delegate_->GetWarnedFiles();
  EXPECT_THAT(warned_files,
              ::testing::UnorderedElementsAreArray(expected_warned_files));

  EXPECT_TRUE(file_transfer_analysis_delegate_
                  ->GetAnalysisResultAfterScan(source_directory_url_)
                  .IsUnknown());
  for (const auto& path : paths) {
    bool should_block = path.value().find("bad") != std::string::npos;
    bool should_warn = path.value().find("warn") != std::string::npos;
    if (should_block || should_warn) {
      EXPECT_TRUE(file_transfer_analysis_delegate_
                      ->GetAnalysisResultAfterScan(PathToFileSystemURL(path))
                      .IsBlocked());
    } else {
      EXPECT_TRUE(file_transfer_analysis_delegate_
                      ->GetAnalysisResultAfterScan(PathToFileSystemURL(path))
                      .IsAllowed());
    }
  }

  {
    std::vector<std::string> expected_filenames;
    std::vector<std::string> expected_shas;
    std::vector<ContentAnalysisResponse::Result> expected_dlp_verdicts;
    std::vector<std::string> expected_results;
    std::vector<std::string> expected_scan_ids;
    std::vector<std::string> expected_rule_names;

    // Mark all files and text with failed scans.
    for (size_t i = 0; i < paths.size(); ++i) {
      auto&& path = paths[i];
      bool should_warn = path.value().find("warn") != std::string::npos;
      if (should_warn) {
        ContentAnalysisResponse response =
            test::FakeContentAnalysisDelegate::DlpResponse(
                ContentAnalysisResponse::Result::SUCCESS,
                base::StrCat({"rule", base::NumberToString(i)}),
                TriggeredRule::WARN);
        std::string request_token =
            base::StrCat({"scan_id", base::NumberToString(i)});
        response.set_request_token(request_token);

        expected_filenames.push_back(path.BaseName().AsUTF8Unsafe());
        expected_shas.push_back(
            "ED7002B439E9AC845F22357D822BAC1444730FBDB6016D3EC9432297B9EC9F73");
        expected_dlp_verdicts.push_back(response.results()[0]);
        expected_results.push_back(EventResultToString(EventResult::BYPASSED));
        expected_scan_ids.push_back(request_token);
        expected_rule_names.push_back(
            base::StrCat({"rule", base::NumberToString(i)}));
      }
    }

    test::EventReportValidator validator(cloud_policy_client());
    base::RunLoop run_loop;
    validator.SetDoneClosure(run_loop.QuitClosure());

    std::vector<chrome::cros::reporting::proto::DlpSensitiveDataEvent>
        expected_events;
    for (size_t i = 0; i < expected_filenames.size(); ++i) {
      chrome::cros::reporting::proto::DlpSensitiveDataEvent expected_event;

      expected_event.set_url("");
      expected_event.set_tab_url("");
      expected_event.set_source(kSourceVolumeInfo.fs_config_string);
      expected_event.set_destination(kDestinationVolumeInfo.fs_config_string);

      expected_event.set_content_type("application/msword");
      expected_event.set_content_size(std::string("content").size());
      expected_event.set_trigger(chrome::cros::reporting::proto::
                                     DataTransferEventTrigger::FILE_TRANSFER);
      expected_event.set_clicked_through(true);
      expected_event.set_user_justification("User justification");

      auto* triggered_rule = expected_event.add_triggered_rule_info();
      triggered_rule->set_rule_name(expected_rule_names[i]);
      triggered_rule->set_action(
          ::chrome::cros::reporting::proto::TriggeredRuleInfo::WARN);

      expected_event.set_profile_identifier(profile_->GetPath().AsUTF8Unsafe());
      expected_event.set_profile_user_name(kUserName);

      expected_events.emplace_back(expected_event);
    }

    validator.ExpectSensitiveDataEvents(std::move(expected_events),
                                        expected_filenames, expected_shas,
                                        expected_results, expected_scan_ids);

    file_transfer_analysis_delegate_->BypassWarnings(kUserJustification);
    run_loop.Run();
  }

  // Should now no longer block bypassed files.
  EXPECT_TRUE(file_transfer_analysis_delegate_
                  ->GetAnalysisResultAfterScan(source_directory_url_)
                  .IsUnknown());
  for (const auto& path : paths) {
    bool should_block = path.value().find("bad") != std::string::npos;
    if (should_block) {
      EXPECT_TRUE(file_transfer_analysis_delegate_
                      ->GetAnalysisResultAfterScan(PathToFileSystemURL(path))
                      .IsBlocked());
    } else {
      EXPECT_TRUE(file_transfer_analysis_delegate_
                      ->GetAnalysisResultAfterScan(PathToFileSystemURL(path))
                      .IsAllowed());
    }
  }

  // Checks that some scanning was performed.
  EXPECT_TRUE(
      file_transfer_analysis_delegate_->GetFilesRequestHandlerForTesting());
}

}  // namespace enterprise_connectors
