910e62b5创建于 1月15日历史提交
// 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_DOWNLOAD_DOWNLOAD_ITEM_WARNING_DATA_H_
#define CHROME_BROWSER_DOWNLOAD_DOWNLOAD_ITEM_WARNING_DATA_H_

#include <optional>
#include <vector>

#include "base/supports_user_data.h"
#include "base/time/time.h"
#include "components/safe_browsing/core/common/proto/csd.pb.h"

namespace download {
class DownloadItem;
}

// Per DownloadItem data for storing warning events on download warnings. The
// data is only set if a warning is shown. These events are added to Safe
// Browsing reports.
class DownloadItemWarningData : public base::SupportsUserData::Data {
 public:
  // The surface that the warning is shown. See
  // go/chrome-download-warning-surfaces for details.
  // These values are persisted to logs. Entries should not be renumbered and
  // numeric values should never be reused.
  enum class WarningSurface {
    // Applicable actions: DISCARD, OPEN_SUBPAGE
    BUBBLE_MAINPAGE = 1,
    // Applicable actions: PROCEED, DISCARD, DISMISS, CLOSE, BACK,
    // PROCEED_DEEP_SCAN, ACCEPT_DEEP_SCAN, OPEN_LEARN_MORE_LINK
    BUBBLE_SUBPAGE = 2,
    // Applicable actions: DISCARD, KEEP, PROCEED, ACCEPT_DEEP_SCAN
    // PROCEED on the downloads page indicates saving a "suspicious" download
    // directly, without going through the prompt. In contrast, KEEP indicates
    // opening the prompt, for a "dangerous" download.
    DOWNLOADS_PAGE = 3,
    // Applicable actions: PROCEED, CANCEL
    DOWNLOAD_PROMPT = 4,
    // Applicable actions: OPEN_SUBPAGE
    // Note: This is only used on Lacros. DownloadItemWarningData is only
    // applied for v2 notifications on ChromeOS Lacros, not for the legacy
    // ChromeOS notifications used on ChromeOS Ash and on Lacros pre-v2. Other
    // platforms do not have desktop notifications for downloads.
    // TODO(chlily): CLOSE should be logged as well but there is currently no
    // way to tell when a download is dangerous on the Ash side, which handles
    // the notification close.
    DOWNLOAD_NOTIFICATION = 5,
    kMaxValue = DOWNLOAD_NOTIFICATION
  };

  // Users action on the warning surface.
  // These values are persisted to logs. Entries should not be renumbered and
  // numeric values should never be reused.
  enum class WarningAction {
    // The warning is shown. This is a special action that may not be triggered
    // by user. We will use the first instance of this action as the anchor to
    // track the latency of other actions.
    SHOWN = 0,
    // The user clicks proceed, which means the user decides to bypass the
    // warning. This is a terminal action.
    // Note that this corresponds to DownloadCommands::Command::KEEP, despite
    // the confusing naming.
    PROCEED = 1,
    // The user clicks discard, which means the user decides to obey the
    // warning and the dangerous download is deleted from disk.
    DISCARD = 2,
    // The user has clicked the keep button on the surface, which causes another
    // surface (e.g. download prompt) to be displayed. This is not a terminal
    // action.
    KEEP = 3,
    // The user has clicked the close button on the surface.
    CLOSE = 4,
    // The user clicks cancel on the download prompt.
    CANCEL = 5,
    // The user has dismissed the bubble by clicking anywhere outside
    // the bubble.
    DISMISS = 6,
    // The user has clicked the back button on the bubble subpage to go back
    // to the bubble main page.
    BACK = 7,
    // The user has opened the download bubble subpage.
    OPEN_SUBPAGE = 8,
    // The user clicks proceed on a prompt for deep scanning.
    PROCEED_DEEP_SCAN = 9,
    // The user clicks the learn more link on the bubble subpage.
    OPEN_LEARN_MORE_LINK = 10,
    // The user accepts starting a deep scan.
    ACCEPT_DEEP_SCAN = 11,
    kMaxValue = ACCEPT_DEEP_SCAN
  };

  struct WarningActionEvent {
    WarningSurface surface;
    WarningAction action;
    // The latency between when the warning is shown for the first time and when
    // this event has happened.
    int64_t action_latency_msec;
    // A terminal action means that the warning disappears after this event,
    // the download is either deleted or saved.
    bool is_terminal_action = false;

    WarningActionEvent(WarningSurface surface,
                       WarningAction action,
                       int64_t action_latency_msec,
                       bool is_terminal_action);

    // Serializes the surface, action, and action_latency_msec into a string
    // in a colon-separated format such as "DOWNLOADS_PAGE:DISCARD:10000".
    std::string ToString() const;
  };

  // Enum representing the trigger of the scan request.
  // These values are persisted to logs. Entries should not be renumbered and
  // numeric values should never be reused.
  // LINT.IfChange
  enum class DeepScanTrigger {
    // The trigger is unknown.
    TRIGGER_UNKNOWN = 0,

    // The trigger is the standard prompt in the download bubble,
    // shown for Advanced Protection or Enhanced Protection users.
    TRIGGER_CONSUMER_PROMPT = 1,

    // The trigger is the enterprise policy.
    TRIGGER_POLICY = 2,

    // The trigger is the encrypted archive prompt in the download
    // bubble, which requires the password as well as the file.
    TRIGGER_ENCRYPTED_CONSUMER_PROMPT = 3,

    // The trigger is automatic deep scanning, with no prompt, which
    // applies only to Enhanced Protection users.
    TRIGGER_IMMEDIATE_DEEP_SCAN = 4,

    kMaxValue = TRIGGER_IMMEDIATE_DEEP_SCAN,
  };
  // LINT.ThenChange(/tools/metrics/histograms/metadata/sb_client/enums.xml)

  ~DownloadItemWarningData() override;

  // Gets all warning actions associated with this `download`. Returns an
  // empty vector if there's no warning data or there is no warning shown for
  // this `download`.
  static std::vector<WarningActionEvent> GetWarningActionEvents(
      const download::DownloadItem* download);

  // Adds an `action` triggered on `surface` for `download`. It may not be
  // added if `download` is not dangerous, null or the length of events
  // associated with this `download` exceeds the limit.
  static void AddWarningActionEvent(download::DownloadItem* download,
                                    WarningSurface surface,
                                    WarningAction action);

  // Returns whether the download was an encrypted archive at the
  // top-level (i.e. the encryption was not within a nested archive).
  static bool IsTopLevelEncryptedArchive(
      const download::DownloadItem* download);
  static void SetIsTopLevelEncryptedArchive(
      download::DownloadItem* download,
      bool is_top_level_encrypted_archive);

  // Returns whether the user has entered an incorrect password for the
  // archive.
  static bool HasIncorrectPassword(const download::DownloadItem* download);
  static void SetHasIncorrectPassword(download::DownloadItem* download,
                                      bool has_incorrect_password);

  // Converts an `event` to the Safe Browsing report proto format.
  static safe_browsing::ClientSafeBrowsingReportRequest::DownloadWarningAction
  ConstructCsbrrDownloadWarningAction(const WarningActionEvent& event);

  // Returns whether we have shown a local password decryption prompt for this
  // download.
  static bool HasShownLocalDecryptionPrompt(
      const download::DownloadItem* download);
  static void SetHasShownLocalDecryptionPrompt(download::DownloadItem* download,
                                               bool has_shown);

  // Returns the reason we initiated deep scanning for the download.
  static DeepScanTrigger DownloadDeepScanTrigger(
      const download::DownloadItem* download);
  static void SetDeepScanTrigger(download::DownloadItem* download,
                                 DeepScanTrigger trigger);

  // Returns whether an encrypted archive was fully extracted.
  static bool IsFullyExtractedArchive(const download::DownloadItem* download);
  static void SetIsFullyExtractedArchive(download::DownloadItem* download,
                                         bool extracted);

  // Time and surface of the first SHOWN event. Time will be null if SHOWN has
  // not yet been logged. Surface will return nullopt if SHOWN has not yet been
  // logged.
  static base::Time WarningFirstShownTime(
      const download::DownloadItem* download);
  static std::optional<WarningSurface> WarningFirstShownSurface(
      const download::DownloadItem* download);

 private:
  DownloadItemWarningData();

  template <typename F, typename V>
  static V GetWithDefault(const download::DownloadItem* download,
                          F&& f,
                          V&& default_value);
  static DownloadItemWarningData* GetOrCreate(download::DownloadItem* download);

  std::vector<WarningActionEvent> ActionEvents() const;

  static const char kKey[];

  base::Time warning_first_shown_time_;
  std::optional<WarningSurface> warning_first_shown_surface_ = std::nullopt;
  std::vector<WarningActionEvent> action_events_;
  bool is_top_level_encrypted_archive_ = false;
  bool has_incorrect_password_ = false;
  bool has_shown_local_decryption_prompt_ = false;
  bool fully_extracted_archive_ = false;
  // Whether a "shown" event has been logged for the Downloads Page for this
  // download. Not persisted across restarts.
  bool logged_downloads_page_shown_ = false;
  DeepScanTrigger deep_scan_trigger_ = DeepScanTrigger::TRIGGER_UNKNOWN;
};

#endif  // CHROME_BROWSER_DOWNLOAD_DOWNLOAD_ITEM_WARNING_DATA_H_