| // Copyright 2018 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.h" |
| |
| #include <utility> |
| #include <vector> |
| |
| #include "base/bind.h" |
| #include "base/callback_forward.h" |
| #include "base/optional.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/time/time.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/chrome_content_browser_client.h" |
| #include "chrome/browser/policy/browser_dm_token_storage.h" |
| #include "chrome/browser/policy/chrome_browser_cloud_management_controller.h" |
| #include "chrome/browser/policy/chrome_browser_policy_connector.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/profiles/profile_attributes_entry.h" |
| #include "chrome/browser/profiles/profile_attributes_storage.h" |
| #include "chrome/browser/profiles/profile_manager.h" |
| #include "chrome/browser/profiles/reporting_util.h" |
| #include "chrome/browser/safe_browsing/download_protection/binary_upload_service.h" |
| #include "chrome/browser/safe_browsing/safe_browsing_service.h" |
| #include "chrome/browser/signin/identity_manager_factory.h" |
| #include "chrome/common/extensions/api/safe_browsing_private.h" |
| #include "components/policy/core/common/cloud/cloud_policy_client.h" |
| #include "components/policy/core/common/cloud/device_management_service.h" |
| #include "components/policy/core/common/cloud/realtime_reporting_job_configuration.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/safe_browsing/common/safe_browsing_prefs.h" |
| #include "components/safe_browsing/proto/webprotect.pb.h" |
| #include "components/signin/public/identity_manager/identity_manager.h" |
| #include "content/public/browser/browser_context.h" |
| #include "extensions/browser/event_router.h" |
| #include "services/network/public/cpp/shared_url_loader_factory.h" |
| #include "url/gurl.h" |
| |
| namespace extensions { |
| |
| const base::Feature SafeBrowsingPrivateEventRouter::kRealtimeReportingFeature{ |
| "SafeBrowsingRealtimeReporting", base::FEATURE_DISABLED_BY_DEFAULT}; |
| |
| // Key names used with when building the dictionary to pass to the real-time |
| // reporting API. |
| const char SafeBrowsingPrivateEventRouter::kKeyUrl[] = "url"; |
| const char SafeBrowsingPrivateEventRouter::kKeyUserName[] = "userName"; |
| const char SafeBrowsingPrivateEventRouter::kKeyIsPhishingUrl[] = |
| "isPhishingUrl"; |
| const char SafeBrowsingPrivateEventRouter::kKeyProfileUserName[] = |
| "profileUserName"; |
| const char SafeBrowsingPrivateEventRouter::kKeyFileName[] = "fileName"; |
| const char SafeBrowsingPrivateEventRouter::kKeyDownloadDigestSha256[] = |
| "downloadDigestSha256"; |
| const char SafeBrowsingPrivateEventRouter::kKeyReason[] = "reason"; |
| const char SafeBrowsingPrivateEventRouter::kKeyNetErrorCode[] = "netErrorCode"; |
| const char SafeBrowsingPrivateEventRouter::kKeyClickedThrough[] = |
| "clickedThrough"; |
| const char SafeBrowsingPrivateEventRouter::kKeyTriggeredRules[] = |
| "triggeredRules"; |
| const char SafeBrowsingPrivateEventRouter::kKeyThreatType[] = "threatType"; |
| const char SafeBrowsingPrivateEventRouter::kKeyContentType[] = "contentType"; |
| const char SafeBrowsingPrivateEventRouter::kKeyContentSize[] = "contentSize"; |
| const char SafeBrowsingPrivateEventRouter::kKeyTrigger[] = "trigger"; |
| |
| const char SafeBrowsingPrivateEventRouter::kKeyPasswordReuseEvent[] = |
| "passwordReuseEvent"; |
| const char SafeBrowsingPrivateEventRouter::kKeyPasswordChangedEvent[] = |
| "passwordChangedEvent"; |
| const char SafeBrowsingPrivateEventRouter::kKeyDangerousDownloadEvent[] = |
| "dangerousDownloadEvent"; |
| const char SafeBrowsingPrivateEventRouter::kKeyInterstitialEvent[] = |
| "interstitialEvent"; |
| const char SafeBrowsingPrivateEventRouter::kKeySensitiveDataEvent[] = |
| "sensitiveDataEvent"; |
| const char SafeBrowsingPrivateEventRouter::kKeyLargeUnscannedFileEvent[] = |
| "largeUnscannedFileEvent"; |
| |
| const char SafeBrowsingPrivateEventRouter::kTriggerFileDownload[] = |
| "FILE_DOWNLOAD"; |
| const char SafeBrowsingPrivateEventRouter::kTriggerFileUpload[] = "FILE_UPLOAD"; |
| const char SafeBrowsingPrivateEventRouter::kTriggerWebContentUpload[] = |
| "WEB_CONTENT_UPLOAD"; |
| |
| SafeBrowsingPrivateEventRouter::SafeBrowsingPrivateEventRouter( |
| content::BrowserContext* context) |
| : context_(context) { |
| event_router_ = EventRouter::Get(context_); |
| |
| // g_browser_process and/or g_browser_process->local_state() may be null |
| // in tests. |
| if (g_browser_process && g_browser_process->local_state()) { |
| RealtimeReportingPrefChanged(std::string()); |
| registrar_.Init(g_browser_process->local_state()); |
| registrar_.Add( |
| prefs::kUnsafeEventsReportingEnabled, |
| base::BindRepeating( |
| &SafeBrowsingPrivateEventRouter::RealtimeReportingPrefChanged, |
| base::Unretained(this))); |
| } |
| } |
| |
| SafeBrowsingPrivateEventRouter::~SafeBrowsingPrivateEventRouter() {} |
| |
| void SafeBrowsingPrivateEventRouter::OnPolicySpecifiedPasswordReuseDetected( |
| const GURL& url, |
| const std::string& user_name, |
| bool is_phishing_url) { |
| api::safe_browsing_private::PolicySpecifiedPasswordReuse params; |
| params.url = url.spec(); |
| params.user_name = user_name; |
| params.is_phishing_url = is_phishing_url; |
| |
| // |event_router_| can be null in tests. |
| if (event_router_) { |
| auto event_value = std::make_unique<base::ListValue>(); |
| event_value->Append(params.ToValue()); |
| |
| auto extension_event = std::make_unique<Event>( |
| events:: |
| SAFE_BROWSING_PRIVATE_ON_POLICY_SPECIFIED_PASSWORD_REUSE_DETECTED, |
| api::safe_browsing_private::OnPolicySpecifiedPasswordReuseDetected:: |
| kEventName, |
| std::move(event_value)); |
| event_router_->BroadcastEvent(std::move(extension_event)); |
| } |
| |
| if (!IsRealtimeReportingEnabled()) |
| return; |
| |
| ReportRealtimeEvent( |
| kKeyPasswordReuseEvent, |
| base::BindOnce( |
| [](const std::string& url, const std::string& user_name, |
| const bool is_phishing_url, const std::string& profile_user_name) { |
| // Convert |params| to a real-time event dictionary |
| // and report it. |
| base::Value event(base::Value::Type::DICTIONARY); |
| event.SetStringKey(kKeyUrl, url); |
| event.SetStringKey(kKeyUserName, user_name); |
| event.SetBoolKey(kKeyIsPhishingUrl, is_phishing_url); |
| event.SetStringKey(kKeyProfileUserName, profile_user_name); |
| return event; |
| }, |
| params.url, params.user_name, params.is_phishing_url, |
| GetProfileUserName())); |
| } |
| |
| void SafeBrowsingPrivateEventRouter::OnPolicySpecifiedPasswordChanged( |
| const std::string& user_name) { |
| // |event_router_| can be null in tests. |
| if (event_router_) { |
| auto event_value = std::make_unique<base::ListValue>(); |
| event_value->Append(std::make_unique<base::Value>(user_name)); |
| auto extension_event = std::make_unique<Event>( |
| events::SAFE_BROWSING_PRIVATE_ON_POLICY_SPECIFIED_PASSWORD_CHANGED, |
| api::safe_browsing_private::OnPolicySpecifiedPasswordChanged:: |
| kEventName, |
| std::move(event_value)); |
| event_router_->BroadcastEvent(std::move(extension_event)); |
| } |
| |
| if (!IsRealtimeReportingEnabled()) |
| return; |
| |
| ReportRealtimeEvent(kKeyPasswordChangedEvent, |
| base::BindOnce( |
| [](const std::string& user_name, |
| const std::string& profile_user_name) { |
| // Convert |params| to a real-time event dictionary |
| // and report it. |
| base::Value event(base::Value::Type::DICTIONARY); |
| event.SetStringKey(kKeyUserName, user_name); |
| event.SetStringKey(kKeyProfileUserName, |
| profile_user_name); |
| return event; |
| }, |
| user_name, GetProfileUserName())); |
| } |
| |
| void SafeBrowsingPrivateEventRouter::OnDangerousDownloadOpened( |
| const GURL& url, |
| const std::string& file_name, |
| const std::string& download_digest_sha256, |
| const std::string& mime_type, |
| const int64_t content_size) { |
| api::safe_browsing_private::DangerousDownloadInfo params; |
| params.url = url.spec(); |
| params.file_name = file_name; |
| params.download_digest_sha256 = download_digest_sha256; |
| params.user_name = GetProfileUserName(); |
| |
| // |event_router_| can be null in tests. |
| if (event_router_) { |
| auto event_value = std::make_unique<base::ListValue>(); |
| event_value->Append(params.ToValue()); |
| |
| auto extension_event = std::make_unique<Event>( |
| events::SAFE_BROWSING_PRIVATE_ON_DANGEROUS_DOWNLOAD_OPENED, |
| api::safe_browsing_private::OnDangerousDownloadOpened::kEventName, |
| std::move(event_value)); |
| event_router_->BroadcastEvent(std::move(extension_event)); |
| } |
| |
| if (!IsRealtimeReportingEnabled()) |
| return; |
| |
| ReportRealtimeEvent( |
| kKeyDangerousDownloadEvent, |
| base::BindOnce( |
| [](const std::string& url, const std::string& file_name, |
| const std::string& download_digest_sha256, |
| const std::string& user_name, const std::string& mime_type, |
| const int64_t content_size) { |
| // Convert |params| to a real-time event dictionary and report it. |
| base::Value event(base::Value::Type::DICTIONARY); |
| event.SetStringKey(kKeyUrl, url); |
| event.SetStringKey(kKeyFileName, file_name); |
| event.SetStringKey(kKeyDownloadDigestSha256, |
| download_digest_sha256); |
| event.SetStringKey(kKeyProfileUserName, user_name); |
| event.SetStringKey(kKeyContentType, mime_type); |
| // |content_size| can be set to -1 to indicate an unknown size, in |
| // which case the field is not set. |
| if (content_size >= 0) |
| event.SetIntKey(kKeyContentSize, content_size); |
| event.SetStringKey(kKeyTrigger, kTriggerFileDownload); |
| return event; |
| }, |
| params.url, params.file_name, params.download_digest_sha256, |
| params.user_name, mime_type, content_size)); |
| } |
| |
| void SafeBrowsingPrivateEventRouter::OnSecurityInterstitialShown( |
| const GURL& url, |
| const std::string& reason, |
| int net_error_code) { |
| api::safe_browsing_private::InterstitialInfo params; |
| params.url = url.spec(); |
| params.reason = reason; |
| if (net_error_code < 0) { |
| params.net_error_code = |
| std::make_unique<std::string>(base::NumberToString(net_error_code)); |
| } |
| params.user_name = GetProfileUserName(); |
| |
| // |event_router_| can be null in tests. |
| if (event_router_) { |
| auto event_value = std::make_unique<base::ListValue>(); |
| event_value->Append(params.ToValue()); |
| |
| auto extension_event = std::make_unique<Event>( |
| events::SAFE_BROWSING_PRIVATE_ON_SECURITY_INTERSTITIAL_SHOWN, |
| api::safe_browsing_private::OnSecurityInterstitialShown::kEventName, |
| std::move(event_value)); |
| event_router_->BroadcastEvent(std::move(extension_event)); |
| } |
| |
| if (!IsRealtimeReportingEnabled()) |
| return; |
| |
| ReportRealtimeEvent( |
| kKeyInterstitialEvent, |
| base::BindOnce( |
| [](const std::string& url, const std::string& reason, |
| int net_error_code, const std::string& user_name) { |
| // Convert |params| to a real-time event dictionary and report it. |
| base::Value event(base::Value::Type::DICTIONARY); |
| event.SetStringKey(kKeyUrl, url); |
| event.SetStringKey(kKeyReason, reason); |
| event.SetIntKey(kKeyNetErrorCode, net_error_code); |
| event.SetStringKey(kKeyProfileUserName, user_name); |
| event.SetBoolKey(kKeyClickedThrough, false); |
| return event; |
| }, |
| params.url, params.reason, net_error_code, params.user_name)); |
| } |
| |
| void SafeBrowsingPrivateEventRouter::OnSecurityInterstitialProceeded( |
| const GURL& url, |
| const std::string& reason, |
| int net_error_code) { |
| api::safe_browsing_private::InterstitialInfo params; |
| params.url = url.spec(); |
| params.reason = reason; |
| if (net_error_code < 0) { |
| params.net_error_code = |
| std::make_unique<std::string>(base::NumberToString(net_error_code)); |
| } |
| params.user_name = GetProfileUserName(); |
| |
| // |event_router_| can be null in tests. |
| if (event_router_) { |
| auto event_value = std::make_unique<base::ListValue>(); |
| event_value->Append(params.ToValue()); |
| |
| auto extension_event = std::make_unique<Event>( |
| events::SAFE_BROWSING_PRIVATE_ON_SECURITY_INTERSTITIAL_PROCEEDED, |
| api::safe_browsing_private::OnSecurityInterstitialProceeded::kEventName, |
| std::move(event_value)); |
| event_router_->BroadcastEvent(std::move(extension_event)); |
| } |
| |
| if (!IsRealtimeReportingEnabled()) |
| return; |
| |
| ReportRealtimeEvent( |
| kKeyInterstitialEvent, |
| base::BindOnce( |
| [](const std::string& url, const std::string& reason, |
| int net_error_code, const std::string& user_name) { |
| // Convert |params| to a real-time event dictionary and report it. |
| base::Value event(base::Value::Type::DICTIONARY); |
| event.SetStringKey(kKeyUrl, url); |
| event.SetStringKey(kKeyReason, reason); |
| event.SetIntKey(kKeyNetErrorCode, net_error_code); |
| event.SetStringKey(kKeyProfileUserName, user_name); |
| event.SetBoolKey(kKeyClickedThrough, true); |
| return event; |
| }, |
| params.url, params.reason, net_error_code, params.user_name)); |
| } |
| |
| void SafeBrowsingPrivateEventRouter::OnDangerousDeepScanningResult( |
| const GURL& url, |
| const std::string& file_name, |
| const std::string& download_digest_sha256, |
| const std::string& threat_type, |
| const std::string& mime_type, |
| const std::string& trigger, |
| const int64_t content_size) { |
| if (!IsRealtimeReportingEnabled()) |
| return; |
| |
| ReportRealtimeEvent( |
| kKeyDangerousDownloadEvent, |
| base::BindOnce( |
| [](const std::string& url, const std::string& file_name, |
| const std::string& download_digest_sha256, |
| const std::string& profile_user_name, |
| const std::string& threat_type, const std::string& mime_type, |
| const std::string& trigger, const int64_t content_size) { |
| // Create a real-time event dictionary from the arguments and |
| // report it. |
| base::Value event(base::Value::Type::DICTIONARY); |
| event.SetStringKey(kKeyUrl, url); |
| event.SetStringKey(kKeyFileName, file_name); |
| event.SetStringKey(kKeyDownloadDigestSha256, |
| download_digest_sha256); |
| event.SetStringKey(kKeyProfileUserName, profile_user_name); |
| event.SetStringKey(kKeyThreatType, threat_type); |
| event.SetStringKey(kKeyContentType, mime_type); |
| // |content_size| can be set to -1 to indicate an unknown size, in |
| // which case the field is not set. |
| if (content_size >= 0) |
| event.SetIntKey(kKeyContentSize, content_size); |
| event.SetStringKey(kKeyTrigger, trigger); |
| return event; |
| }, |
| url.spec(), file_name, download_digest_sha256, GetProfileUserName(), |
| threat_type, mime_type, trigger, content_size)); |
| } |
| |
| void SafeBrowsingPrivateEventRouter::OnSensitiveDataEvent( |
| const safe_browsing::DlpDeepScanningVerdict& verdict, |
| const GURL& url, |
| const std::string& file_name, |
| const std::string& download_digest_sha256, |
| const std::string& mime_type, |
| const std::string& trigger, |
| const int64_t content_size) { |
| if (!IsRealtimeReportingEnabled()) |
| return; |
| |
| ReportRealtimeEvent( |
| kKeySensitiveDataEvent, |
| base::BindOnce( |
| [](const safe_browsing::DlpDeepScanningVerdict& verdict, |
| const std::string& url, const std::string& file_name, |
| const std::string& download_digest_sha256, |
| const std::string& profile_user_name, const std::string& mime_type, |
| const std::string& trigger, const int64_t content_size) { |
| // Create a real-time event dictionary from the arguments and |
| // report it. |
| base::Value event(base::Value::Type::DICTIONARY); |
| event.SetStringKey(kKeyUrl, url); |
| event.SetStringKey(kKeyFileName, file_name); |
| event.SetStringKey(kKeyDownloadDigestSha256, |
| download_digest_sha256); |
| event.SetStringKey(kKeyProfileUserName, profile_user_name); |
| event.SetStringKey(kKeyContentType, mime_type); |
| // |content_size| can be set to -1 to indicate an unknown size, in |
| // which case the field is not set. |
| if (content_size >= 0) |
| event.SetIntKey(kKeyContentSize, content_size); |
| event.SetStringKey(kKeyTrigger, trigger); |
| |
| base::ListValue triggered_rules; |
| for (auto rule : verdict.triggered_rules()) { |
| triggered_rules.AppendString(rule.rule_name()); |
| } |
| event.SetKey(kKeyTriggeredRules, std::move(triggered_rules)); |
| return event; |
| }, |
| verdict, url.spec(), file_name, download_digest_sha256, |
| GetProfileUserName(), mime_type, trigger, content_size)); |
| } |
| |
| void SafeBrowsingPrivateEventRouter::OnLargeUnscannedFileEvent( |
| const GURL& url, |
| const std::string& file_name, |
| const std::string& download_digest_sha256, |
| const std::string& mime_type, |
| const std::string& trigger, |
| const int64_t content_size) { |
| if (!IsRealtimeReportingEnabled()) |
| return; |
| |
| ReportRealtimeEvent( |
| kKeyLargeUnscannedFileEvent, |
| base::BindOnce( |
| [](const std::string& url, const std::string& file_name, |
| const std::string& download_digest_sha256, |
| const std::string& profile_user_name, const std::string& mime_type, |
| const std::string& trigger, const int64_t content_size) { |
| // Create a real-time event dictionary from the arguments and |
| // report it. |
| base::Value event(base::Value::Type::DICTIONARY); |
| event.SetStringKey(kKeyUrl, url); |
| event.SetStringKey(kKeyFileName, file_name); |
| event.SetStringKey(kKeyDownloadDigestSha256, |
| download_digest_sha256); |
| event.SetStringKey(kKeyProfileUserName, profile_user_name); |
| event.SetStringKey(kKeyContentType, mime_type); |
| // |content_size| can be set to -1 to indicate an unknown size, in |
| // which case the field is not set. |
| if (content_size >= 0) |
| event.SetIntKey(kKeyContentSize, content_size); |
| event.SetStringKey(kKeyTrigger, trigger); |
| return event; |
| }, |
| url.spec(), file_name, download_digest_sha256, GetProfileUserName(), |
| mime_type, trigger, content_size)); |
| } |
| |
| void SafeBrowsingPrivateEventRouter::OnDangerousDownloadWarning( |
| const GURL& url, |
| const std::string& file_name, |
| const std::string& download_digest_sha256, |
| const std::string& threat_type, |
| const std::string& mime_type, |
| const int64_t content_size) { |
| if (!IsRealtimeReportingEnabled()) |
| return; |
| |
| ReportRealtimeEvent( |
| kKeyDangerousDownloadEvent, |
| base::BindOnce( |
| [](const std::string& url, const std::string& file_name, |
| const std::string& download_digest_sha256, |
| const std::string& profile_user_name, |
| const std::string& threat_type, const std::string& mime_type, |
| const int64_t content_size) { |
| // Create a real-time event dictionary and report it. |
| base::Value event(base::Value::Type::DICTIONARY); |
| event.SetStringKey(kKeyUrl, url); |
| event.SetStringKey(kKeyFileName, file_name); |
| event.SetStringKey(kKeyDownloadDigestSha256, |
| download_digest_sha256); |
| event.SetStringKey(kKeyProfileUserName, profile_user_name); |
| event.SetStringKey(kKeyThreatType, threat_type); |
| event.SetBoolKey(kKeyClickedThrough, false); |
| event.SetStringKey(kKeyContentType, mime_type); |
| // |content_size| can be set to -1 to indicate an unknown size, in |
| // which case the field is not set. |
| if (content_size >= 0) |
| event.SetIntKey(kKeyContentSize, content_size); |
| event.SetStringKey(kKeyTrigger, kTriggerFileDownload); |
| return event; |
| }, |
| url.spec(), file_name, download_digest_sha256, GetProfileUserName(), |
| threat_type, mime_type, content_size)); |
| } |
| |
| void SafeBrowsingPrivateEventRouter::OnDangerousDownloadWarningBypassed( |
| const GURL& url, |
| const std::string& file_name, |
| const std::string& download_digest_sha256, |
| const std::string& threat_type, |
| const std::string& mime_type, |
| const int64_t content_size) { |
| if (!IsRealtimeReportingEnabled()) |
| return; |
| |
| ReportRealtimeEvent( |
| kKeyDangerousDownloadEvent, |
| base::BindOnce( |
| [](const std::string& url, const std::string& file_name, |
| const std::string& download_digest_sha256, |
| const std::string& profile_user_name, |
| const std::string& threat_type, const std::string& mime_type, |
| const int64_t content_size) { |
| // Create a real-time event dictionary and report it. |
| base::Value event(base::Value::Type::DICTIONARY); |
| event.SetStringKey(kKeyUrl, url); |
| event.SetStringKey(kKeyFileName, file_name); |
| event.SetStringKey(kKeyDownloadDigestSha256, |
| download_digest_sha256); |
| event.SetStringKey(kKeyProfileUserName, profile_user_name); |
| event.SetStringKey(kKeyThreatType, threat_type); |
| event.SetBoolKey(kKeyClickedThrough, true); |
| event.SetStringKey(kKeyContentType, mime_type); |
| // |content_size| can be set to -1 to indicate an unknown size, in |
| // which case the field is not set. |
| if (content_size >= 0) |
| event.SetIntKey(kKeyContentSize, content_size); |
| event.SetStringKey(kKeyTrigger, kTriggerFileDownload); |
| return event; |
| }, |
| url.spec(), file_name, download_digest_sha256, GetProfileUserName(), |
| threat_type, mime_type, content_size)); |
| } |
| |
| void SafeBrowsingPrivateEventRouter::SetCloudPolicyClientForTesting( |
| std::unique_ptr<policy::CloudPolicyClient> client) { |
| DCHECK_EQ(nullptr, client_.get()); |
| client_ = std::move(client); |
| } |
| |
| void SafeBrowsingPrivateEventRouter::InitRealtimeReportingClient() { |
| #if !defined(OS_CHROMEOS) |
| // If already initialized, do nothing. |
| if (client_) |
| return; |
| |
| // This method is not compiled on Chrome OS because |
| // ChromeBrowserCloudManagementController does not exist. Also, |
| // policy::BrowserDMTokenStorage::Get()->RetrieveDMToken() doesn't return a |
| // valid token either. Once these are fixed the #if !defined can be removed. |
| if (!policy::ChromeBrowserCloudManagementController::IsEnabled()) |
| return; |
| |
| if (!base::FeatureList::IsEnabled(kRealtimeReportingFeature)) |
| return; |
| |
| // |identity_manager_| may be null in tests. If there is no identity |
| // manager don't enable the real-time reporting API since the router won't |
| // be able to fill in all the info needed for the reports. |
| identity_manager_ = IdentityManagerFactory::GetForProfile( |
| Profile::FromBrowserContext(context_)); |
| if (!identity_manager_) |
| return; |
| |
| // |device_management_service| may be null in tests. If there is no device |
| // management service don't enable the real-time reporting API since the |
| // router won't be able to create the reporting server client below. |
| policy::DeviceManagementService* device_management_service = |
| g_browser_process->browser_policy_connector() |
| ->device_management_service(); |
| if (!device_management_service) |
| return; |
| |
| if (g_browser_process) { |
| binary_upload_service_ = |
| g_browser_process->safe_browsing_service()->GetBinaryUploadService( |
| Profile::FromBrowserContext(context_)); |
| binary_upload_service_->IsAuthorized(base::BindOnce( |
| &SafeBrowsingPrivateEventRouter::InitRealtimeReportingClientCallback, |
| weakptr_factory_.GetWeakPtr(), device_management_service)); |
| } |
| #endif |
| } |
| |
| void SafeBrowsingPrivateEventRouter::InitRealtimeReportingClientCallback( |
| policy::DeviceManagementService* device_management_service, |
| bool authorized) { |
| #if !defined(OS_CHROMEOS) |
| // Don't initialize the client if the browser cannot upload data. |
| if (!authorized) |
| return; |
| |
| // Make sure we have a DM token to proceed. During the lifetime of a running |
| // chrome browser, this can only change from empty to non-empty. There are |
| // no cases where chrome starts with a dm token and then it goes away. |
| // When chrome starts without a dm token and determines that one is needed, |
| // browser startup is blocked until it is retrieved or an error occurs. In |
| // the latter case, chrome won't try to retrieve it again until the next |
| // restart. |
| // |
| // Therefore, it is OK to retrieve the dm token once here on initialization |
| // of the router to determine if real-time reporting can be enabled or not. |
| std::string dm_token = |
| policy::BrowserDMTokenStorage::Get()->RetrieveDMToken(); |
| std::string client_id = |
| policy::BrowserDMTokenStorage::Get()->RetrieveClientId(); |
| |
| if (dm_token.empty()) |
| return; |
| |
| // Make sure DeviceManagementService has been initialized. |
| device_management_service->ScheduleInitialization(0); |
| |
| client_ = std::make_unique<policy::CloudPolicyClient>( |
| /*machine_id=*/std::string(), /*machine_model=*/std::string(), |
| /*brand_code=*/std::string(), /*ethernet_mac_address=*/std::string(), |
| /*dock_mac_address=*/std::string(), /*manufacture_date=*/std::string(), |
| device_management_service, g_browser_process->shared_url_loader_factory(), |
| nullptr, policy::CloudPolicyClient::DeviceDMTokenCallback()); |
| |
| if (!client_->is_registered()) { |
| client_->SetupRegistration( |
| dm_token, client_id, |
| /*user_affiliation_ids=*/std::vector<std::string>()); |
| } |
| #endif |
| } |
| |
| bool SafeBrowsingPrivateEventRouter::IsRealtimeReportingEnabled() { |
| // g_browser_process and/or g_browser_process->local_state() may be null |
| // in tests. |
| return g_browser_process && g_browser_process->local_state() && |
| g_browser_process->local_state()->GetBoolean( |
| prefs::kUnsafeEventsReportingEnabled); |
| } |
| |
| void SafeBrowsingPrivateEventRouter::RealtimeReportingPrefChanged( |
| const std::string& pref) { |
| // If the reporting policy has been turned on, try to initialized now. |
| if (IsRealtimeReportingEnabled()) |
| InitRealtimeReportingClient(); |
| } |
| |
| void SafeBrowsingPrivateEventRouter::ReportRealtimeEvent( |
| const std::string& name, |
| EventBuilder event_builder) { |
| if (binary_upload_service_) { |
| binary_upload_service_->IsAuthorized(base::BindOnce( |
| &SafeBrowsingPrivateEventRouter::ReportRealtimeEventCallback, |
| weakptr_factory_.GetWeakPtr(), name, std::move(event_builder))); |
| } |
| } |
| |
| void SafeBrowsingPrivateEventRouter::ReportRealtimeEventCallback( |
| const std::string& name, |
| EventBuilder event_builder, |
| bool authorized) { |
| // Ignore the event if we know we can't report it. |
| if (!authorized) |
| return; |
| |
| // |client_| should be set when authorized is true. |
| DCHECK(client_); |
| |
| // Format the current time (UTC) in RFC3339 format. |
| base::Time::Exploded now_exploded; |
| base::Time::Now().UTCExplode(&now_exploded); |
| std::string now_str = base::StringPrintf( |
| "%d-%02d-%02dT%02d:%02d:%02d.%03dZ", now_exploded.year, |
| now_exploded.month, now_exploded.day_of_month, now_exploded.hour, |
| now_exploded.minute, now_exploded.second, now_exploded.millisecond); |
| |
| base::Value wrapper(base::Value::Type::DICTIONARY); |
| wrapper.SetStringKey("time", now_str); |
| wrapper.SetKey(name, std::move(event_builder).Run()); |
| |
| base::Value event_list(base::Value::Type::LIST); |
| event_list.Append(std::move(wrapper)); |
| |
| client_->UploadRealtimeReport( |
| policy::RealtimeReportingJobConfiguration::BuildReport( |
| std::move(event_list), |
| reporting::GetContext(Profile::FromBrowserContext(context_))), |
| base::DoNothing()); |
| } |
| |
| std::string SafeBrowsingPrivateEventRouter::GetProfileUserName() { |
| // |identity_manager_| may be null is some tests. |
| return identity_manager_ && identity_manager_->HasPrimaryAccount() |
| ? identity_manager_->GetPrimaryAccountInfo().email |
| : std::string(); |
| } |
| |
| } // namespace extensions |