chromium/chrome/browser/enterprise/connectors/analysis/file_transfer_analysis_delegate.h

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

#ifndef CHROME_BROWSER_ENTERPRISE_CONNECTORS_ANALYSIS_FILE_TRANSFER_ANALYSIS_DELEGATE_H_
#define CHROME_BROWSER_ENTERPRISE_CONNECTORS_ANALYSIS_FILE_TRANSFER_ANALYSIS_DELEGATE_H_

#include <vector>

#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "chrome/browser/enterprise/connectors/analysis/content_analysis_delegate_base.h"
#include "chrome/browser/enterprise/connectors/common.h"
#include "chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils.h"
#include "components/enterprise/common/proto/connectors.pb.h"
#include "storage/browser/file_system/file_system_url.h"

class Profile;

namespace storage {
class FileSystemContext;
class RecursiveOperationDelegate;
}  // namespace storage

namespace enterprise_connectors {

class FilesRequestHandler;

// `FileTransferAnalysisDelegate` handles scanning and reporting of ChromeOS
// file system transfers.
// A user of `FileTransferAnalysisDelegate` should first check whether scanning
// is enabled for a pair of filesystem urls using `IsEnabledVec()`.
// Note: `IsEnabledVec()` allows checking for multiple source urls at once. In
// this case, a user has to create a FileTransferAnalysisDelegate for each
// source url.
//
// If scanning is enabled, a user proceeds with the creation of the class and
// then calls `UploadData()` to start the scan. Once the scans are complete,
// `callback_` is run.
// After a completed scan, a user of `FileTransferAnalysisDelegate` can access
// the scanning results for different filesystem urls using
// `GetAnalysisResult()`.
//
// If `source_url` is a directory, all files contained within the directory or
// any descended directory will be scanned. If `source_url` is a file only that
// file will be scanned.
class FileTransferAnalysisDelegate {
 public:
  using FileTransferAnalysisDelegateFactory = base::RepeatingCallback<
      std::unique_ptr<enterprise_connectors::FileTransferAnalysisDelegate>(
          safe_browsing::DeepScanAccessPoint access_point,
          storage::FileSystemURL source_url,
          storage::FileSystemURL destination_url,
          Profile* profile,
          storage::FileSystemContext* file_system_context,
          enterprise_connectors::AnalysisSettings settings)>;

  // The verdict of an analysis.
  enum Verdict {
    // The file transfer is allowed.
    ALLOWED,
    // The file transfer is blocked.
    BLOCKED,
    // A file transfer result verdict is unknown if the file was copied/moved to
    // a scanned directory after the scan was started
    UNKNOWN,
  };

  // The result of an analysis.
  class FileTransferAnalysisResult {
   public:
    // Creates a result representing an allowed file transfer.
    static FileTransferAnalysisResult Allowed();
    // Creates a result for a file transfer blocked because of `tag`.
    static FileTransferAnalysisResult Blocked(
        FinalContentAnalysisResult final_result,
        const std::string& tag);
    // Represents a file transfer for which there is no known result.
    static FileTransferAnalysisResult Unknown();

    ~FileTransferAnalysisResult();
    FileTransferAnalysisResult(const FileTransferAnalysisResult& other);
    FileTransferAnalysisResult& operator=(FileTransferAnalysisResult&& other);

    bool IsAllowed() const;
    bool IsBlocked() const;
    bool IsUnknown() const;

    const std::string& tag() const;
    const std::optional<FinalContentAnalysisResult> final_result() const;

   private:
    FileTransferAnalysisResult(
        Verdict verdict,
        std::optional<FinalContentAnalysisResult> final_result,
        const std::string& tag);

    Verdict verdict_ = Verdict::UNKNOWN;
    // The tag ("dlp" or "malware") is only relevant when verdict is BLOCKED.
    // Note however that results associated with blocked files can have an empty
    // tag. This may happen when the file is blocked because it's encrypted or
    // it's too large to be uploaded.
    // For blocked files with an empty tag, final result contains the reason for
    // which they were blocked.
    std::optional<FinalContentAnalysisResult> final_result_;
    std::string tag_;
  };

  virtual ~FileTransferAnalysisDelegate();

  // Create the FileTransferAnalysisDelegate. This function uses the factory if
  // it is set via `SetFactorForTesting()`.
  //
  // For `block_until_verdict == 0`, the `destination_url` has to point to the
  // copied file/directory and not its parent. If it points to the parent, all
  // files within the destination directory are scanned.
  static std::unique_ptr<FileTransferAnalysisDelegate> Create(
      safe_browsing::DeepScanAccessPoint access_point,
      storage::FileSystemURL source_url,
      storage::FileSystemURL destination_url,
      Profile* profile,
      storage::FileSystemContext* file_system_context,
      AnalysisSettings settings);

  // Set a factory for the FileTransferAnalysisDelegate.
  // Can be used in testing to create `MockFileTransferAnalysisDelegate`s.
  static void SetFactorForTesting(FileTransferAnalysisDelegateFactory factory);

  // Returns a vector with the AnalysisSettings for file transfers from the
  // respective source url to the destination_url.
  // If the enterprise connectors are not enabled for any of the transfers an
  // empty vector is returned. Each entry in the returned vector corresponds to
  // the entry in the `source_urls` vector with the same index.
  static std::vector<std::optional<AnalysisSettings>> IsEnabledVec(
      Profile* profile,
      const std::vector<storage::FileSystemURL>& source_urls,
      storage::FileSystemURL destination_url);

  // Main entrypoint to start the file uploads.
  // Once scanning is complete `callback_` will be called.
  virtual void UploadData(base::OnceClosure completion_callback);

  // Calling this function is only allowed after the scan is complete!
  virtual FileTransferAnalysisResult GetAnalysisResultAfterScan(
      storage::FileSystemURL url);
  // Calling this function is only allowed after the scan is complete!
  virtual std::vector<storage::FileSystemURL> GetWarnedFiles() const;

  // Called when the user cancels a transfer.
  void Cancel(bool warning);

  // Called when the user byapass a warning.
  void BypassWarnings(std::optional<std::u16string> user_justification);

  // Returns the custom message specified by the admin for the given tag, or
  // std::nullopt if there isn't any.
  std::optional<std::u16string> GetCustomMessage(const std::string& tag) const;

  // Returns the custom "learn more" URL specified by the admin for the given
  // tag, or std::nullopt if there isn't any.
  std::optional<GURL> GetCustomLearnMoreUrl(const std::string& tag) const;

  // Returns whether a user justification is required for the given tag.
  bool BypassRequiresJustification(const std::string& tag) const;

  FilesRequestHandler* GetFilesRequestHandlerForTesting();

 protected:
  // For `block_until_verdict == 0`, the `destination_url` has to point to the
  // copied file/directory and not its parent. If it points to the parent, all
  // files within the destination directory are scanned.
  FileTransferAnalysisDelegate(safe_browsing::DeepScanAccessPoint access_point,
                               storage::FileSystemURL source_url,
                               storage::FileSystemURL destination_url,
                               Profile* profile,
                               storage::FileSystemContext* file_system_context,
                               AnalysisSettings settings);

 private:
  void OnGotFileURLs(std::vector<storage::FileSystemURL> source_urls);

  void ContentAnalysisCompleted(std::vector<RequestHandlerResult> results);

  AnalysisSettings settings_;
  raw_ptr<Profile> profile_;
  safe_browsing::DeepScanAccessPoint access_point_;
  std::vector<storage::FileSystemURL> scanning_urls_;
  storage::FileSystemURL source_url_;
  storage::FileSystemURL destination_url_;
  base::OnceClosure callback_;
  std::vector<RequestHandlerResult> results_;
  std::vector<size_t> warned_file_indices_;
  bool warning_is_bypassed_{false};

  std::unique_ptr<storage::RecursiveOperationDelegate> get_file_urls_delegate_;
  std::unique_ptr<FilesRequestHandler> request_handler_;

  base::WeakPtrFactory<FileTransferAnalysisDelegate> weak_ptr_factory_{this};
};
}  // namespace enterprise_connectors

#endif  // CHROME_BROWSER_ENTERPRISE_CONNECTORS_ANALYSIS_FILE_TRANSFER_ANALYSIS_DELEGATE_H_