| // Copyright 2012 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| // |
| // Helper class which handles communication with the SafeBrowsing servers for |
| // improved binary download protection. |
| |
| #ifndef CHROME_BROWSER_SAFE_BROWSING_DOWNLOAD_PROTECTION_DOWNLOAD_PROTECTION_SERVICE_H_ |
| #define CHROME_BROWSER_SAFE_BROWSING_DOWNLOAD_PROTECTION_DOWNLOAD_PROTECTION_SERVICE_H_ |
| |
| #include <stdint.h> |
| |
| #include <memory> |
| #include <set> |
| #include <string> |
| #include <vector> |
| |
| #include "base/callback_list.h" |
| #include "base/containers/flat_map.h" |
| #include "base/containers/flat_set.h" |
| #include "base/files/file_path.h" |
| #include "base/functional/callback.h" |
| #include "base/gtest_prod_util.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/memory/scoped_refptr.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/supports_user_data.h" |
| #include "base/types/optional_ref.h" |
| #include "chrome/browser/download/download_commands.h" |
| #include "chrome/browser/enterprise/connectors/common.h" |
| #include "chrome/browser/safe_browsing/download_protection/download_protection_delegate.h" |
| #include "chrome/browser/safe_browsing/download_protection/download_protection_observer.h" |
| #include "chrome/browser/safe_browsing/download_protection/download_protection_util.h" |
| #include "chrome/browser/safe_browsing/services_delegate.h" |
| #include "components/safe_browsing/core/browser/db/database_manager.h" |
| #include "components/safe_browsing/core/browser/safe_browsing_metrics_collector.h" |
| #include "components/safe_browsing/core/common/proto/csd.pb.h" |
| #include "components/sessions/core/session_id.h" |
| #include "url/gurl.h" |
| |
| #if !BUILDFLAG(IS_ANDROID) |
| #include "chrome/browser/safe_browsing/download_protection/deep_scanning_request.h" |
| #endif |
| |
| namespace content { |
| class PageNavigator; |
| struct FileSystemAccessWriteItem; |
| } // namespace content |
| |
| namespace download { |
| class DownloadItem; |
| } |
| |
| namespace network { |
| class SharedURLLoaderFactory; |
| } |
| |
| class Profile; |
| |
| namespace safe_browsing { |
| class BinaryFeatureExtractor; |
| class CheckClientDownloadRequest; |
| class CheckClientDownloadRequestBase; |
| class CheckFileSystemAccessWriteRequest; |
| class ClientDownloadRequest; |
| class DeepScanningRequest; |
| class DownloadRequestMaker; |
| class SafeBrowsingUIManager; |
| |
| #if !BUILDFLAG(IS_ANDROID) |
| class DownloadFeedbackService; |
| #endif |
| |
| // This class provides an asynchronous API to check whether a particular |
| // client download is malicious or not. |
| class DownloadProtectionService { |
| public: |
| // Creates a download service. The service is initially disabled. You need |
| // to call SetEnabled() to start it. `sb_service` owns this object via the |
| // ServicesDelegate. `delegate` must not be null. |
| DownloadProtectionService( |
| SafeBrowsingServiceImpl* sb_service, |
| std::unique_ptr<DownloadProtectionDelegate> delegate); |
| |
| // Same as above, but creates the default delegate instance. This is meant as |
| // a convenience for tests. Prefer the constructor above that explicitly |
| // provides the delegate, if possible. |
| explicit DownloadProtectionService(SafeBrowsingServiceImpl* sb_service); |
| |
| DownloadProtectionService(const DownloadProtectionService&) = delete; |
| DownloadProtectionService& operator=(const DownloadProtectionService&) = |
| delete; |
| |
| virtual ~DownloadProtectionService(); |
| |
| // Parse a flag of blocklisted sha256 hashes to check at each download. |
| // This is used for testing, to hunt for safe-browsing by-pass bugs. |
| virtual void ParseManualBlocklistFlag(); |
| |
| // Return true if this hash value is blocklisted via flag (for testing). |
| virtual bool IsHashManuallyBlocklisted(const std::string& sha256_hash) const; |
| |
| // Checks whether the given client download is likely to be malicious or not. |
| // The result is delivered asynchronously via the given callback. This |
| // method must be called on the UI thread, and the callback will also be |
| // invoked on the UI thread. This method must be called once the download |
| // is finished and written to disk. |
| virtual void CheckClientDownload( |
| download::DownloadItem* item, |
| CheckDownloadRepeatingCallback callback, |
| base::optional_ref<const std::string> password = std::nullopt); |
| |
| // Checks the user permissions, then calls |CheckClientDownload| if |
| // appropriate. Returns whether we may do more asynchronous work to check the |
| // download, such as sending a ping (potentially subject to sampling or other |
| // eligibility checks). If this returns true, the `callback` will be invoked |
| // with the result of a check. If this returns false, the `callback` will not |
| // be run. |
| virtual bool MaybeCheckClientDownload( |
| download::DownloadItem* item, |
| CheckDownloadRepeatingCallback callback); |
| |
| // Cancel the pending check for `item`. This function simply drops the pending |
| // work in the `DownloadProtectionService`. The caller is responsible for |
| // updating the download state so that it completes successfully. |
| void CancelChecksForDownload(download::DownloadItem* item); |
| |
| // Returns whether the download URL should be checked for safety based on user |
| // prefs. |
| virtual bool ShouldCheckDownloadUrl(download::DownloadItem* item); |
| |
| // Checks whether any of the URLs in the redirect chain of the |
| // download match the SafeBrowsing bad binary URL list. The result is |
| // delivered asynchronously via the given callback. This method must be |
| // called on the UI thread, and the callback will also be invoked on the UI |
| // thread. Pre-condition: !info.download_url_chain.empty(). |
| // The caller should check ShouldCheckDownloadUrl() beforehand; this is not |
| // verified in this method. |
| virtual void CheckDownloadUrl(download::DownloadItem* item, |
| CheckDownloadCallback callback); |
| |
| // Returns true if the download specified by |info| may be scanned by |
| // CheckClientDownload() for malicious content. (Caller may apply further |
| // logic to determine if a scan occurs, such as sampling or other checks.) |
| // May modify the DownloadItem with a SupportsUserData::Data. |
| virtual bool IsSupportedDownload(download::DownloadItem& item, |
| const base::FilePath& target_path) const; |
| |
| // If download protection is enabled for `item`, checks whether the given File |
| // System Access write operation is likely to be malicious or not. The result |
| // of the check is delivered asynchronously via the given callback, or the |
| // callback will be invoked with a result of UNKNOWN. This method must be |
| // called on the UI thread, and the callback will also be invoked on the UI |
| // thread. This method must be called once the write is finished and data has |
| // been written to disk. |
| virtual void CheckFileSystemAccessWrite( |
| std::unique_ptr<content::FileSystemAccessWriteItem> item, |
| CheckDownloadCallback callback); |
| |
| // Display more information to the user regarding the download specified by |
| // |info|. This method is invoked when the user requests more information |
| // about a download that was marked as malicious. |
| void ShowDetailsForDownload(const download::DownloadItem* item, |
| content::PageNavigator* navigator); |
| |
| // Enables or disables the service. This is usually called by the |
| // SafeBrowsingServiceImpl, which tracks whether any profile uses these |
| // services at all. Disabling causes any pending and future requests to have |
| // their callbacks called with "UNKNOWN" results. |
| void SetEnabled(bool enabled); |
| |
| bool enabled() const { return enabled_; } |
| |
| DownloadProtectionDelegate* delegate() { return delegate_.get(); } |
| const DownloadProtectionDelegate* delegate() const { return delegate_.get(); } |
| |
| // Returns the URL that will be contacted for download protection requests. |
| const GURL& GetDownloadRequestUrl() const; |
| |
| // Returns the timeout that is used by CheckClientDownload(). |
| base::TimeDelta GetDownloadRequestTimeout() const; |
| |
| // Checks the user permissions, and submits the downloaded file if |
| // appropriate. Returns whether the submission was successful. |
| bool MaybeBeginFeedbackForDownload(Profile* profile, |
| download::DownloadItem* download, |
| const std::string& ping_request, |
| const std::string& ping_response); |
| |
| // Registers a callback that will be run when a ClientDownloadRequest has |
| // been formed. |
| base::CallbackListSubscription RegisterClientDownloadRequestCallback( |
| const ClientDownloadRequestCallback& callback); |
| |
| // Registers a callback that will be run when a FileSystemAccessWriteRequest |
| // has been formed. |
| base::CallbackListSubscription RegisterFileSystemAccessWriteRequestCallback( |
| const FileSystemAccessWriteRequestCallback& callback); |
| |
| double allowlist_sample_rate() const; |
| |
| static void SetDownloadProtectionData( |
| download::DownloadItem* item, |
| const std::string& token, |
| const ClientDownloadResponse::Verdict& verdict, |
| const ClientDownloadResponse::TailoredVerdict& tailored_verdict); |
| |
| static std::string GetDownloadPingToken(const download::DownloadItem* item); |
| |
| // Whether a DownloadProtectionData is found on the item. |
| static bool HasDownloadProtectionVerdict(const download::DownloadItem* item); |
| |
| // Returns ClientDownloadResponse::SAFE by default if no |
| // DownloadProtectionData is found. |
| static ClientDownloadResponse::Verdict GetDownloadProtectionVerdict( |
| const download::DownloadItem* item); |
| |
| static ClientDownloadResponse::TailoredVerdict |
| GetDownloadProtectionTailoredVerdict(const download::DownloadItem* item); |
| |
| // Sends dangerous download opened report when download is opened or |
| // shown in folder. |
| void MaybeSendDangerousDownloadOpenedReport(download::DownloadItem* item, |
| bool show_download_in_folder); |
| |
| // Called to trigger a bypass event report for |download|. This is used when |
| // the async scan verdict is received for a file that was already opened by |
| // the user while it was being processed, and the verdict ended up being |
| // "dangerous" or "sensitive". |
| void ReportDelayedBypassEvent(download::DownloadItem* download, |
| download::DownloadDangerType danger_type); |
| |
| #if !BUILDFLAG(IS_ANDROID) |
| // Uploads `metadata` to Safe Browsing for deep scanning, using the upload |
| // service attached to the profile `item` was downloaded in. This is |
| // non-blocking, and the result we be provided through `callback`. `trigger` |
| // is used to identify the reason for deep scanning, aka enterprise policy or |
| // APP. `download_check_result` indicates the previously known SB verdict to |
| // apply to the download should deep scanning fail. `analysis_settings` |
| // contains settings to apply throughout scanning (types of scans to do, |
| // whether to block/allow large files, etc). This must be called on the UI |
| // thread. |
| void UploadForDeepScanning( |
| std::unique_ptr<DeepScanningMetadata> metadata, |
| CheckDownloadRepeatingCallback callback, |
| DownloadItemWarningData::DeepScanTrigger trigger, |
| DownloadCheckResult download_check_result, |
| enterprise_connectors::AnalysisSettings analysis_settings, |
| base::optional_ref<const std::string> password); |
| |
| // Helper functions for encrypted archive scans. |
| static void UploadForConsumerDeepScanning( |
| download::DownloadItem* item, |
| DownloadItemWarningData::DeepScanTrigger trigger, |
| base::optional_ref<const std::string> password); |
| static void CheckDownloadWithLocalDecryption( |
| download::DownloadItem* item, |
| base::optional_ref<const std::string> password); |
| |
| // Uploads a save package `item` for deep scanning. `save_package_file` |
| // contains a mapping of on-disk files part of that save package to their |
| // final paths. |
| void UploadSavePackageForDeepScanning( |
| download::DownloadItem* item, |
| base::flat_map<base::FilePath, base::FilePath> save_package_files, |
| CheckDownloadRepeatingCallback callback, |
| enterprise_connectors::AnalysisSettings analysis_settings); |
| |
| // Returns all the currently active deep scanning requests. |
| std::vector<DeepScanningRequest*> GetDeepScanningRequests(); |
| #endif // !BUILDFLAG(IS_ANDROID) |
| |
| virtual scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory( |
| content::BrowserContext* browser_context); |
| |
| // Removes all pending download requests that are associated with the |
| // `browser_context`. |
| void RemovePendingDownloadRequests(content::BrowserContext* browser_context); |
| |
| // Returns the maximum number of user gestures for a download referrer |
| // chain. If `item` is non-null, information about that download may |
| // change the limit. |
| static int GetDownloadAttributionUserGestureLimit( |
| download::DownloadItem* item = nullptr); |
| |
| private: |
| friend class DownloadUrlSBClient; |
| template <bool UseMockDbManager> |
| friend class DownloadProtectionServiceTestBase; |
| friend class DownloadDangerPromptTest; |
| friend class CheckClientDownloadRequestBase; |
| friend class CheckClientDownloadRequest; |
| friend class CheckFileSystemAccessWriteRequest; |
| friend class DownloadRequestMaker; |
| |
| #if !BUILDFLAG(IS_ANDROID) |
| friend class DeepScanningRequest; |
| #endif |
| |
| FRIEND_TEST_ALL_PREFIXES(DownloadProtectionServiceMockTimeTest, |
| TestDownloadRequestTimeout); |
| FRIEND_TEST_ALL_PREFIXES(DownloadProtectionServiceTest, |
| VerifyReferrerChainWithEmptyNavigationHistory); |
| FRIEND_TEST_ALL_PREFIXES(DownloadProtectionServiceTest, |
| VerifyReferrerChainLengthForExtendedReporting); |
| |
| static const void* const kDownloadProtectionDataKey; |
| |
| // Helper class for easy setting and getting data related to download |
| // protection. The data is only set when the server returns an unsafe verdict |
| // (i.e. not safe or unknown). |
| class DownloadProtectionData : public base::SupportsUserData::Data { |
| public: |
| explicit DownloadProtectionData( |
| const std::string& token, |
| const ClientDownloadResponse::Verdict& verdict, |
| const ClientDownloadResponse::TailoredVerdict& tailored_verdict) |
| : token_string_(token), |
| verdict_(verdict), |
| tailored_verdict_(tailored_verdict) {} |
| |
| DownloadProtectionData(const DownloadProtectionData&) = delete; |
| DownloadProtectionData& operator=(const DownloadProtectionData&) = delete; |
| |
| std::string token_string() { return token_string_; } |
| ClientDownloadResponse::Verdict verdict() { return verdict_; } |
| ClientDownloadResponse::TailoredVerdict tailored_verdict() { |
| return tailored_verdict_; |
| } |
| |
| private: |
| std::string token_string_; |
| ClientDownloadResponse::Verdict verdict_; |
| ClientDownloadResponse::TailoredVerdict tailored_verdict_; |
| }; |
| |
| // Cancels all requests in |download_requests_|, and empties it, releasing |
| // the references to the requests. |
| void CancelPendingRequests(); |
| |
| // Called by a CheckClientDownloadRequest instance when it finishes, to |
| // remove it from |download_requests_| and to report security sensitive |
| // events to safe_browsing_metrics_collector. |
| void RequestFinished(CheckClientDownloadRequestBase* request, |
| content::BrowserContext* browser_context, |
| DownloadCheckResult result); |
| |
| #if !BUILDFLAG(IS_ANDROID) |
| // Called by a DeepScanningRequest when it finishes, to remove it from |
| // |deep_scanning_requests_|. |
| virtual void RequestFinished(DeepScanningRequest* request); |
| #endif |
| |
| // Routes the dangerous download opened reports to `SafeBrowsingEventRouter` |
| // and `ReportingEventRouter`, if available. |
| void OnDangerousDownloadOpened(download::DownloadItem* item, |
| Profile* profile); |
| |
| // Sends sensitive file bypassed event via `ReportingEventRouter`. |
| void ReportSensitiveFileBypassEnterpriseEvent( |
| download::DownloadItem* item, |
| Profile* profile, |
| const enterprise_connectors::FileMetadata& metadata, |
| const enterprise_connectors::ContentAnalysisResponse::Result& result, |
| const google::protobuf::RepeatedPtrField<ReferrerChainEntry>& |
| referrer_chain, |
| const google::protobuf::RepeatedPtrField<std::string>& frame_urls); |
| |
| // Sends dangerous download opened event via `ReportingEventRouter`. |
| void ReportDangerousDownloadOpenedEnterpriseEvent( |
| download::DownloadItem* item, |
| Profile* profile, |
| const enterprise_connectors::FileMetadata& metadata, |
| const google::protobuf::RepeatedPtrField<ReferrerChainEntry>& |
| referrer_chain, |
| const google::protobuf::RepeatedPtrField<std::string>& frame_urls); |
| void ReportDangerousDownloadOpenedEnterpriseEvent( |
| download::DownloadItem* item, |
| Profile* profile, |
| const google::protobuf::RepeatedPtrField<ReferrerChainEntry>& |
| referrer_chain, |
| const google::protobuf::RepeatedPtrField<std::string>& frame_urls); |
| |
| // Sends dangerous download opened event via `SafeBrowsingPrivateEventRouter`. |
| void ReportDangerousDownloadOpenedSafeBrowsingEvent( |
| download::DownloadItem* item, |
| Profile* profile, |
| const enterprise_connectors::FileMetadata& metadata); |
| void ReportDangerousDownloadOpenedSafeBrowsingEvent( |
| download::DownloadItem* item, |
| Profile* profile); |
| |
| #if !BUILDFLAG(IS_ANDROID) |
| // Get the BinaryUploadService for the given |profile|. Virtual so it can be |
| // overridden in tests. |
| virtual BinaryUploadService* GetBinaryUploadService( |
| Profile* profile, |
| const enterprise_connectors::AnalysisSettings& settings); |
| #endif |
| |
| // Callback when deep scanning has finished, but we may want to do the |
| // metadata check anyway. |
| void MaybeCheckMetadataAfterDeepScanning( |
| download::DownloadItem* item, |
| CheckDownloadRepeatingCallback callback, |
| DownloadCheckResult result); |
| |
| raw_ptr<SafeBrowsingServiceImpl> sb_service_ = nullptr; |
| // Delegate providing platform-specific logic. Never null. |
| std::unique_ptr<DownloadProtectionDelegate> delegate_; |
| // These pointers may be NULL if SafeBrowsing is disabled. |
| scoped_refptr<SafeBrowsingUIManager> ui_manager_; |
| scoped_refptr<SafeBrowsingDatabaseManager> database_manager_; |
| |
| // Set of pending server requests for DownloadManager mediated downloads. |
| base::flat_map< |
| content::BrowserContext*, |
| base::flat_map<CheckClientDownloadRequestBase*, |
| std::unique_ptr<CheckClientDownloadRequestBase>>> |
| context_download_requests_; |
| |
| #if !BUILDFLAG(IS_ANDROID) |
| // Set of pending server requests for deep scanning. |
| base::flat_set<std::unique_ptr<DeepScanningRequest>> deep_scanning_requests_; |
| #endif |
| |
| // Keeps track of the state of the service. |
| bool enabled_ = false; |
| |
| // BinaryFeatureExtractor object, may be overridden for testing. |
| scoped_refptr<BinaryFeatureExtractor> binary_feature_extractor_; |
| |
| #if !BUILDFLAG(IS_ANDROID) |
| std::unique_ptr<DownloadFeedbackService> feedback_service_; |
| #endif |
| |
| // A list of callbacks to be run on the main thread when a |
| // ClientDownloadRequest has been formed. |
| ClientDownloadRequestCallbackList client_download_request_callbacks_; |
| |
| // A list of callbacks to be run on the main thread when a |
| // FileSystemAccessWriteRequest has been formed. |
| FileSystemAccessWriteRequestCallbackList |
| file_system_access_write_request_callbacks_; |
| |
| // List of 8-byte hashes that are blocklisted manually by flag. |
| // Normally empty. |
| std::set<std::string> manual_blocklist_hashes_; |
| |
| // Rate of allowlisted downloads we sample to send out download ping. |
| // Overrides the value provided by the delegate. Intended for testing only. |
| std::optional<double> allowlist_sample_rate_; |
| |
| // DownloadProtectionObserver to send real time reports for dangerous download |
| // events and handle special user actions on the download. |
| DownloadProtectionObserver download_protection_observer_; |
| |
| base::WeakPtrFactory<DownloadProtectionService> weak_ptr_factory_; |
| }; |
| |
| } // namespace safe_browsing |
| |
| #endif // CHROME_BROWSER_SAFE_BROWSING_DOWNLOAD_PROTECTION_DOWNLOAD_PROTECTION_SERVICE_H_ |