chromium/chrome/browser/enterprise/connectors/analysis/file_transfer_analysis_delegate_unittest.cc

// 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.

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif

#include "chrome/browser/enterprise/connectors/analysis/file_transfer_analysis_delegate.h"

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

#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/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 "chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.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/policy/core/common/cloud/mock_cloud_policy_client.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[] = "[email protected]";

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" })";

const std::set<std::string>* DocMimeTypes() {
  static std::set<std::string> set = {
      "application/msword", "text/plain",
      // The 50 MB file can result in no mimetype being found.
      ""};
  return &set;
}

const std::set<std::string>* ZipMimeTypes() {
  static std::set<std::string> set = {"application/zip",
                                      "application/x-zip-compressed"};
  return &set;
}

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);
    extensions::SafeBrowsingPrivateEventRouterFactory::GetInstance()
        ->SetTestingFactory(
            profile_, base::BindRepeating(
                          &safe_browsing::BuildSafeBrowsingPrivateEventRouter));
    RealtimeReportingClientFactory::GetInstance()->SetTestingFactory(
        profile_,
        base::BindRepeating(&safe_browsing::BuildRealtimeReportingClient));
    RealtimeReportingClientFactory::GetForProfile(profile())
        ->SetBrowserCloudPolicyClientForTesting(cloud_policy_client_.get());
    identity_test_environment_ =
        std::make_unique<signin::IdentityTestEnvironment>();
    identity_test_environment_->MakePrimaryAccountAvailable(
        kUserName, signin::ConsentLevel::kSync);
    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(
        safe_browsing::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(
      safe_browsing::BinaryUploadService::Result result,
      const base::FilePath& path,
      std::unique_ptr<safe_browsing::BinaryUploadService::Request> 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,
                       safe_browsing::BinaryUploadService::Result::SUCCESS,
                       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(content.data(), content.size());
      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_ = std::nullopt;

  // 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());
  validator.ExpectSensitiveDataEvent(
      /*url*/ "",
      /*tab_url*/ "",
      /*source*/ kSourceVolumeInfo.fs_config_string,
      /*destination*/ kDestinationVolumeInfo.fs_config_string,
      /*filename*/ "foo.doc",
      // printf "content" | sha256sum  |  tr '[:lower:]' '[:upper:]'
      /*sha*/
      "ED7002B439E9AC845F22357D822BAC1444730FBDB6016D3EC9432297B9EC9F73",
      /*trigger*/
      extensions::SafeBrowsingPrivateEventRouter::kTriggerFileTransfer,
      /*dlp_verdict*/ response.results()[0],
      /*mimetype*/ DocMimeTypes(),
      /*size*/ std::string("content").size(),
      /*result*/
      safe_browsing::EventResultToString(safe_browsing::EventResult::BLOCKED),
      /*username*/ kUserName,
      /*profile_identifier*/ profile_->GetPath().AsUTF8Unsafe(),
      /*scan_id*/ scan_id,
      /*content_transfer_method*/ std::nullopt,
      /*user_justification*/ std::nullopt);

  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)
          .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());
    validator.ExpectSensitiveDataEvent(
        /*url*/ "",
        /*tab_url*/ "",
        /*source*/ kSourceVolumeInfo.fs_config_string,
        /*destination*/ kDestinationVolumeInfo.fs_config_string,
        /*filename*/ "foo.doc",
        // printf "content" | sha256sum  |  tr '[:lower:]' '[:upper:]'
        /*sha*/
        "ED7002B439E9AC845F22357D822BAC1444730FBDB6016D3EC9432297B9EC9F73",
        /*trigger*/
        extensions::SafeBrowsingPrivateEventRouter::kTriggerFileTransfer,
        /*dlp_verdict*/ response.results()[0],
        /*mimetype*/ DocMimeTypes(),
        /*size*/ std::string("content").size(),
        /*result*/
        safe_browsing::EventResultToString(safe_browsing::EventResult::WARNED),
        /*username*/ kUserName,
        /*profile_identifier*/ profile_->GetPath().AsUTF8Unsafe(),
        /*scan_id*/ scan_id,
        /*content_transfer_method*/ std::nullopt,
        /*user_justification*/ std::nullopt);

    ScanUpload(source_url, destination_directory_url_);
  }

  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());
    validator.ExpectSensitiveDataEvent(
        /*url*/ "",
        /*tab_url*/ "",
        /*source*/ kSourceVolumeInfo.fs_config_string,
        /*destination*/ kDestinationVolumeInfo.fs_config_string,
        /*filename*/ "foo.doc",
        // printf "content" | sha256sum  |  tr '[:lower:]' '[:upper:]'
        /*sha*/
        "ED7002B439E9AC845F22357D822BAC1444730FBDB6016D3EC9432297B9EC9F73",
        /*trigger*/
        extensions::SafeBrowsingPrivateEventRouter::kTriggerFileTransfer,
        /*dlp_verdict*/ response.results()[0],
        /*mimetype*/ DocMimeTypes(),
        /*size*/ std::string("content").size(),
        /*result*/
        safe_browsing::EventResultToString(safe_browsing::EventResult::WARNED),
        /*username*/ kUserName,
        /*profile_identifier*/ profile_->GetPath().AsUTF8Unsafe(),
        /*scan_id*/ scan_id,
        /*content_transfer_method*/ std::nullopt,
        /*user_justification*/ std::nullopt);

    ScanUpload(source_url, destination_directory_url_);
  }

  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());
    validator.ExpectSensitiveDataEvent(
        /*url*/ "",
        /*tab_url*/ "",
        /*source*/ kSourceVolumeInfo.fs_config_string,
        /*destination*/ kDestinationVolumeInfo.fs_config_string,
        /*filename*/ "foo.doc",
        // printf "content" | sha256sum  |  tr '[:lower:]' '[:upper:]'
        /*sha*/
        "ED7002B439E9AC845F22357D822BAC1444730FBDB6016D3EC9432297B9EC9F73",
        /*trigger*/
        extensions::SafeBrowsingPrivateEventRouter::kTriggerFileTransfer,
        /*dlp_verdict*/ response.results()[0],
        /*mimetype*/ DocMimeTypes(),
        /*size*/ std::string("content").size(),
        /*result*/
        safe_browsing::EventResultToString(
            safe_browsing::EventResult::BYPASSED),
        /*username*/ kUserName,
        /*profile_identifier*/ profile_->GetPath().AsUTF8Unsafe(),
        /*scan_id*/ scan_id,
        /*content_transfer_method*/ std::nullopt,
        /*user_justification*/ kUserJustification);

    file_transfer_analysis_delegate_->BypassWarnings(kUserJustification);
  }
}

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());
  validator.ExpectSensitiveDataEvent(
      /*url*/ "",
      /*tab_url*/ "",
      /*source*/ kSourceVolumeInfo.fs_config_string,
      /*destination*/ kDestinationVolumeInfo.fs_config_string,
      /*filename*/ "foo.doc",
      // printf "content" | sha256sum  |  tr '[:lower:]' '[:upper:]'
      /*sha*/
      "ED7002B439E9AC845F22357D822BAC1444730FBDB6016D3EC9432297B9EC9F73",
      /*trigger*/
      extensions::SafeBrowsingPrivateEventRouter::kTriggerFileTransfer,
      /*dlp_verdict*/ response.results()[0],
      /*mimetype*/ DocMimeTypes(),
      /*size*/ std::string("content").size(),
      /*result*/
      safe_browsing::EventResultToString(safe_browsing::EventResult::ALLOWED),
      /*username*/ kUserName,
      /*profile_identifier*/ profile_->GetPath().AsUTF8Unsafe(),
      /*scan_id*/ scan_id,
      /*content_transfer_method*/ std::nullopt,
      /*user_justification*/ std::nullopt);

  ScanUpload(source_url, destination_url);

  // 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());
  validator.ExpectDangerousDeepScanningResult(
      /*url*/ "",
      /*tab_url*/ "",
      /*source*/ kSourceVolumeInfo.fs_config_string,
      /*destination*/ kDestinationVolumeInfo.fs_config_string,
      /*filename*/ "foo.doc",
      // printf "content" | sha256sum  |  tr '[:lower:]' '[:upper:]'
      /*sha*/
      "ED7002B439E9AC845F22357D822BAC1444730FBDB6016D3EC9432297B9EC9F73",
      /*thread_type*/ "DANGEROUS",
      /*trigger*/
      extensions::SafeBrowsingPrivateEventRouter::kTriggerFileTransfer,
      /*mimetype*/ DocMimeTypes(),
      /*size*/ std::string("content").size(),
      /*result*/
      safe_browsing::EventResultToString(safe_browsing::EventResult::BLOCKED),
      /*username*/ kUserName,
      /*profile_identifier*/ profile_->GetPath().AsUTF8Unsafe(),
      /*scan_id*/ scan_id);

  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)
          .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());
  // When resumable upload is in use and the policy does not block encrypted
  // files by default, the file's metadata is uploaded for scanning.
  validator.ExpectSensitiveDataEvent(
      /*url*/ "",
      /*tab_url*/ "",
      /*source*/ kSourceVolumeInfo.fs_config_string,
      /*destination*/ kDestinationVolumeInfo.fs_config_string,
      /*filename*/ "encrypted.zip",
      // printf "content" | sha256sum  |  tr '[:lower:]' '[:upper:]'
      /*sha*/
      "701FCEA8B2112FFAB257A8A8DFD3382ABCF047689AB028D42903E3B3AA488D9A",
      /*trigger*/
      extensions::SafeBrowsingPrivateEventRouter::kTriggerFileTransfer,
      /*dlp_verdict*/ response.results()[0],
      /*mimetype*/ ZipMimeTypes(),
      /*size*/ 20015,
      /*result*/
      safe_browsing::EventResultToString(safe_browsing::EventResult::ALLOWED),
      /*username*/ kUserName,
      /*profile_identifier*/ profile_->GetPath().AsUTF8Unsafe(),
      /*scan_id*/ scan_id,
      /*content_transfer_method*/ std::nullopt,
      /*user_justification*/ std::nullopt);

  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,
       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());
  validator.ExpectSensitiveDataEvent(
      /*url*/ "",
      /*tab_url*/ "",
      /*source*/ kSourceVolumeInfo.fs_config_string,
      /*destination*/ kDestinationVolumeInfo.fs_config_string,
      /*filename*/ "foo.doc",
      // printf "content" | sha256sum  |  tr '[:lower:]' '[:upper:]'
      /*sha*/
      "ED7002B439E9AC845F22357D822BAC1444730FBDB6016D3EC9432297B9EC9F73",
      /*trigger*/
      extensions::SafeBrowsingPrivateEventRouter::kTriggerFileTransfer,
      /*dlp_verdict*/ response.results()[0],
      /*mimetype*/ DocMimeTypes(),
      /*size*/ std::string("content").size(),
      /*result*/
      safe_browsing::EventResultToString(safe_browsing::EventResult::BLOCKED),
      /*username*/ kUserName,
      /*profile_identifier*/ profile_->GetPath().AsUTF8Unsafe(),
      /*scan_id*/ scan_id,
      /*content_transfer_method*/ std::nullopt,
      /*user_justification*/ std::nullopt);

  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(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());
  validator.ExpectSensitiveDataEvents(
      /*url*/ "",
      /*tab_url*/ "",
      /*source*/ kSourceVolumeInfo.fs_config_string,
      /*destination*/ kDestinationVolumeInfo.fs_config_string,
      /*filenames*/ {"foo.doc", "baa.doc", "blub.doc"},
      // printf "content" | sha256sum  |  tr '[:lower:]' '[:upper:]'
      /*sha256s*/
      {"ED7002B439E9AC845F22357D822BAC1444730FBDB6016D3EC9432297B9EC9F73",
       "ED7002B439E9AC845F22357D822BAC1444730FBDB6016D3EC9432297B9EC9F73",
       "ED7002B439E9AC845F22357D822BAC1444730FBDB6016D3EC9432297B9EC9F73"},
      /*trigger*/
      extensions::SafeBrowsingPrivateEventRouter::kTriggerFileTransfer,
      /*dlp_verdicts*/
      {response.results()[0], response.results()[0], response.results()[0]},
      /*mimetype*/ DocMimeTypes(),
      /*size*/ std::string("content").size(),
      /*result*/
      {safe_browsing::EventResultToString(safe_browsing::EventResult::BLOCKED),
       safe_browsing::EventResultToString(safe_browsing::EventResult::BLOCKED),
       safe_browsing::EventResultToString(safe_browsing::EventResult::BLOCKED)},
      /*username*/ kUserName,
      /*profile_identifier*/ profile_->GetPath().AsUTF8Unsafe(),
      /*scan_ids*/ {scan_id, scan_id, scan_id},
      /*content_transfer_method*/ std::nullopt,
      /*user_justification*/ std::nullopt);

  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))
                    .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());
  validator.ExpectSensitiveDataEvents(
      /*url*/ "",
      /*tab_url*/ "",
      /*source*/ kSourceVolumeInfo.fs_config_string,
      /*destination*/ kDestinationVolumeInfo.fs_config_string,
      /*filenames*/ {"bad1.doc", "bad2.doc"},
      // printf "content" | sha256sum  |  tr '[:lower:]' '[:upper:]'
      /*sha256s*/
      {"ED7002B439E9AC845F22357D822BAC1444730FBDB6016D3EC9432297B9EC9F73",
       "ED7002B439E9AC845F22357D822BAC1444730FBDB6016D3EC9432297B9EC9F73"},
      /*trigger*/
      extensions::SafeBrowsingPrivateEventRouter::kTriggerFileTransfer,
      /*dlp_verdicts*/
      {result, result},
      /*mimetype*/ DocMimeTypes(),
      /*size*/ std::string("content").size(),
      /*result*/
      {safe_browsing::EventResultToString(safe_browsing::EventResult::BLOCKED),
       safe_browsing::EventResultToString(safe_browsing::EventResult::BLOCKED)},
      /*username*/ kUserName,
      /*profile_identifier*/ profile_->GetPath().AsUTF8Unsafe(),
      /*scan_ids*/ {scan_id, scan_id},
      /*content_transfer_method*/ std::nullopt,
      /*user_justification*/ std::nullopt);

  ScanUpload(source_directory_url_, destination_directory_url_);

  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;

  // 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(safe_browsing::EventResultToString(
          safe_browsing::EventResult::BLOCKED));
      expected_scan_ids.push_back(request_token);
    }
  }

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

  test::EventReportValidator validator(cloud_policy_client());
  validator.ExpectSensitiveDataEvents(
      /*url*/ "",
      /*tab_url*/ "",
      /*source*/ kSourceVolumeInfo.fs_config_string,
      /*destination*/ kDestinationVolumeInfo.fs_config_string,
      /*filenames*/ expected_filenames,
      // printf "content" | sha256sum  |  tr '[:lower:]' '[:upper:]'
      /*sha256s*/
      expected_shas,
      /*trigger*/
      extensions::SafeBrowsingPrivateEventRouter::kTriggerFileTransfer,
      /*dlp_verdicts*/
      expected_dlp_verdicts,
      /*mimetype*/ DocMimeTypes(),
      /*size*/ std::string("content").size(),
      /*result*/
      expected_results,
      /*username*/ kUserName,
      /*profile_identifier*/ profile_->GetPath().AsUTF8Unsafe(),
      /*scan_ids*/ expected_scan_ids,
      /*content_transfer_method*/ std::nullopt,
      /*user_justification*/ std::nullopt);

  ScanUpload(source_directory_url_, destination_directory_url_);

  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;

    // 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]);
        if (should_block) {
          expected_results.push_back(safe_browsing::EventResultToString(
              safe_browsing::EventResult::BLOCKED));
        } else {
          ASSERT_TRUE(should_warn);
          expected_results.push_back(safe_browsing::EventResultToString(
              safe_browsing::EventResult::WARNED));
        }
        expected_scan_ids.push_back(request_token);
      }

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

    test::EventReportValidator validator(cloud_policy_client());
    validator.ExpectSensitiveDataEvents(
        /*url*/ "",
        /*tab_url*/ "",
        /*source*/ kSourceVolumeInfo.fs_config_string,
        /*destination*/ kDestinationVolumeInfo.fs_config_string,
        /*filenames*/ expected_filenames,
        // printf "content" | sha256sum  |  tr '[:lower:]' '[:upper:]'
        /*sha256s*/
        expected_shas,
        /*trigger*/
        extensions::SafeBrowsingPrivateEventRouter::kTriggerFileTransfer,
        /*dlp_verdicts*/
        expected_dlp_verdicts,
        /*mimetype*/ DocMimeTypes(),
        /*size*/ std::string("content").size(),
        /*result*/
        expected_results,
        /*username*/ kUserName,
        /*profile_identifier*/ profile_->GetPath().AsUTF8Unsafe(),
        /*scan_ids*/ expected_scan_ids,
        /*content_transfer_method*/ std::nullopt,
        /*user_justification*/ std::nullopt);

    ScanUpload(source_directory_url_, destination_directory_url_);
  }

  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;

    // 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(safe_browsing::EventResultToString(
            safe_browsing::EventResult::BYPASSED));

        expected_scan_ids.push_back(request_token);
      }
    }

    test::EventReportValidator validator(cloud_policy_client());
    validator.ExpectSensitiveDataEvents(
        /*url*/ "",
        /*tab_url*/ "",
        /*source*/ kSourceVolumeInfo.fs_config_string,
        /*destination*/ kDestinationVolumeInfo.fs_config_string,
        /*filenames*/ expected_filenames,
        // printf "content" | sha256sum  |  tr '[:lower:]' '[:upper:]'
        /*sha256s*/
        expected_shas,
        /*trigger*/
        extensions::SafeBrowsingPrivateEventRouter::kTriggerFileTransfer,
        /*dlp_verdicts*/
        expected_dlp_verdicts,
        /*mimetype*/ DocMimeTypes(),
        /*size*/ std::string("content").size(),
        /*result*/
        expected_results,
        /*username*/ kUserName,
        /*profile_identifier*/ profile_->GetPath().AsUTF8Unsafe(),
        /*scan_ids*/ expected_scan_ids,
        /*content_transfer_method*/ std::nullopt,
        /*user_justification*/ kUserJustification);

    file_transfer_analysis_delegate_->BypassWarnings(kUserJustification);
  }

  // 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