blob: c9548626f2b21473988c2301dfe5b64426de2f2b [file] [log] [blame]
// 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_