diff options
Diffstat (limited to 'chromium/components/safe_browsing/content')
36 files changed, 941 insertions, 282 deletions
diff --git a/chromium/components/safe_browsing/content/base_blocking_page.cc b/chromium/components/safe_browsing/content/base_blocking_page.cc index 513ed5b08f7..bc7d4dc3c7d 100644 --- a/chromium/components/safe_browsing/content/base_blocking_page.cc +++ b/chromium/components/safe_browsing/content/base_blocking_page.cc @@ -138,10 +138,6 @@ void BaseBlockingPage::PopulateInterstitialStrings( sb_error_ui_->PopulateStringsForHtml(load_time_data); } -void BaseBlockingPage::OnInterstitialClosing() { - UpdateMetricsAfterSecurityInterstitial(); -} - void BaseBlockingPage::FinishThreatDetails(const base::TimeDelta& delay, bool did_proceed, int num_visits) {} diff --git a/chromium/components/safe_browsing/content/base_blocking_page.h b/chromium/components/safe_browsing/content/base_blocking_page.h index 1e35a434363..97bfb779040 100644 --- a/chromium/components/safe_browsing/content/base_blocking_page.h +++ b/chromium/components/safe_browsing/content/base_blocking_page.h @@ -61,7 +61,7 @@ class BaseBlockingPage bool ShouldCreateNewNavigation() const override; void PopulateInterstitialStrings( base::DictionaryValue* load_time_data) override; - void OnInterstitialClosing() override; + void OnInterstitialClosing() override {} // Called when the interstitial is going away. Intentionally do nothing in // this base class. diff --git a/chromium/components/safe_browsing/content/base_ui_manager.cc b/chromium/components/safe_browsing/content/base_ui_manager.cc index 6643359fd2e..680b1098f46 100644 --- a/chromium/components/safe_browsing/content/base_ui_manager.cc +++ b/chromium/components/safe_browsing/content/base_ui_manager.cc @@ -190,6 +190,21 @@ void BaseUIManager::OnBlockingPageDone( } } +namespace { +// In the case of nested WebContents, returns the WebContents where it is +// suitable to show an interstitial. +content::WebContents* GetEmbeddingWebContentsForInterstitial( + content::WebContents* source_contents) { + content::WebContents* top_level_contents = source_contents; + // Note that |WebContents::GetResponsibleWebContents| is not suitable here + // since we want to stay within any GuestViews. + while (top_level_contents->IsPortal()) { + top_level_contents = top_level_contents->GetPortalHostWebContents(); + } + return top_level_contents; +} +} // namespace + void BaseUIManager::DisplayBlockingPage( const UnsafeResource& resource) { DCHECK_CURRENTLY_ON(BrowserThread::UI); @@ -245,13 +260,26 @@ void BaseUIManager::DisplayBlockingPage( } AddToWhitelistUrlSet(GetMainFrameWhitelistUrlForResource(resource), - resource.web_contents_getter.Run(), - true /* A decision is now pending */, + web_contents, true /* A decision is now pending */, resource.threat_type); - GURL unsafe_url = (resource.IsMainPageLoadBlocked() || - !GetNavigationEntryForResource(resource)) - ? resource.url - : GetNavigationEntryForResource(resource)->GetURL(); + + // |entry| can be null if we are on a brand new tab, and a resource is added + // via javascript without a navigation. + content::NavigationEntry* entry = GetNavigationEntryForResource(resource); + + // If unsafe content is loaded in a portal, we treat its embedder as + // dangerous. + content::WebContents* outermost_contents = + GetEmbeddingWebContentsForInterstitial(web_contents); + + GURL unsafe_url = resource.url; + if (outermost_contents != web_contents) { + DCHECK(outermost_contents->GetController().GetLastCommittedEntry()); + unsafe_url = + outermost_contents->GetController().GetLastCommittedEntry()->GetURL(); + } else if (entry && !resource.IsMainPageLoadBlocked()) { + unsafe_url = entry->GetURL(); + } AddUnsafeResource(unsafe_url, resource); // If the delayed warnings experiment is not enabled, with committed // interstitials we just cancel the load from here, the actual interstitial @@ -273,28 +301,24 @@ void BaseUIManager::DisplayBlockingPage( DCHECK(!resource.is_delayed_warning); } - if ((!resource.IsMainPageLoadBlocked() || resource.is_delayed_warning) && - !IsWhitelisted(resource)) { + if (!resource.IsMainPageLoadBlocked() || resource.is_delayed_warning || + outermost_contents != web_contents) { + DCHECK(!IsWhitelisted(resource)); // For subresource triggered interstitials, we trigger the error page // navigation from here since there will be no navigation to intercept // in the throttle. - content::WebContents* contents = resource.web_contents_getter.Run(); - content::NavigationEntry* entry = GetNavigationEntryForResource(resource); - // entry can be null if we are on a brand new tab, and a resource is added - // via javascript without a navigation. - GURL blocked_url = entry ? entry->GetURL() : resource.url; - + // // Blocking pages handle both user interaction, and generation of the // interstitial HTML. In the case of subresources, we need the HTML // content prior to (and in a different process than when) installing the // command handlers. For this reason we create a blocking page here just // to generate the HTML, and immediately delete it. - BaseBlockingPage* blocking_page = - CreateBlockingPageForSubresource(contents, blocked_url, resource); - contents->GetController().LoadPostCommitErrorPage( - contents->GetMainFrame(), blocked_url, blocking_page->GetHTMLContents(), - net::ERR_BLOCKED_BY_CLIENT); - delete blocking_page; + std::unique_ptr<BaseBlockingPage> blocking_page = + base::WrapUnique(CreateBlockingPageForSubresource( + outermost_contents, unsafe_url, resource)); + outermost_contents->GetController().LoadPostCommitErrorPage( + outermost_contents->GetMainFrame(), unsafe_url, + blocking_page->GetHTMLContents(), net::ERR_BLOCKED_BY_CLIENT); } } diff --git a/chromium/components/safe_browsing/content/browser/BUILD.gn b/chromium/components/safe_browsing/content/browser/BUILD.gn index 6111969ed2b..0b942f26ce3 100644 --- a/chromium/components/safe_browsing/content/browser/BUILD.gn +++ b/chromium/components/safe_browsing/content/browser/BUILD.gn @@ -33,7 +33,7 @@ jumbo_source_set("browser") { "//components/safe_browsing/core/common:common", "//components/safe_browsing/core/db:database_manager", "//components/safe_browsing/core/realtime:policy_engine", - "//components/safe_browsing/core/realtime:url_lookup_service", + "//components/safe_browsing/core/realtime:url_lookup_service_base", "//components/safe_browsing/core/web_ui:constants", "//components/security_interstitials/content:security_interstitial_page", "//components/security_interstitials/core:unsafe_resource", diff --git a/chromium/components/safe_browsing/content/browser/browser_url_loader_throttle.cc b/chromium/components/safe_browsing/content/browser/browser_url_loader_throttle.cc index cb3aa4a82be..2fa3d9cc4c9 100644 --- a/chromium/components/safe_browsing/content/browser/browser_url_loader_throttle.cc +++ b/chromium/components/safe_browsing/content/browser/browser_url_loader_throttle.cc @@ -13,7 +13,7 @@ #include "components/safe_browsing/core/common/safebrowsing_constants.h" #include "components/safe_browsing/core/common/utils.h" #include "components/safe_browsing/core/realtime/policy_engine.h" -#include "components/safe_browsing/core/realtime/url_lookup_service.h" +#include "components/safe_browsing/core/realtime/url_lookup_service_base.h" #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/web_contents.h" #include "net/log/net_log_event_type.h" @@ -34,14 +34,16 @@ class BrowserURLLoaderThrottle::CheckerOnIO base::RepeatingCallback<content::WebContents*()> web_contents_getter, base::WeakPtr<BrowserURLLoaderThrottle> throttle, bool real_time_lookup_enabled, - bool enhanced_protection_enabled, - base::WeakPtr<RealTimeUrlLookupService> url_lookup_service) + bool can_rt_check_subresource_url, + bool can_check_db, + base::WeakPtr<RealTimeUrlLookupServiceBase> url_lookup_service) : delegate_getter_(std::move(delegate_getter)), frame_tree_node_id_(frame_tree_node_id), web_contents_getter_(web_contents_getter), throttle_(std::move(throttle)), real_time_lookup_enabled_(real_time_lookup_enabled), - enhanced_protection_enabled_(enhanced_protection_enabled), + can_rt_check_subresource_url_(can_rt_check_subresource_url), + can_check_db_(can_check_db), url_lookup_service_(url_lookup_service) {} // Starts the initial safe browsing check. This check and future checks may be @@ -63,8 +65,8 @@ class BrowserURLLoaderThrottle::CheckerOnIO url, frame_tree_node_id_, -1 /* render_process_id */, -1 /* render_frame_id */, originated_from_service_worker); if (skip_checks_) { - base::PostTask( - FROM_HERE, {content::BrowserThread::UI}, + content::GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&BrowserURLLoaderThrottle::SkipChecks, throttle_)); return; } @@ -72,7 +74,7 @@ class BrowserURLLoaderThrottle::CheckerOnIO url_checker_ = std::make_unique<SafeBrowsingUrlCheckerImpl>( headers, load_flags, resource_type, has_user_gesture, url_checker_delegate, web_contents_getter_, real_time_lookup_enabled_, - enhanced_protection_enabled_, url_lookup_service_); + can_rt_check_subresource_url_, can_check_db_, url_lookup_service_); CheckUrl(url, method); } @@ -81,8 +83,8 @@ class BrowserURLLoaderThrottle::CheckerOnIO void CheckUrl(const GURL& url, const std::string& method) { DCHECK_CURRENTLY_ON(content::BrowserThread::IO); if (skip_checks_) { - base::PostTask( - FROM_HERE, {content::BrowserThread::UI}, + content::GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&BrowserURLLoaderThrottle::SkipChecks, throttle_)); return; } @@ -108,8 +110,8 @@ class BrowserURLLoaderThrottle::CheckerOnIO return; } - base::PostTask( - FROM_HERE, {content::BrowserThread::UI}, + content::GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&BrowserURLLoaderThrottle::NotifySlowCheck, throttle_)); // In this case |proceed| and |showed_interstitial| should be ignored. The @@ -124,8 +126,8 @@ class BrowserURLLoaderThrottle::CheckerOnIO void OnCompleteCheck(bool slow_check, bool proceed, bool showed_interstitial) { - base::PostTask( - FROM_HERE, {content::BrowserThread::UI}, + content::GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&BrowserURLLoaderThrottle::OnCompleteCheck, throttle_, slow_check, proceed, showed_interstitial)); } @@ -139,8 +141,9 @@ class BrowserURLLoaderThrottle::CheckerOnIO bool skip_checks_ = false; base::WeakPtr<BrowserURLLoaderThrottle> throttle_; bool real_time_lookup_enabled_ = false; - bool enhanced_protection_enabled_ = false; - base::WeakPtr<RealTimeUrlLookupService> url_lookup_service_; + bool can_rt_check_subresource_url_ = false; + bool can_check_db_ = true; + base::WeakPtr<RealTimeUrlLookupServiceBase> url_lookup_service_; }; // static @@ -148,7 +151,7 @@ std::unique_ptr<BrowserURLLoaderThrottle> BrowserURLLoaderThrottle::Create( GetDelegateCallback delegate_getter, const base::RepeatingCallback<content::WebContents*()>& web_contents_getter, int frame_tree_node_id, - base::WeakPtr<RealTimeUrlLookupService> url_lookup_service) { + base::WeakPtr<RealTimeUrlLookupServiceBase> url_lookup_service) { return base::WrapUnique<BrowserURLLoaderThrottle>( new BrowserURLLoaderThrottle(std::move(delegate_getter), web_contents_getter, frame_tree_node_id, @@ -159,7 +162,7 @@ BrowserURLLoaderThrottle::BrowserURLLoaderThrottle( GetDelegateCallback delegate_getter, const base::RepeatingCallback<content::WebContents*()>& web_contents_getter, int frame_tree_node_id, - base::WeakPtr<RealTimeUrlLookupService> url_lookup_service) { + base::WeakPtr<RealTimeUrlLookupServiceBase> url_lookup_service) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); // Decide whether to do real time URL lookups or not. @@ -167,13 +170,18 @@ BrowserURLLoaderThrottle::BrowserURLLoaderThrottle( url_lookup_service ? url_lookup_service->CanPerformFullURLLookup() : false; - bool enhanced_protection_enabled = - url_lookup_service && url_lookup_service->IsUserEpOptedIn(); + bool can_rt_check_subresource_url = + url_lookup_service && url_lookup_service->CanCheckSubresourceURL(); + // Decide whether safe browsing database can be checked. + // If url_lookup_service is null, safe browsing database should be checked by + // default. + bool can_check_db = + url_lookup_service ? url_lookup_service->CanCheckSafeBrowsingDb() : true; io_checker_ = std::make_unique<CheckerOnIO>( std::move(delegate_getter), frame_tree_node_id, web_contents_getter, weak_factory_.GetWeakPtr(), real_time_lookup_enabled, - enhanced_protection_enabled, url_lookup_service); + can_rt_check_subresource_url, can_check_db, url_lookup_service); } BrowserURLLoaderThrottle::~BrowserURLLoaderThrottle() { @@ -193,8 +201,8 @@ void BrowserURLLoaderThrottle::WillStartRequest( original_url_ = request->url; pending_checks_++; - base::PostTask( - FROM_HERE, {content::BrowserThread::IO}, + content::GetIOThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce( &BrowserURLLoaderThrottle::CheckerOnIO::Start, io_checker_->AsWeakPtr(), request->headers, request->load_flags, @@ -223,8 +231,8 @@ void BrowserURLLoaderThrottle::WillRedirectRequest( return; pending_checks_++; - base::PostTask( - FROM_HERE, {content::BrowserThread::IO}, + content::GetIOThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&BrowserURLLoaderThrottle::CheckerOnIO::CheckUrl, io_checker_->AsWeakPtr(), redirect_info->new_url, redirect_info->new_method)); @@ -324,8 +332,8 @@ void BrowserURLLoaderThrottle::NotifySlowCheck() { } void BrowserURLLoaderThrottle::DeleteCheckerOnIO() { - base::DeleteSoon(FROM_HERE, {content::BrowserThread::IO}, - std::move(io_checker_)); + content::GetIOThreadTaskRunner({})->DeleteSoon(FROM_HERE, + std::move(io_checker_)); } } // namespace safe_browsing diff --git a/chromium/components/safe_browsing/content/browser/browser_url_loader_throttle.h b/chromium/components/safe_browsing/content/browser/browser_url_loader_throttle.h index d31b38a3dbc..fbd0536a4c7 100644 --- a/chromium/components/safe_browsing/content/browser/browser_url_loader_throttle.h +++ b/chromium/components/safe_browsing/content/browser/browser_url_loader_throttle.h @@ -28,7 +28,7 @@ namespace safe_browsing { class UrlCheckerDelegate; -class RealTimeUrlLookupService; +class RealTimeUrlLookupServiceBase; // BrowserURLLoaderThrottle is used in the browser process to query // SafeBrowsing to determine whether a URL and also its redirect URLs are safe @@ -49,7 +49,7 @@ class BrowserURLLoaderThrottle : public blink::URLLoaderThrottle { const base::RepeatingCallback<content::WebContents*()>& web_contents_getter, int frame_tree_node_id, - base::WeakPtr<RealTimeUrlLookupService> url_lookup_service); + base::WeakPtr<RealTimeUrlLookupServiceBase> url_lookup_service); ~BrowserURLLoaderThrottle() override; @@ -86,7 +86,7 @@ class BrowserURLLoaderThrottle : public blink::URLLoaderThrottle { const base::RepeatingCallback<content::WebContents*()>& web_contents_getter, int frame_tree_node_id, - base::WeakPtr<RealTimeUrlLookupService> url_lookup_service); + base::WeakPtr<RealTimeUrlLookupServiceBase> url_lookup_service); // |slow_check| indicates whether it reports the result of a slow check. // (Please see comments of CheckerOnIO::OnCheckUrlResult() for what slow check diff --git a/chromium/components/safe_browsing/content/browser/mojo_safe_browsing_impl.cc b/chromium/components/safe_browsing/content/browser/mojo_safe_browsing_impl.cc index 43e5f816d0b..b9dc2d53324 100644 --- a/chromium/components/safe_browsing/content/browser/mojo_safe_browsing_impl.cc +++ b/chromium/components/safe_browsing/content/browser/mojo_safe_browsing_impl.cc @@ -156,7 +156,9 @@ void MojoSafeBrowsingImpl::CreateCheckerAndCheck( delegate_, base::BindRepeating(&GetWebContentsFromID, render_process_id_, static_cast<int>(render_frame_id)), - /*real_time_lookup_enabled=*/false, /*enhanced_protection_enabled=*/false, + /*real_time_lookup_enabled=*/false, + /*can_rt_check_subresource_url=*/false, + /*can_check_db=*/true, /*url_lookup_service=*/nullptr); checker_impl->CheckUrl( diff --git a/chromium/components/safe_browsing/content/browser/safe_browsing_url_checker_impl_content.cc b/chromium/components/safe_browsing/content/browser/safe_browsing_url_checker_impl_content.cc index 4ac3714571e..b22dc6eed7d 100644 --- a/chromium/components/safe_browsing/content/browser/safe_browsing_url_checker_impl_content.cc +++ b/chromium/components/safe_browsing/content/browser/safe_browsing_url_checker_impl_content.cc @@ -13,7 +13,7 @@ #include "components/safe_browsing/core/common/safebrowsing_constants.h" #include "components/safe_browsing/core/common/thread_utils.h" #include "components/safe_browsing/core/realtime/policy_engine.h" -#include "components/safe_browsing/core/realtime/url_lookup_service.h" +#include "components/safe_browsing/core/realtime/url_lookup_service_base.h" #include "components/safe_browsing/core/web_ui/constants.h" namespace safe_browsing { @@ -21,8 +21,8 @@ namespace safe_browsing { bool SafeBrowsingUrlCheckerImpl::CanPerformFullURLLookup(const GURL& url) { return real_time_lookup_enabled_ && RealTimePolicyEngine::CanPerformFullURLLookupForResourceType( - resource_type_, enhanced_protection_enabled_) && - RealTimeUrlLookupService::CanCheckUrl(url); + resource_type_, can_rt_check_subresource_url_) && + RealTimeUrlLookupServiceBase::CanCheckUrl(url); } void SafeBrowsingUrlCheckerImpl::OnRTLookupRequest( @@ -48,7 +48,7 @@ void SafeBrowsingUrlCheckerImpl::OnRTLookupResponse( bool is_expected_resource_type = (ResourceType::kMainFrame == resource_type_) || ((ResourceType::kSubFrame == resource_type_) && - enhanced_protection_enabled_); + can_rt_check_subresource_url_); DCHECK(is_expected_resource_type); const GURL& url = urls_[next_index_].url; @@ -75,8 +75,9 @@ void SafeBrowsingUrlCheckerImpl::OnRTLookupResponse( // TODO(crbug.com/1033692): Only take the first threat info into account // because threat infos are returned in decreasing order of severity. // Consider extend it to support multiple threat types. - sb_threat_type = RealTimeUrlLookupService::GetSBThreatTypeForRTThreatType( - response->threat_info(0).threat_type()); + sb_threat_type = + RealTimeUrlLookupServiceBase::GetSBThreatTypeForRTThreatType( + response->threat_info(0).threat_type()); } OnUrlResult(url, sb_threat_type, ThreatMetadata()); } diff --git a/chromium/components/safe_browsing/content/browser/threat_details.cc b/chromium/components/safe_browsing/content/browser/threat_details.cc index a71ac3092c4..c63087e8c8f 100644 --- a/chromium/components/safe_browsing/content/browser/threat_details.cc +++ b/chromium/components/safe_browsing/content/browser/threat_details.cc @@ -20,7 +20,6 @@ #include "base/stl_util.h" #include "base/strings/string_piece.h" #include "base/strings/string_util.h" -#include "base/task/post_task.h" #include "components/history/core/browser/history_service.h" #include "components/safe_browsing/content/base_ui_manager.h" #include "components/safe_browsing/content/browser/threat_details_cache.h" @@ -849,8 +848,8 @@ void ThreatDetails::OnCacheCollectionReady() { return; } - base::PostTask( - FROM_HERE, {content::BrowserThread::UI}, + content::GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&WebUIInfoSingleton::AddToCSBRRsSent, base::Unretained(WebUIInfoSingleton::GetInstance()), std::move(report_))); @@ -877,8 +876,8 @@ void ThreatDetails::MaybeFillReferrerChain() { void ThreatDetails::AllDone() { is_all_done_ = true; - base::PostTask(FROM_HERE, {content::BrowserThread::UI}, - base::BindOnce(std::move(done_callback_), + content::GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(std::move(done_callback_), base::Unretained(web_contents()))); } diff --git a/chromium/components/safe_browsing/content/browser/threat_details_cache.cc b/chromium/components/safe_browsing/content/browser/threat_details_cache.cc index d0ea614c3bd..70c5ee9278c 100644 --- a/chromium/components/safe_browsing/content/browser/threat_details_cache.cc +++ b/chromium/components/safe_browsing/content/browser/threat_details_cache.cc @@ -12,7 +12,6 @@ #include "base/hash/md5.h" #include "base/lazy_instance.h" #include "base/strings/string_util.h" -#include "base/task/post_task.h" #include "components/safe_browsing/content/browser/threat_details.h" #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" @@ -52,8 +51,8 @@ void ThreatDetailsCacheCollector::StartCacheCollection( // Post a task in the message loop, so the callers don't need to // check if we call their callback immediately. - base::PostTask(FROM_HERE, {BrowserThread::UI}, - base::BindOnce(&ThreatDetailsCacheCollector::OpenEntry, this)); + content::GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&ThreatDetailsCacheCollector::OpenEntry, this)); } bool ThreatDetailsCacheCollector::HasStarted() { @@ -227,15 +226,15 @@ void ThreatDetailsCacheCollector::AdvanceEntry() { current_load_.reset(); // Create a task so we don't take over the UI thread for too long. - base::PostTask(FROM_HERE, {BrowserThread::UI}, - base::BindOnce(&ThreatDetailsCacheCollector::OpenEntry, this)); + content::GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&ThreatDetailsCacheCollector::OpenEntry, this)); } void ThreatDetailsCacheCollector::AllDone(bool success) { DVLOG(1) << "AllDone"; DCHECK_CURRENTLY_ON(BrowserThread::UI); *result_ = success; - base::PostTask(FROM_HERE, {BrowserThread::UI}, std::move(callback_)); + content::GetUIThreadTaskRunner({})->PostTask(FROM_HERE, std::move(callback_)); } } // namespace safe_browsing diff --git a/chromium/components/safe_browsing/content/browser/threat_details_history.cc b/chromium/components/safe_browsing/content/browser/threat_details_history.cc index d434e97c4ac..c9976122b8c 100644 --- a/chromium/components/safe_browsing/content/browser/threat_details_history.cc +++ b/chromium/components/safe_browsing/content/browser/threat_details_history.cc @@ -10,7 +10,6 @@ #include "base/bind.h" #include "base/bind_helpers.h" -#include "base/task/post_task.h" #include "components/safe_browsing/content/browser/threat_details.h" #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" @@ -41,8 +40,8 @@ void ThreatDetailsRedirectsCollector::StartHistoryCollection( return; } - base::PostTask( - FROM_HERE, {BrowserThread::UI}, + content::GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&ThreatDetailsRedirectsCollector::StartGetRedirects, this, urls)); } @@ -107,7 +106,7 @@ void ThreatDetailsRedirectsCollector::OnGotQueryRedirectsTo( void ThreatDetailsRedirectsCollector::AllDone() { DVLOG(1) << "AllDone"; - base::PostTask(FROM_HERE, {BrowserThread::UI}, std::move(callback_)); + content::GetUIThreadTaskRunner({})->PostTask(FROM_HERE, std::move(callback_)); } void ThreatDetailsRedirectsCollector::HistoryServiceBeingDeleted( diff --git a/chromium/components/safe_browsing/content/common/safe_browsing.mojom b/chromium/components/safe_browsing/content/common/safe_browsing.mojom index a5cc71ad13e..72c21819f36 100644 --- a/chromium/components/safe_browsing/content/common/safe_browsing.mojom +++ b/chromium/components/safe_browsing/content/common/safe_browsing.mojom @@ -118,7 +118,13 @@ enum PhishingDetectorResult { }; [EnableIf=full_safe_browsing] +// Interface for setting the CSD model and to start phishing classification. interface PhishingDetector { + // A classification model for client-side phishing detection. + // The string is an encoded safe_browsing::ClientSideModel protocol buffer, or + // empty to disable client-side phishing detection for this renderer. + SetPhishingModel(string model); + // Tells the renderer to begin phishing detection for the given toplevel URL // which it has started loading. Returns the serialized request proto and a // |result| enum to indicate failure. If the URL is phishing the request proto @@ -126,10 +132,3 @@ interface PhishingDetector { StartPhishingDetection(url.mojom.Url url) => (PhishingDetectorResult result, string request_proto); }; - -interface PhishingModelSetter { - // A classification model for client-side phishing detection. - // The string is an encoded safe_browsing::ClientSideModel protocol buffer, or - // empty to disable client-side phishing detection for this renderer. - SetPhishingModel(string model); -}; diff --git a/chromium/components/safe_browsing/content/password_protection/BUILD.gn b/chromium/components/safe_browsing/content/password_protection/BUILD.gn index 26d6f07d5ee..2d06ca399b5 100644 --- a/chromium/components/safe_browsing/content/password_protection/BUILD.gn +++ b/chromium/components/safe_browsing/content/password_protection/BUILD.gn @@ -28,6 +28,7 @@ source_set("password_protection") { "//components/password_manager/core/browser:browser", "//components/safe_browsing/content/common:interfaces", "//components/safe_browsing/content/web_ui:web_ui", + "//components/safe_browsing/core:client_model_proto", "//components/safe_browsing/core:csd_proto", "//components/safe_browsing/core:features", "//components/safe_browsing/core/browser:referrer_chain_provider", @@ -43,7 +44,9 @@ source_set("password_protection") { "//components/url_formatter", "//content/public/browser:browser", "//net:net", + "//third_party/opencv:emd", "//third_party/protobuf:protobuf_lite", + "//ui/gfx:color_utils", ] } if (safe_browsing_mode == 1) { diff --git a/chromium/components/safe_browsing/content/password_protection/DEPS b/chromium/components/safe_browsing/content/password_protection/DEPS index c39c8a73429..776629f7b37 100644 --- a/chromium/components/safe_browsing/content/password_protection/DEPS +++ b/chromium/components/safe_browsing/content/password_protection/DEPS @@ -10,8 +10,9 @@ include_rules = [ "+net", "+services/network/public", "+third_party/blink/public/common/page/page_zoom.h", - "+ui/gfx/geometry", + "+ui/gfx", "+third_party/skia/include", + "+third_party/opencv", ] specific_include_rules = { diff --git a/chromium/components/safe_browsing/content/password_protection/metrics_util.cc b/chromium/components/safe_browsing/content/password_protection/metrics_util.cc index 7b66bf6308b..072a0f97219 100644 --- a/chromium/components/safe_browsing/content/password_protection/metrics_util.cc +++ b/chromium/components/safe_browsing/content/password_protection/metrics_util.cc @@ -7,6 +7,7 @@ #include "base/logging.h" #include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_macros.h" +#include "base/notreached.h" #include "base/time/time.h" #include "net/http/http_status_code.h" diff --git a/chromium/components/safe_browsing/content/password_protection/mock_password_protection_service.h b/chromium/components/safe_browsing/content/password_protection/mock_password_protection_service.h index 1e3783718e4..e88bcd4d1d9 100644 --- a/chromium/components/safe_browsing/content/password_protection/mock_password_protection_service.h +++ b/chromium/components/safe_browsing/content/password_protection/mock_password_protection_service.h @@ -36,13 +36,13 @@ class MockPasswordProtectionService : public PasswordProtectionService { MOCK_CONST_METHOD1(GetSignedInNonSyncAccount, AccountInfo(const std::string&)); MOCK_CONST_METHOD1(IsOtherGaiaAccountGmail, bool(const std::string&)); - MOCK_CONST_METHOD2(IsURLWhitelistedForPasswordEntry, - bool(const GURL&, RequestOutcome*)); + MOCK_CONST_METHOD1(IsURLWhitelistedForPasswordEntry, bool(const GURL&)); MOCK_METHOD0(CanSendSamplePing, bool()); MOCK_METHOD0(IsExtendedReporting, bool()); MOCK_METHOD0(IsEnhancedProtection, bool()); MOCK_METHOD0(IsIncognito, bool()); + MOCK_METHOD1(IsInPasswordAlertMode, bool(ReusedPasswordAccountType)); MOCK_METHOD0(IsHistorySyncEnabled, bool()); MOCK_METHOD0(IsUnderAdvancedProtection, bool()); MOCK_METHOD0(ReportPasswordChanged, void()); @@ -57,10 +57,13 @@ class MockPasswordProtectionService : public PasswordProtectionService { MOCK_METHOD1( RemovePhishedSavedPasswordCredential, void(const std::vector<password_manager::MatchingReusedCredential>&)); - MOCK_METHOD3(IsPingingEnabled, + MOCK_METHOD2(IsPingingEnabled, bool(LoginReputationClientRequest::TriggerType, - ReusedPasswordAccountType, - RequestOutcome*)); + ReusedPasswordAccountType)); + MOCK_METHOD3(GetPingNotSentReason, + RequestOutcome(LoginReputationClientRequest::TriggerType, + const GURL&, + ReusedPasswordAccountType)); MOCK_METHOD5(ShowModalWarning, void(content::WebContents*, RequestOutcome, @@ -85,8 +88,8 @@ class MockPasswordProtectionService : public PasswordProtectionService { RequestOutcome, PasswordType, const safe_browsing::LoginReputationClientResponse*)); - MOCK_METHOD3(CanShowInterstitial, - bool(RequestOutcome, ReusedPasswordAccountType, const GURL&)); + MOCK_METHOD2(CanShowInterstitial, + bool(ReusedPasswordAccountType, const GURL&)); MOCK_METHOD5(MaybeStartPasswordFieldOnFocusRequest, void(content::WebContents*, const GURL&, diff --git a/chromium/components/safe_browsing/content/password_protection/password_protection_request.cc b/chromium/components/safe_browsing/content/password_protection/password_protection_request.cc index 82b217141c6..94831c3c64c 100644 --- a/chromium/components/safe_browsing/content/password_protection/password_protection_request.cc +++ b/chromium/components/safe_browsing/content/password_protection/password_protection_request.cc @@ -11,7 +11,6 @@ #include "base/containers/flat_set.h" #include "base/memory/weak_ptr.h" #include "base/metrics/histogram_macros.h" -#include "base/task/post_task.h" #include "base/task/thread_pool.h" #include "base/time/time.h" #include "components/password_manager/core/browser/password_manager_metrics_util.h" @@ -159,7 +158,7 @@ void PasswordProtectionRequest::CheckWhitelist() { auto result_callback = base::BindOnce(&OnWhitelistCheckDoneOnIO, GetWeakPtr()); tracker_.PostTask( - base::CreateSingleThreadTaskRunner({BrowserThread::IO}).get(), FROM_HERE, + content::GetIOThreadTaskRunner({}).get(), FROM_HERE, base::BindOnce(&AllowlistCheckerClient::StartCheckCsdWhitelist, password_protection_service_->database_manager(), main_frame_url_, std::move(result_callback))); @@ -170,8 +169,8 @@ void PasswordProtectionRequest::OnWhitelistCheckDoneOnIO( base::WeakPtr<PasswordProtectionRequest> weak_request, bool match_whitelist) { // Don't access weak_request on IO thread. Move it back to UI thread first. - base::PostTask( - FROM_HERE, {BrowserThread::UI}, + content::GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&PasswordProtectionRequest::OnWhitelistCheckDone, weak_request, match_whitelist)); } @@ -332,8 +331,8 @@ void PasswordProtectionRequest::FillRequestProto(bool is_sampled_ping) { main_frame_url_, base::BindRepeating(&PasswordProtectionRequest::OnGetDomFeatures, GetWeakPtr())); - base::PostDelayedTask( - FROM_HERE, {BrowserThread::UI}, + content::GetUIThreadTaskRunner({})->PostDelayedTask( + FROM_HERE, base::BindOnce(&PasswordProtectionRequest::OnGetDomFeatureTimeout, GetWeakPtr()), base::TimeDelta::FromMilliseconds(kDomFeatureTimeoutMs)); @@ -520,8 +519,8 @@ void PasswordProtectionRequest::StartTimeout() { // The weak pointer used for the timeout will be invalidated (and // hence would prevent the timeout) if the check completes on time and // execution reaches Finish(). - base::PostDelayedTask( - FROM_HERE, {BrowserThread::UI}, + content::GetUIThreadTaskRunner({})->PostDelayedTask( + FROM_HERE, base::BindOnce(&PasswordProtectionRequest::Cancel, GetWeakPtr(), true), base::TimeDelta::FromMilliseconds(request_timeout_in_ms_)); } diff --git a/chromium/components/safe_browsing/content/password_protection/password_protection_service.cc b/chromium/components/safe_browsing/content/password_protection/password_protection_service.cc index f93318f850b..6a180cb90ef 100644 --- a/chromium/components/safe_browsing/content/password_protection/password_protection_service.cc +++ b/chromium/components/safe_browsing/content/password_protection/password_protection_service.cc @@ -17,7 +17,6 @@ #include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" #include "base/strings/string_util.h" -#include "base/task/post_task.h" #include "components/content_settings/core/browser/host_content_settings_map.h" #include "components/password_manager/core/browser/password_manager_metrics_util.h" #include "components/password_manager/core/browser/password_reuse_detector.h" @@ -101,18 +100,22 @@ void PasswordProtectionService::MaybeStartPasswordFieldOnFocusRequest( const GURL& password_form_frame_url, const std::string& hosted_domain) { DCHECK_CURRENTLY_ON(BrowserThread::UI); - RequestOutcome reason; - if (CanSendPing(LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE, - main_frame_url, - GetPasswordProtectionReusedPasswordAccountType( - PasswordType::PASSWORD_TYPE_UNKNOWN, - /*username=*/""), - &reason)) { + LoginReputationClientRequest::TriggerType trigger_type = + LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE; + ReusedPasswordAccountType reused_password_account_type = + GetPasswordProtectionReusedPasswordAccountType( + PasswordType::PASSWORD_TYPE_UNKNOWN, + /*username=*/""); + if (CanSendPing(trigger_type, main_frame_url, reused_password_account_type)) { StartRequest(web_contents, main_frame_url, password_form_action, password_form_frame_url, /* username */ "", PasswordType::PASSWORD_TYPE_UNKNOWN, {}, /* matching_reused_credentials: not used for this type */ LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE, true); + } else { + RequestOutcome reason = GetPingNotSentReason(trigger_type, main_frame_url, + reused_password_account_type); + LogNoPingingReason(trigger_type, reason, reused_password_account_type); } } #endif @@ -127,13 +130,10 @@ void PasswordProtectionService::MaybeStartProtectedPasswordEntryRequest( matching_reused_credentials, bool password_field_exists) { DCHECK_CURRENTLY_ON(BrowserThread::UI); + LoginReputationClientRequest::TriggerType trigger_type = + LoginReputationClientRequest::PASSWORD_REUSE_EVENT; ReusedPasswordAccountType reused_password_account_type = GetPasswordProtectionReusedPasswordAccountType(password_type, username); - RequestOutcome reason; - // Need to populate |reason| to be passed into CanShowInterstitial. - bool can_send_ping = - CanSendPing(LoginReputationClientRequest::PASSWORD_REUSE_EVENT, - main_frame_url, reused_password_account_type, &reason); if (IsSupportedPasswordTypeForPinging(password_type)) { #if BUILDFLAG(FULL_SAFE_BROWSING) // Collect metrics about typical page-zoom on login pages. @@ -143,7 +143,8 @@ void PasswordProtectionService::MaybeStartProtectedPasswordEntryRequest( "PasswordProtection.PageZoomFactor", static_cast<int>(100 * blink::PageZoomLevelToZoomFactor(zoom_level))); #endif // defined(FULL_SAFE_BROWSING) - if (can_send_ping) { + if (CanSendPing(LoginReputationClientRequest::PASSWORD_REUSE_EVENT, + main_frame_url, reused_password_account_type)) { saved_passwords_matching_reused_credentials_ = matching_reused_credentials; StartRequest(web_contents, main_frame_url, GURL(), GURL(), username, @@ -151,6 +152,9 @@ void PasswordProtectionService::MaybeStartProtectedPasswordEntryRequest( LoginReputationClientRequest::PASSWORD_REUSE_EVENT, password_field_exists); } else { + RequestOutcome reason = GetPingNotSentReason( + trigger_type, main_frame_url, reused_password_account_type); + LogNoPingingReason(trigger_type, reason, reused_password_account_type); #if defined(SYNC_PASSWORD_REUSE_WARNING_ENABLED) if (reused_password_account_type.is_account_syncing()) MaybeLogPasswordReuseLookupEvent(web_contents, reason, password_type, @@ -160,8 +164,9 @@ void PasswordProtectionService::MaybeStartProtectedPasswordEntryRequest( } #if defined(SYNC_PASSWORD_REUSE_WARNING_ENABLED) - if (CanShowInterstitial(reason, reused_password_account_type, - main_frame_url)) { + if (CanShowInterstitial(reused_password_account_type, main_frame_url)) { + LogPasswordAlertModeOutcome(RequestOutcome::SUCCEEDED, + reused_password_account_type); username_for_last_shown_warning_ = username; reused_password_account_type_for_last_shown_warning_ = reused_password_account_type; @@ -247,24 +252,9 @@ void PasswordProtectionService::StartRequest( bool PasswordProtectionService::CanSendPing( LoginReputationClientRequest::TriggerType trigger_type, const GURL& main_frame_url, - ReusedPasswordAccountType password_type, - RequestOutcome* reason) { - *reason = RequestOutcome::UNKNOWN; - bool is_pinging_enabled = - IsPingingEnabled(trigger_type, password_type, reason); - // Pinging is enabled for password_reuse trigger level; however we need to - // make sure *reason is set appropriately. - PasswordProtectionTrigger trigger_level = - GetPasswordProtectionWarningTriggerPref(password_type); - if (trigger_level == PASSWORD_REUSE) { - *reason = RequestOutcome::PASSWORD_ALERT_MODE; - } - if (is_pinging_enabled && - !IsURLWhitelistedForPasswordEntry(main_frame_url, reason)) { - return true; - } - LogNoPingingReason(trigger_type, *reason, password_type); - return false; + ReusedPasswordAccountType password_type) { + return IsPingingEnabled(trigger_type, password_type) && + !IsURLWhitelistedForPasswordEntry(main_frame_url); } void PasswordProtectionService::RequestFinished( @@ -408,8 +398,8 @@ void PasswordProtectionService::FillUserPopulation( void PasswordProtectionService::OnURLsDeleted( history::HistoryService* history_service, const history::DeletionInfo& deletion_info) { - base::PostTask( - FROM_HERE, {BrowserThread::UI}, + content::GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindRepeating(&PasswordProtectionService:: RemoveUnhandledSyncPasswordReuseOnURLsDeleted, GetWeakPtr(), deletion_info.IsAllHistory(), @@ -551,7 +541,8 @@ bool PasswordProtectionService::IsSupportedPasswordTypeForPinging( PasswordType password_type) const { switch (password_type) { case PasswordType::SAVED_PASSWORD: - return true; + return base::FeatureList::IsEnabled( + safe_browsing::kPasswordProtectionForSavedPasswords); case PasswordType::PRIMARY_ACCOUNT_PASSWORD: return true; case PasswordType::ENTERPRISE_PASSWORD: diff --git a/chromium/components/safe_browsing/content/password_protection/password_protection_service.h b/chromium/components/safe_browsing/content/password_protection/password_protection_service.h index 307e95dbd3d..1823146c4b4 100644 --- a/chromium/components/safe_browsing/content/password_protection/password_protection_service.h +++ b/chromium/components/safe_browsing/content/password_protection/password_protection_service.h @@ -208,9 +208,7 @@ class PasswordProtectionService : public history::HistoryServiceObserver { // If |url| matches Safe Browsing whitelist domains, password protection // change password URL, or password protection login URLs in the enterprise // policy. - virtual bool IsURLWhitelistedForPasswordEntry( - const GURL& url, - RequestOutcome* reason) const = 0; + virtual bool IsURLWhitelistedForPasswordEntry(const GURL& url) const = 0; // Persist the phished saved password credential in the "compromised // credentials" table. Calls the password store to add a row for each @@ -300,14 +298,12 @@ class PasswordProtectionService : public history::HistoryServiceObserver { friend class PasswordProtectionRequest; // Chrome can send password protection ping if it is allowed by for the - // |trigger_type| and if Safe Browsing can compute reputation of - // |main_frame_url| (e.g. Safe Browsing is not able to compute reputation of a - // private IP or a local host). Update |reason| if sending ping is not - // allowed. |password_type| is used for UMA metric recording. + // |trigger_type| and |password_type| and if Safe Browsing can compute + // reputation of |main_frame_url| (e.g. Safe Browsing is not able to compute + // reputation of a private IP or a local host). bool CanSendPing(LoginReputationClientRequest::TriggerType trigger_type, const GURL& main_frame_url, - ReusedPasswordAccountType password_type, - RequestOutcome* reason); + ReusedPasswordAccountType password_type); // Called by a PasswordProtectionRequest instance when it finishes to remove // itself from |requests_|. @@ -364,10 +360,12 @@ class PasswordProtectionService : public history::HistoryServiceObserver { virtual bool IsIncognito() = 0; + virtual bool IsInPasswordAlertMode( + ReusedPasswordAccountType password_type) = 0; + virtual bool IsPingingEnabled( LoginReputationClientRequest::TriggerType trigger_type, - ReusedPasswordAccountType password_type, - RequestOutcome* reason) = 0; + ReusedPasswordAccountType password_type) = 0; virtual bool IsHistorySyncEnabled() = 0; @@ -408,10 +406,8 @@ class PasswordProtectionService : public history::HistoryServiceObserver { bool IsModalWarningShowingInWebContents(content::WebContents* web_contents); // Determines if we should show chrome://reset-password interstitial based on - // previous request outcome, the reused |password_type| and the - // |main_frame_url|. - virtual bool CanShowInterstitial(RequestOutcome reason, - ReusedPasswordAccountType password_type, + // the reused |password_type| and the |main_frame_url|. + virtual bool CanShowInterstitial(ReusedPasswordAccountType password_type, const GURL& main_frame_url) = 0; #endif @@ -422,6 +418,13 @@ class PasswordProtectionService : public history::HistoryServiceObserver { virtual LoginReputationClientRequest::PasswordReuseEvent::SyncAccountType GetSyncAccountType() const = 0; + // Returns the reason why a ping is not sent based on the |trigger_type|, + // |url| and |password_type|. Crash if |CanSendPing| is true. + virtual RequestOutcome GetPingNotSentReason( + LoginReputationClientRequest::TriggerType trigger_type, + const GURL& url, + ReusedPasswordAccountType password_type) = 0; + const std::list<std::string>& common_spoofed_domains() const { return common_spoofed_domains_; } diff --git a/chromium/components/safe_browsing/content/password_protection/password_protection_service_unittest.cc b/chromium/components/safe_browsing/content/password_protection/password_protection_service_unittest.cc index d7c00d3fe41..26357224b45 100644 --- a/chromium/components/safe_browsing/content/password_protection/password_protection_service_unittest.cc +++ b/chromium/components/safe_browsing/content/password_protection/password_protection_service_unittest.cc @@ -96,6 +96,8 @@ class TestPhishingDetector : public mojom::PhishingDetector { mojo::PendingReceiver<mojom::PhishingDetector>(std::move(handle))); } + void SetPhishingModel(const std::string& model) override {} + void StartPhishingDetection( const GURL& url, StartPhishingDetectionCallback callback) override { @@ -275,7 +277,7 @@ class PasswordProtectionServiceTest : public ::testing::TestWithParam<bool> { EXPECT_CALL(*password_protection_service_, IsIncognito()) .WillRepeatedly(Return(false)); EXPECT_CALL(*password_protection_service_, - IsURLWhitelistedForPasswordEntry(_, _)) + IsURLWhitelistedForPasswordEntry(_)) .WillRepeatedly(Return(false)); EXPECT_CALL(*password_protection_service_, GetPasswordProtectionWarningTriggerPref(_)) @@ -1166,9 +1168,13 @@ TEST_P(PasswordProtectionServiceTest, VerifyShouldShowModalWarning) { reused_password_account_type.set_account_type( ReusedPasswordAccountType::SAVED_PASSWORD); - EXPECT_TRUE(password_protection_service_->ShouldShowModalWarning( + EXPECT_FALSE(password_protection_service_->ShouldShowModalWarning( LoginReputationClientRequest::PASSWORD_REUSE_EVENT, - reused_password_account_type, LoginReputationClientResponse::PHISHING)); + reused_password_account_type, + LoginReputationClientResponse::LOW_REPUTATION)); + base::test::ScopedFeatureList feature_list; + feature_list.InitAndEnableFeature( + safe_browsing::kPasswordProtectionForSavedPasswords); EXPECT_TRUE(password_protection_service_->ShouldShowModalWarning( LoginReputationClientRequest::PASSWORD_REUSE_EVENT, reused_password_account_type, @@ -1330,38 +1336,26 @@ TEST_P(PasswordProtectionServiceTest, VerifyIsSupportedPasswordTypeForPinging) { .WillRepeatedly(Return(account_info)); EXPECT_TRUE(password_protection_service_->IsSupportedPasswordTypeForPinging( - PasswordType::SAVED_PASSWORD)); - EXPECT_TRUE(password_protection_service_->IsSupportedPasswordTypeForPinging( PasswordType::PRIMARY_ACCOUNT_PASSWORD)); -// kPasswordProtectionForSignedInUsers is disabled by default on Android. -#if defined(OS_ANDROID) EXPECT_FALSE(password_protection_service_->IsSupportedPasswordTypeForPinging( -#else - EXPECT_TRUE(password_protection_service_->IsSupportedPasswordTypeForPinging( -#endif PasswordType::OTHER_GAIA_PASSWORD)); EXPECT_TRUE(password_protection_service_->IsSupportedPasswordTypeForPinging( PasswordType::ENTERPRISE_PASSWORD)); - - EXPECT_TRUE(password_protection_service_->IsSupportedPasswordTypeForPinging( + EXPECT_FALSE(password_protection_service_->IsSupportedPasswordTypeForPinging( PasswordType::SAVED_PASSWORD)); - EXPECT_TRUE(password_protection_service_->IsSupportedPasswordTypeForPinging( - PasswordType::ENTERPRISE_PASSWORD)); { base::test::ScopedFeatureList feature_list; - feature_list.InitAndDisableFeature( + feature_list.InitAndEnableFeature( safe_browsing::kPasswordProtectionForSignedInUsers); - // Only ping for signed in, non-syncing users if the experiment is on. - EXPECT_FALSE( - password_protection_service_->IsSupportedPasswordTypeForPinging( - PasswordType::OTHER_GAIA_PASSWORD)); + EXPECT_TRUE(password_protection_service_->IsSupportedPasswordTypeForPinging( + PasswordType::OTHER_GAIA_PASSWORD)); } { base::test::ScopedFeatureList feature_list; feature_list.InitAndEnableFeature( - safe_browsing::kPasswordProtectionForSignedInUsers); + safe_browsing::kPasswordProtectionForSavedPasswords); EXPECT_TRUE(password_protection_service_->IsSupportedPasswordTypeForPinging( - PasswordType::OTHER_GAIA_PASSWORD)); + PasswordType::SAVED_PASSWORD)); } } diff --git a/chromium/components/safe_browsing/content/password_protection/visual_utils.cc b/chromium/components/safe_browsing/content/password_protection/visual_utils.cc index 85349795389..c751167b8a3 100644 --- a/chromium/components/safe_browsing/content/password_protection/visual_utils.cc +++ b/chromium/components/safe_browsing/content/password_protection/visual_utils.cc @@ -7,9 +7,14 @@ #include "components/safe_browsing/content/password_protection/visual_utils.h" +#include "base/check_op.h" #include "base/numerics/checked_math.h" +#include "components/safe_browsing/core/proto/client_model.pb.h" +#include "components/safe_browsing/core/proto/csd.pb.h" +#include "third_party/opencv/src/emd_wrapper.h" #include "third_party/skia/include/core/SkBitmap.h" #include "third_party/skia/include/core/SkPixmap.h" +#include "ui/gfx/color_utils.h" namespace safe_browsing { namespace visual_utils { @@ -22,6 +27,87 @@ const int kPHashDownsampleWidth = 288; const int kPHashDownsampleHeight = 288; const int kPHashBlockSize = 6; +// Constants for computing Earth Movers Distance (EMD) between ColorHistograms. +const float kMaxCentroidDistance = 1.414; +const float kMaxColorDistance = 14; + +size_t GetBitCount(uint8_t byte) { + size_t count = 0; + for (; byte > 0; byte >>= 1) { + if (byte & 1) + count++; + } + + return count; +} + +bool GetHashDistance(const std::string& hash1, + const std::string& hash2, + size_t* out) { + // The beginning of the hash is a Varint representing the stride. For values + // at most 127, this is just the value of the first char. Chrome only + // generates strides less than 128, so we can simply extract the first char. + if (hash1[0] != hash2[0]) + return false; + if (hash1[0] & 0x80) + return false; + + size_t count = std::min(hash1.size(), hash2.size()); + size_t distance = 8 * (std::max(hash1.size(), hash2.size()) - count); + for (size_t i = 1; i < count; i++) { + distance += GetBitCount(hash1[i] ^ hash2[i]); + } + + *out = distance; + return true; +} + +opencv::PointDistribution HistogramBinsToPointDistribution( + const google::protobuf::RepeatedPtrField<VisualFeatures::ColorHistogramBin>& + bins) { + opencv::PointDistribution distribution; + distribution.dimensions = 5; + for (const VisualFeatures::ColorHistogramBin& bin : bins) { + distribution.weights.push_back(bin.weight()); + std::vector<float> position(5); + position[0] = bin.centroid_x() / kMaxCentroidDistance; + position[1] = bin.centroid_y() / kMaxCentroidDistance; + position[2] = bin.quantized_r() / kMaxColorDistance; + position[3] = bin.quantized_g() / kMaxColorDistance; + position[4] = bin.quantized_b() / kMaxColorDistance; + distribution.positions.push_back(std::move(position)); + } + + return distribution; +} + +bool ImageHasColorInRange(const SkBitmap& image, + const MatchRule::ColorRange& color_range) { + for (int i = 0; i < image.width(); i++) { + for (int j = 0; j < image.height(); j++) { + SkScalar hsv[3]; + SkColorToHSV(image.getColor(i, j), hsv); + if (color_range.low() <= hsv[0] && hsv[0] <= color_range.high()) + return true; + } + } + + return false; +} + +uint8_t GetMedian(const std::vector<uint8_t>& data) { + std::vector<uint8_t> buffer; + buffer.assign(data.begin(), data.end()); + std::vector<uint8_t>::iterator middle = buffer.begin() + (buffer.size() / 2); + std::nth_element(buffer.begin(), middle, buffer.end()); + if (buffer.size() % 2 == 1) { + return *middle; + } else { + // For even-sized sets, return the average of the two middle elements. + return (*middle + *std::max_element(buffer.begin(), middle)) / 2; + } +} + } // namespace // A QuantizedColor takes the highest 3 bits of R, G, and B, and concatenates @@ -171,5 +257,101 @@ std::unique_ptr<SkBitmap> BlockMeanAverage(const SkBitmap& image, return target; } +std::string GetHashFromBlurredImage( + VisualFeatures::BlurredImage blurred_image) { + DCHECK_EQ(blurred_image.data().size(), + 3u * blurred_image.width() * blurred_image.height()); + // Convert the blurred image to grayscale. + std::vector<uint8_t> luma_values; + luma_values.resize(blurred_image.width() * blurred_image.height()); + for (size_t i = 0; i < luma_values.size(); i++) { + luma_values[i] = color_utils::GetLuma(SkColorSetRGB( + static_cast<unsigned char>(blurred_image.data()[3 * i]), + static_cast<unsigned char>(blurred_image.data()[3 * i + 1]), + static_cast<unsigned char>(blurred_image.data()[3 * i + 2]))); + } + + uint8_t median_luma = GetMedian(luma_values); + + std::string output; + + // The server-side hash generation writes a Varint here, rather than just an + // int. These encodings coincide as long as the width is at most 127. If + // we begin using larger widths, we need to implement the Varint encoding used + // on the server. + DCHECK_LT(blurred_image.width(), 128); + output.push_back(static_cast<unsigned char>(blurred_image.width())); + + // Write the output bitstring. + unsigned char next_byte = 0; + int bits_encoded_so_far = 0; + for (const uint8_t luma_value : luma_values) { + next_byte <<= 1; + if (luma_value >= median_luma) + next_byte |= 1; + + ++bits_encoded_so_far; + if (bits_encoded_so_far == 8) { + output.push_back(next_byte); + bits_encoded_so_far = 0; + } + } + + if (bits_encoded_so_far != 0) { + next_byte <<= 8 - bits_encoded_so_far; + output.push_back(next_byte); + } + + return output; +} + +base::Optional<VisionMatchResult> IsVisualMatch(const SkBitmap& image, + const VisualTarget& target) { + VisualFeatures::BlurredImage blurred_image; + if (!GetBlurredImage(image, &blurred_image)) + return base::nullopt; + std::string hash = GetHashFromBlurredImage(blurred_image); + size_t hash_distance; + bool has_hash_distance = GetHashDistance(hash, target.hash(), &hash_distance); + + VisualFeatures::ColorHistogram histogram; + if (!GetHistogramForImage(image, &histogram)) + return base::nullopt; + + opencv::PointDistribution point_distribution = + HistogramBinsToPointDistribution(histogram.bins()); + base::Optional<double> color_distance = opencv::EMD( + point_distribution, HistogramBinsToPointDistribution(target.bins())); + + for (const MatchRule& match_rule : target.match_config().match_rule()) { + bool is_match = true; + if (match_rule.has_hash_distance()) { + is_match &= + (has_hash_distance && hash_distance <= match_rule.hash_distance()); + } + + if (match_rule.has_color_distance()) { + is_match &= (color_distance.has_value() && + color_distance.value() <= match_rule.color_distance()); + } + + for (const MatchRule::ColorRange& color_range : match_rule.color_range()) { + is_match &= ImageHasColorInRange(image, color_range); + } + + if (is_match) { + VisionMatchResult result; + result.set_matched_target_digest(target.digest()); + if (has_hash_distance) + result.set_vision_matched_phash_score(hash_distance); + if (color_distance.has_value()) + result.set_vision_matched_emd_score(color_distance.value()); + return result; + } + } + + return base::nullopt; +} + } // namespace visual_utils } // namespace safe_browsing diff --git a/chromium/components/safe_browsing/content/password_protection/visual_utils.h b/chromium/components/safe_browsing/content/password_protection/visual_utils.h index 6d2d24fdf40..34b6764efa5 100644 --- a/chromium/components/safe_browsing/content/password_protection/visual_utils.h +++ b/chromium/components/safe_browsing/content/password_protection/visual_utils.h @@ -7,6 +7,8 @@ #include <string> +#include "base/optional.h" +#include "components/safe_browsing/core/proto/client_model.pb.h" #include "components/safe_browsing/core/proto/csd.pb.h" #include "third_party/skia/include/core/SkBitmap.h" @@ -39,6 +41,14 @@ bool GetBlurredImage(const SkBitmap& image, std::unique_ptr<SkBitmap> BlockMeanAverage(const SkBitmap& image, int block_size); +// Returns the hash used to compare blurred images. +std::string GetHashFromBlurredImage(VisualFeatures::BlurredImage blurred_image); + +// Returns whether the given |image| is a match for the |target|. Returns +// nullopt in the case of no match, and the VisionMatchResult if it is a match. +base::Optional<VisionMatchResult> IsVisualMatch(const SkBitmap& image, + const VisualTarget& target); + } // namespace visual_utils } // namespace safe_browsing diff --git a/chromium/components/safe_browsing/content/password_protection/visual_utils_unittest.cc b/chromium/components/safe_browsing/content/password_protection/visual_utils_unittest.cc index 140c97884d2..752e65dbe3e 100644 --- a/chromium/components/safe_browsing/content/password_protection/visual_utils_unittest.cc +++ b/chromium/components/safe_browsing/content/password_protection/visual_utils_unittest.cc @@ -252,5 +252,264 @@ TEST_F(VisualUtilsTest, BlockMeanAveragePartialBlocks) { EXPECT_EQ(*blocks->getAddr32(1, 1), kBlue); } +TEST_F(VisualUtilsTest, IsVisualMatchHash) { + { + // An all-white image should hash to all 1-bits. + for (int x = 0; x < 1000; x++) + for (int y = 0; y < 1000; y++) + *bitmap_.getAddr32(x, y) = kWhite; + + std::vector<unsigned char> target_hash; + target_hash.push_back('\x30'); + for (int i = 0; i < 288; i++) + target_hash.push_back('\xff'); + + VisualTarget target; + target.set_hash(target_hash.data(), target_hash.size()); + target.mutable_match_config()->add_match_rule()->set_hash_distance(0.0); + EXPECT_TRUE(IsVisualMatch(bitmap_, target).has_value()); + } + + { + // Make the top quarter black, and the corresponding bits of the hash should + // be 0. + for (int x = 0; x < 1000; x++) + for (int y = 0; y < 250; y++) + *bitmap_.getAddr32(x, y) = kBlack; + + std::vector<unsigned char> target_hash; + target_hash.push_back('\x30'); + for (int i = 0; i < 72; i++) + target_hash.push_back('\x00'); + for (int i = 0; i < 216; i++) + target_hash.push_back('\xff'); + VisualTarget target; + target.set_hash(target_hash.data(), target_hash.size()); + + target.mutable_match_config()->add_match_rule()->set_hash_distance(0.0); + EXPECT_TRUE(IsVisualMatch(bitmap_, target).has_value()); + } +} + +TEST_F(VisualUtilsTest, IsVisualMatchHashPartialMatch) { + // Make the top quarter black, and the corresponding bits of the hash should + // be 0. + for (int x = 0; x < 1000; x++) + for (int y = 0; y < 250; y++) + *bitmap_.getAddr32(x, y) = kBlack; + for (int x = 0; x < 1000; x++) + for (int y = 250; y < 1000; y++) + *bitmap_.getAddr32(x, y) = kWhite; + + std::vector<unsigned char> target_hash; + target_hash.push_back('\x30'); + for (int i = 0; i < 75; i++) + target_hash.push_back('\x00'); + for (int i = 0; i < 213; i++) + target_hash.push_back('\xff'); + + VisualTarget target; + target.set_hash(target_hash.data(), target_hash.size()); + target.mutable_match_config()->add_match_rule()->set_hash_distance(23.0); + EXPECT_FALSE(IsVisualMatch(bitmap_, target).has_value()); + target.mutable_match_config()->add_match_rule()->set_hash_distance(24.0); + EXPECT_TRUE(IsVisualMatch(bitmap_, target).has_value()); +} + +TEST_F(VisualUtilsTest, IsVisualMatchHashStrideComparison) { + for (int x = 0; x < 1000; x++) + for (int y = 0; y < 1000; y++) + *bitmap_.getAddr32(x, y) = kWhite; + + std::vector<unsigned char> target_hash; + target_hash.push_back('\x30'); + for (int i = 0; i < 288; i++) + target_hash.push_back('\xff'); + + VisualTarget target; + target.set_hash(target_hash.data(), target_hash.size()); + target.mutable_match_config()->add_match_rule()->set_hash_distance(0.0); + EXPECT_TRUE(IsVisualMatch(bitmap_, target).has_value()); + + target_hash[0] = '\x00'; + target.set_hash(target_hash.data(), target_hash.size()); + EXPECT_FALSE(IsVisualMatch(bitmap_, target).has_value()); +} + +TEST_F(VisualUtilsTest, IsVisualMatchHistogramOnly) { + for (int x = 0; x < 1000; x++) + for (int y = 0; y < 1000; y++) + *bitmap_.getAddr32(x, y) = kWhite; + + { + VisualTarget target; + VisualFeatures::ColorHistogramBin* bin = target.add_bins(); + // Our coordinates range from 0 to 999, giving an average of 0.4995 instead + // of 0.5. + bin->set_centroid_x(0.4995); + bin->set_centroid_y(0.4995); + bin->set_quantized_r(7); + bin->set_quantized_g(7); + bin->set_quantized_b(7); + bin->set_weight(1.0); + target.mutable_match_config()->add_match_rule()->set_color_distance(0.0); + EXPECT_TRUE(IsVisualMatch(bitmap_, target).has_value()); + } + + { + // Move the target histogram to have centroid 0,0. This leads to a distance + // just under 0.5 between the actual and target histograms. + VisualTarget target; + VisualFeatures::ColorHistogramBin* bin = target.add_bins(); + bin->set_centroid_x(0); + bin->set_centroid_y(0); + bin->set_quantized_r(7); + bin->set_quantized_g(7); + bin->set_quantized_b(7); + bin->set_weight(1.0); + + MatchRule* match_rule = target.mutable_match_config()->add_match_rule(); + match_rule->set_color_distance(0.5); + EXPECT_TRUE(IsVisualMatch(bitmap_, target).has_value()); + + match_rule->set_color_distance(0.4); + EXPECT_FALSE(IsVisualMatch(bitmap_, target).has_value()); + } + + { + // Change the target histogram color slightly. This leads to a distance + // of roughly 0.12 between the actual and target histogram. + VisualTarget target; + VisualFeatures::ColorHistogramBin* bin = target.add_bins(); + bin->set_centroid_x(0.4995); + bin->set_centroid_y(0.4995); + bin->set_quantized_r(6); + bin->set_quantized_g(6); + bin->set_quantized_b(6); + bin->set_weight(1.0); + + MatchRule* match_rule = target.mutable_match_config()->add_match_rule(); + match_rule->set_color_distance(0.2); + EXPECT_TRUE(IsVisualMatch(bitmap_, target).has_value()); + + match_rule->set_color_distance(0.1); + EXPECT_FALSE(IsVisualMatch(bitmap_, target).has_value()); + } +} + +TEST_F(VisualUtilsTest, IsVisualMatchColorRange) { + for (int x = 0; x < 1000; x++) + for (int y = 0; y < 1000; y++) + *bitmap_.getAddr32(x, y) = kWhite; + *bitmap_.getAddr32(0, 0) = kBlue; + + SkScalar hsv[3]; + SkColorToHSV(bitmap_.getColor(0, 0), hsv); + SkScalar target_hue = hsv[0]; + VisualTarget target; + MatchRule::ColorRange* color_range = + target.mutable_match_config()->add_match_rule()->add_color_range(); + color_range->set_low(target_hue); + color_range->set_high(target_hue); + + // Blue hue present + EXPECT_TRUE(IsVisualMatch(bitmap_, target).has_value()); + + // Color range too high + color_range->set_low(target_hue + 1); + color_range->set_high(target_hue + 1); + EXPECT_FALSE(IsVisualMatch(bitmap_, target).has_value()); + + // Color range too low + color_range->set_low(target_hue - 1); + color_range->set_high(target_hue - 1); + EXPECT_FALSE(IsVisualMatch(bitmap_, target).has_value()); + + // No blue hue present + *bitmap_.getAddr32(0, 0) = kWhite; + EXPECT_FALSE(IsVisualMatch(bitmap_, target).has_value()); +} + +TEST_F(VisualUtilsTest, IsVisualMatchMultipleColorRanges) { + for (int x = 0; x < 1000; x++) + for (int y = 0; y < 1000; y++) + *bitmap_.getAddr32(x, y) = kWhite; + *bitmap_.getAddr32(0, 0) = kBlue; + *bitmap_.getAddr32(1, 0) = kGreen; + + SkScalar hsv[3]; + SkColorToHSV(bitmap_.getColor(0, 0), hsv); + SkScalar blue_hue = hsv[0]; + VisualTarget target; + MatchRule* match_rule = target.mutable_match_config()->add_match_rule(); + MatchRule::ColorRange* color_range = match_rule->add_color_range(); + color_range->set_low(blue_hue); + color_range->set_high(blue_hue); + + SkColorToHSV(bitmap_.getColor(1, 0), hsv); + SkScalar green_hue = hsv[0]; + color_range = match_rule->add_color_range(); + color_range->set_low(green_hue); + color_range->set_high(green_hue); + + // Both hues present + EXPECT_TRUE(IsVisualMatch(bitmap_, target).has_value()); + + // No blue hue present + *bitmap_.getAddr32(0, 0) = kWhite; + EXPECT_FALSE(IsVisualMatch(bitmap_, target).has_value()); + + // No green hue present + *bitmap_.getAddr32(0, 0) = kBlue; + *bitmap_.getAddr32(1, 0) = kWhite; + EXPECT_FALSE(IsVisualMatch(bitmap_, target).has_value()); + + // Neither hue present + *bitmap_.getAddr32(0, 0) = kWhite; + EXPECT_FALSE(IsVisualMatch(bitmap_, target).has_value()); +} + +TEST_F(VisualUtilsTest, IsVisualMatchMultipleMatchRules) { + for (int x = 0; x < 1000; x++) + for (int y = 0; y < 1000; y++) + *bitmap_.getAddr32(x, y) = kWhite; + *bitmap_.getAddr32(0, 0) = kBlue; + *bitmap_.getAddr32(1, 0) = kGreen; + + // Create a target with two match rules, one matching blue pixels and one + // matching green. + VisualTarget target; + MatchRule* match_rule_blue = target.mutable_match_config()->add_match_rule(); + MatchRule::ColorRange* color_range = match_rule_blue->add_color_range(); + SkScalar hsv[3]; + SkColorToHSV(bitmap_.getColor(0, 0), hsv); + SkScalar blue_hue = hsv[0]; + color_range->set_low(blue_hue); + color_range->set_high(blue_hue); + + MatchRule* match_rule_green = target.mutable_match_config()->add_match_rule(); + color_range = match_rule_green->add_color_range(); + SkColorToHSV(bitmap_.getColor(1, 0), hsv); + SkScalar green_hue = hsv[0]; + color_range->set_low(green_hue); + color_range->set_high(green_hue); + + // Both hues present + EXPECT_TRUE(IsVisualMatch(bitmap_, target).has_value()); + + // No blue hue present + *bitmap_.getAddr32(0, 0) = kWhite; + EXPECT_TRUE(IsVisualMatch(bitmap_, target).has_value()); + + // No green hue present + *bitmap_.getAddr32(0, 0) = kBlue; + *bitmap_.getAddr32(1, 0) = kWhite; + EXPECT_TRUE(IsVisualMatch(bitmap_, target).has_value()); + + // Neither hue present + *bitmap_.getAddr32(0, 0) = kWhite; + EXPECT_FALSE(IsVisualMatch(bitmap_, target).has_value()); +} + } // namespace visual_utils } // namespace safe_browsing diff --git a/chromium/components/safe_browsing/content/renderer/websocket_sb_handshake_throttle.cc b/chromium/components/safe_browsing/content/renderer/websocket_sb_handshake_throttle.cc index 4b92b8fcffa..d9fef3975a1 100644 --- a/chromium/components/safe_browsing/content/renderer/websocket_sb_handshake_throttle.cc +++ b/chromium/components/safe_browsing/content/renderer/websocket_sb_handshake_throttle.cc @@ -23,22 +23,9 @@ namespace safe_browsing { WebSocketSBHandshakeThrottle::WebSocketSBHandshakeThrottle( mojom::SafeBrowsing* safe_browsing, int render_frame_id) - : render_frame_id_(render_frame_id), - safe_browsing_(safe_browsing), - result_(Result::UNKNOWN) {} + : render_frame_id_(render_frame_id), safe_browsing_(safe_browsing) {} -WebSocketSBHandshakeThrottle::~WebSocketSBHandshakeThrottle() { - // ThrottleHandshake() should always be called, but since that is done all the - // way over in Blink, just avoid logging if it is not called rather than - // DCHECK()ing. - if (start_time_.is_null()) - return; - if (result_ == Result::UNKNOWN) { - result_ = Result::ABANDONED; - UMA_HISTOGRAM_TIMES("SafeBrowsing.WebSocket.Elapsed.Abandoned", - base::TimeTicks::Now() - start_time_); - } -} +WebSocketSBHandshakeThrottle::~WebSocketSBHandshakeThrottle() = default; void WebSocketSBHandshakeThrottle::ThrottleHandshake( const blink::WebURL& url, @@ -48,7 +35,8 @@ void WebSocketSBHandshakeThrottle::ThrottleHandshake( completion_callback_ = std::move(completion_callback); url_ = url; int load_flags = 0; - start_time_ = base::TimeTicks::Now(); + DCHECK_EQ(state_, State::kInitial); + state_ = State::kStarted; safe_browsing_->CreateCheckerAndCheck( render_frame_id_, url_checker_.BindNewPipeAndPassReceiver(), url, "GET", net::HttpRequestHeaders(), load_flags, @@ -65,17 +53,14 @@ void WebSocketSBHandshakeThrottle::ThrottleHandshake( void WebSocketSBHandshakeThrottle::OnCompleteCheck(bool proceed, bool showed_interstitial) { - DCHECK(!start_time_.is_null()); - base::TimeDelta elapsed = base::TimeTicks::Now() - start_time_; + DCHECK_EQ(state_, State::kStarted); if (proceed) { - result_ = Result::SAFE; - UMA_HISTOGRAM_TIMES("SafeBrowsing.WebSocket.Elapsed.Safe", elapsed); + state_ = State::kSafe; std::move(completion_callback_).Run(base::nullopt); } else { // When the insterstitial is dismissed the page is navigated and this object // is destroyed before reaching here. - result_ = Result::BLOCKED; - UMA_HISTOGRAM_TIMES("SafeBrowsing.WebSocket.Elapsed.Blocked", elapsed); + state_ = State::kBlocked; std::move(completion_callback_) .Run(blink::WebString::FromUTF8(base::StringPrintf( "WebSocket connection to %s failed safe browsing check", @@ -93,7 +78,8 @@ void WebSocketSBHandshakeThrottle::OnCheckResult( return; } - // TODO(yzshen): Notify the network service to pause processing response body. + // TODO(yzshen): Notify the network service to stop reading from the + // WebSocket. if (!notifier_receiver_) { notifier_receiver_ = std::make_unique<mojo::Receiver<mojom::UrlCheckNotifier>>(this); @@ -102,14 +88,12 @@ void WebSocketSBHandshakeThrottle::OnCheckResult( } void WebSocketSBHandshakeThrottle::OnMojoDisconnect() { - DCHECK_EQ(result_, Result::UNKNOWN); + DCHECK(state_ == State::kStarted); url_checker_.reset(); notifier_receiver_.reset(); - // Make the destructor record NOT_SUPPORTED in the result histogram. - result_ = Result::NOT_SUPPORTED; - // Don't record the time elapsed because it's unlikely to be meaningful. + state_ = State::kNotSupported; std::move(completion_callback_).Run(base::nullopt); // |this| is destroyed here. } diff --git a/chromium/components/safe_browsing/content/renderer/websocket_sb_handshake_throttle.h b/chromium/components/safe_browsing/content/renderer/websocket_sb_handshake_throttle.h index 53f7761c78b..c703942d2e0 100644 --- a/chromium/components/safe_browsing/content/renderer/websocket_sb_handshake_throttle.h +++ b/chromium/components/safe_browsing/content/renderer/websocket_sb_handshake_throttle.h @@ -35,14 +35,12 @@ class WebSocketSBHandshakeThrottle : public blink::WebSocketHandshakeThrottle, completion_callback) override; private: - // These values are logged to UMA so do not renumber or reuse. - enum class Result { - UNKNOWN = 0, - SAFE = 1, - BLOCKED = 2, - ABANDONED = 3, - NOT_SUPPORTED = 4, - RESULT_COUNT + enum class State { + kInitial, + kStarted, + kSafe, + kBlocked, + kNotSupported, }; // mojom::UrlCheckNotifier implementation. @@ -60,8 +58,10 @@ class WebSocketSBHandshakeThrottle : public blink::WebSocketHandshakeThrottle, mojo::Remote<mojom::SafeBrowsingUrlChecker> url_checker_; mojom::SafeBrowsing* safe_browsing_; std::unique_ptr<mojo::Receiver<mojom::UrlCheckNotifier>> notifier_receiver_; - base::TimeTicks start_time_; - Result result_; + + // |state_| is used to validate that events happen in the right order. It + // isn't used to control the behaviour of the class. + State state_ = State::kInitial; base::WeakPtrFactory<WebSocketSBHandshakeThrottle> weak_factory_{this}; diff --git a/chromium/components/safe_browsing/content/triggers/ad_popup_trigger.cc b/chromium/components/safe_browsing/content/triggers/ad_popup_trigger.cc index ce197ab32b7..3206c87eb4e 100644 --- a/chromium/components/safe_browsing/content/triggers/ad_popup_trigger.cc +++ b/chromium/components/safe_browsing/content/triggers/ad_popup_trigger.cc @@ -14,7 +14,6 @@ #include "base/rand_util.h" #include "base/single_thread_task_runner.h" #include "base/strings/string_number_conversions.h" -#include "base/task/post_task.h" #include "components/safe_browsing/content/triggers/trigger_util.h" #include "components/safe_browsing/core/features.h" #include "components/safe_browsing/core/triggers/trigger_manager.h" @@ -68,8 +67,7 @@ AdPopupTrigger::AdPopupTrigger( prefs_(prefs), url_loader_factory_(url_loader_factory), history_service_(history_service), - task_runner_( - base::CreateSingleThreadTaskRunner({content::BrowserThread::UI})) {} + task_runner_(content::GetUIThreadTaskRunner({})) {} AdPopupTrigger::~AdPopupTrigger() {} diff --git a/chromium/components/safe_browsing/content/triggers/ad_redirect_trigger.cc b/chromium/components/safe_browsing/content/triggers/ad_redirect_trigger.cc index a281138194a..842274ef16f 100644 --- a/chromium/components/safe_browsing/content/triggers/ad_redirect_trigger.cc +++ b/chromium/components/safe_browsing/content/triggers/ad_redirect_trigger.cc @@ -14,7 +14,6 @@ #include "base/rand_util.h" #include "base/single_thread_task_runner.h" #include "base/strings/string_number_conversions.h" -#include "base/task/post_task.h" #include "components/safe_browsing/content/triggers/trigger_util.h" #include "components/safe_browsing/core/features.h" #include "components/safe_browsing/core/triggers/trigger_manager.h" @@ -67,8 +66,7 @@ AdRedirectTrigger::AdRedirectTrigger( prefs_(prefs), url_loader_factory_(url_loader_factory), history_service_(history_service), - task_runner_( - base::CreateSingleThreadTaskRunner({content::BrowserThread::UI})) {} + task_runner_(content::GetUIThreadTaskRunner({})) {} AdRedirectTrigger::~AdRedirectTrigger() {} diff --git a/chromium/components/safe_browsing/content/triggers/ad_sampler_trigger.cc b/chromium/components/safe_browsing/content/triggers/ad_sampler_trigger.cc index afefe48ba55..7cf7372f043 100644 --- a/chromium/components/safe_browsing/content/triggers/ad_sampler_trigger.cc +++ b/chromium/components/safe_browsing/content/triggers/ad_sampler_trigger.cc @@ -14,7 +14,6 @@ #include "base/rand_util.h" #include "base/single_thread_task_runner.h" #include "base/strings/string_number_conversions.h" -#include "base/task/post_task.h" #include "components/safe_browsing/content/triggers/trigger_util.h" #include "components/safe_browsing/core/features.h" #include "components/safe_browsing/core/triggers/trigger_manager.h" @@ -94,8 +93,7 @@ AdSamplerTrigger::AdSamplerTrigger( prefs_(prefs), url_loader_factory_(url_loader_factory), history_service_(history_service), - task_runner_( - base::CreateSingleThreadTaskRunner({content::BrowserThread::UI})) {} + task_runner_(content::GetUIThreadTaskRunner({})) {} AdSamplerTrigger::~AdSamplerTrigger() {} diff --git a/chromium/components/safe_browsing/content/triggers/suspicious_site_trigger.cc b/chromium/components/safe_browsing/content/triggers/suspicious_site_trigger.cc index e531a18b18b..f144ab0f24b 100644 --- a/chromium/components/safe_browsing/content/triggers/suspicious_site_trigger.cc +++ b/chromium/components/safe_browsing/content/triggers/suspicious_site_trigger.cc @@ -8,7 +8,6 @@ #include "base/memory/ptr_util.h" #include "base/metrics/histogram_macros.h" #include "base/single_thread_task_runner.h" -#include "base/task/post_task.h" #include "components/history/core/browser/history_service.h" #include "components/prefs/pref_service.h" #include "components/safe_browsing/core/triggers/trigger_manager.h" @@ -68,8 +67,7 @@ SuspiciousSiteTrigger::SuspiciousSiteTrigger( prefs_(prefs), url_loader_factory_(url_loader_factory), history_service_(history_service), - task_runner_( - base::CreateSingleThreadTaskRunner({content::BrowserThread::UI})) {} + task_runner_(content::GetUIThreadTaskRunner({})) {} SuspiciousSiteTrigger::~SuspiciousSiteTrigger() {} diff --git a/chromium/components/safe_browsing/content/web_ui/BUILD.gn b/chromium/components/safe_browsing/content/web_ui/BUILD.gn index aa148b1849d..1658cb6b147 100644 --- a/chromium/components/safe_browsing/content/web_ui/BUILD.gn +++ b/chromium/components/safe_browsing/content/web_ui/BUILD.gn @@ -12,6 +12,7 @@ static_library("web_ui") { deps = [ "//base", + "//components/enterprise/common/proto:connectors_proto", "//components/password_manager/core/browser:hash_password_manager", "//components/resources:components_resources_grit", "//components/resources:components_scaled_resources_grit", diff --git a/chromium/components/safe_browsing/content/web_ui/DEPS b/chromium/components/safe_browsing/content/web_ui/DEPS index 4019a99d985..c4dfe28ac40 100644 --- a/chromium/components/safe_browsing/content/web_ui/DEPS +++ b/chromium/components/safe_browsing/content/web_ui/DEPS @@ -1,4 +1,5 @@ include_rules = [ + "+components/enterprise/common/proto/connectors.pb.h", "+components/grit/components_resources.h", "+components/password_manager/core/browser/hash_password_manager.h", "+components/user_prefs", diff --git a/chromium/components/safe_browsing/content/web_ui/resources/safe_browsing.css b/chromium/components/safe_browsing/content/web_ui/resources/safe_browsing.css index 6e6c6e0b70d..2d864d2f6aa 100644 --- a/chromium/components/safe_browsing/content/web_ui/resources/safe_browsing.css +++ b/chromium/components/safe_browsing/content/web_ui/resources/safe_browsing.css @@ -4,7 +4,7 @@ body { color: rgb(48, 57, 66); - margin:15px; + margin: 15px; } p { white-space: pre-wrap; @@ -13,25 +13,36 @@ p { background-color: #fbfbfb; border: 1px solid #cecece; border-radius: 3px; - padding: 19px; line-height: 1.5; + padding: 19px; } #sb-title { font-size: 2em; margin-bottom: 0.8em; } -h1, h2, h3, p { +h1, +h2, +h3, +p { font-weight: normal; line-height: 1.5; } table.request-response { - table-layout:fixed; - width: 100%; - word-break:break-all; - white-space:pre-wrap; border: 1px solid #cecece; border-radius: 3px; + table-layout: fixed; + white-space: pre-wrap; + width: 100%; + word-break: break-all; } table.request-response td { width: 50%; } +.bold-span { + font-weight: bold; +} +.result-container { + font-weight: normal; + line-height: 1.5; + white-space: normal; +} diff --git a/chromium/components/safe_browsing/content/web_ui/resources/safe_browsing.html b/chromium/components/safe_browsing/content/web_ui/resources/safe_browsing.html index df4d006c887..75689ec8485 100644 --- a/chromium/components/safe_browsing/content/web_ui/resources/safe_browsing.html +++ b/chromium/components/safe_browsing/content/web_ui/resources/safe_browsing.html @@ -35,21 +35,21 @@ <tabpanel> <h2>Experiments</h2> <div class="content"> - <p id="experiments-list"></p> + <p id="experiments-list" class="result-container"></p> </div> <h2>Preferences</h2> <div class="content"> - <p id="preferences-list"></p> + <p id="preferences-list" class="result-container"></p> </div> <h2>Safe Browsing Cookie</h2> <div class="content"> - <p id="cookie-panel"></p> + <p id="cookie-panel" class="result-container"></p> </div> </tabpanel> <tabpanel> <h2>Database Manager</h2> <div class="content"> - <p id="database-info-list"></p> + <p id="database-info-list" class="result-container"></p> </div> </tabpanel> <tabpanel> @@ -92,7 +92,7 @@ </tabpanel> <tabpanel> <h2>RT Lookup Pings</h2> - <p id="rt-lookup-experiment-enabled"></p> + <p id="rt-lookup-experiment-enabled" class="result-container"></p> <table id="rt-lookup-ping-list" class="request-response"></table> </tabpanel> <tabpanel> @@ -123,6 +123,24 @@ </tabpanel> </tabpanels> </tabbox> + <template id="result-template"> + <div> + <span class="bold-span"></span> + <span></span> + </div> + </template> + <template id="cookie-template"> + <div> + <span class="bold-span">Value: </span> + <span class="result"></span> + </div> + <span class="bold-span">Created: </span> + <span class="result"></span> + </template> + <template id="rt-lookup-template"> + <span class="bold-span">RT Lookup Experiment Enabled: </span> + <span id="experiment-bool"></span> + </template> <script src="safe_browsing.js"></script> </body> </html> diff --git a/chromium/components/safe_browsing/content/web_ui/resources/safe_browsing.js b/chromium/components/safe_browsing/content/web_ui/resources/safe_browsing.js index bdb119528f1..f5ec1b4274d 100644 --- a/chromium/components/safe_browsing/content/web_ui/resources/safe_browsing.js +++ b/chromium/components/safe_browsing/content/web_ui/resources/safe_browsing.js @@ -161,62 +161,79 @@ cr.define('safe_browsing', function() { function addExperiments(result) { const resLength = result.length; - let experimentsListFormatted = ''; for (let i = 0; i < resLength; i += 2) { - experimentsListFormatted += "<div><b>" + result[i + 1] + - "</b>: " + result[i] + "</div>"; + const experimentsListFormatted = + $('result-template').content.cloneNode(true); + experimentsListFormatted.querySelectorAll('span')[0].textContent = + result[i + 1] + ': '; + experimentsListFormatted.querySelectorAll('span')[1].textContent = + result[i]; + $('experiments-list').appendChild(experimentsListFormatted); } - $('experiments-list').innerHTML = experimentsListFormatted; } function addPrefs(result) { const resLength = result.length; - let preferencesListFormatted = ""; for (let i = 0; i < resLength; i += 2) { - preferencesListFormatted += "<div><b>" + result[i + 1] + "</b>: " + - result[i] + "</div>"; + const preferencesListFormatted = + $('result-template').content.cloneNode(true); + preferencesListFormatted.querySelectorAll('span')[0].textContent = + result[i + 1] + ': '; + preferencesListFormatted.querySelectorAll('span')[1].textContent = + result[i]; + $('preferences-list').appendChild(preferencesListFormatted); } - $('preferences-list').innerHTML = preferencesListFormatted; } function addCookie(result) { - const cookieFormatted = '<b>Value:</b> ' + result[0] + '\n' + - '<b>Created:</b> ' + (new Date(result[1])).toLocaleString(); - $('cookie-panel').innerHTML = cookieFormatted; + const cookieFormatted = $('cookie-template').content.cloneNode(true); + cookieFormatted.querySelectorAll('.result')[0].textContent = result[0]; + cookieFormatted.querySelectorAll('.result')[1].textContent = + (new Date(result[1])).toLocaleString(); + $('cookie-panel').appendChild(cookieFormatted); } function addSavedPasswords(result) { const resLength = result.length; - let savedPasswordFormatted = ""; for (let i = 0; i < resLength; i += 2) { - savedPasswordFormatted += "<div>" + result[i]; - if (result[i+1]) { - savedPasswordFormatted += " (GAIA password)"; - } else { - savedPasswordFormatted += " (Enterprise password)"; - } - savedPasswordFormatted += "</div>"; + const savedPasswordFormatted = document.createElement('div'); + const suffix = result[i + 1] ? 'GAIA password' : 'Enterprise password'; + savedPasswordFormatted.textContent = `${result[i]} (${suffix})`; + $('saved-passwords').appendChild(savedPasswordFormatted); } - - $('saved-passwords').innerHTML = savedPasswordFormatted; } function addDatabaseManagerInfo(result) { const resLength = result.length; - let preferencesListFormatted = ""; for (let i = 0; i < resLength; i += 2) { - preferencesListFormatted += "<div><b>" + result[i] + "</b>: " + - result[i + 1] + "</div>"; + const preferencesListFormatted = + $('result-template').content.cloneNode(true); + preferencesListFormatted.querySelectorAll('span')[0].textContent = + result[i] + ': '; + const value = result[i + 1]; + if (Array.isArray(value)) { + const blockQuote = document.createElement('blockquote'); + value.forEach(item => { + const div = document.createElement('div'); + div.textContent = item; + blockQuote.appendChild(div); + }); + preferencesListFormatted.querySelectorAll('span')[1].appendChild( + blockQuote); + } else { + preferencesListFormatted.querySelectorAll('span')[1].textContent = + value; + } + $('database-info-list').appendChild(preferencesListFormatted); } - $('database-info-list').innerHTML = preferencesListFormatted; } function addFullHashCacheInfo(result) { - $('full-hash-cache-info').innerHTML = result; + $('full-hash-cache-info').textContent = result; } function addSentClientDownloadRequestsInfo(result) { @@ -307,8 +324,9 @@ cr.define('safe_browsing', function() { } function addRTLookupExperimentEnabled(enabled) { - const enabledFormatted = '<b>RT Lookup Experiment Enabled:</b> ' + enabled; - $('rt-lookup-experiment-enabled').innerHTML = enabledFormatted; + const enabledFormatted = $('rt-lookup-template').content.cloneNode(true); + enabledFormatted.querySelector('#experiment-bool').textContent = enabled; + $('rt-lookup-experiment-enabled').appendChild(enabledFormatted); } function addLogMessage(result) { @@ -339,7 +357,8 @@ cr.define('safe_browsing', function() { cr.sendWithPromise('getReferrerChain', $('referrer-chain-url').value) .then((response) => { - $('referrer-chain-content').innerHTML = response; + $('referrer-chain-content').innerHTML = trustedTypes.emptyHTML; + $('referrer-chain-content').textContent = response; }); } diff --git a/chromium/components/safe_browsing/content/web_ui/safe_browsing_ui.cc b/chromium/components/safe_browsing/content/web_ui/safe_browsing_ui.cc index 8dd703da6b7..c04f24f0556 100644 --- a/chromium/components/safe_browsing/content/web_ui/safe_browsing_ui.cc +++ b/chromium/components/safe_browsing/content/web_ui/safe_browsing_ui.cc @@ -14,6 +14,7 @@ #include "base/base64url.h" #include "base/bind.h" #include "base/callback.h" +#include "base/i18n/number_formatting.h" #include "base/i18n/time_formatting.h" #include "base/json/json_string_value_serializer.h" #include "base/memory/ref_counted.h" @@ -21,9 +22,9 @@ #include "base/stl_util.h" #include "base/strings/string_number_conversions.h" #include "base/strings/utf_string_conversions.h" -#include "base/task/post_task.h" #include "base/time/time.h" #include "base/values.h" +#include "components/enterprise/common/proto/connectors.pb.h" #include "components/grit/components_resources.h" #include "components/grit/components_scaled_resources.h" #include "components/password_manager/core/browser/hash_password_manager.h" @@ -219,8 +220,8 @@ void WebUIInfoSingleton::LogMessage(const std::string& message) { base::Time timestamp = base::Time::Now(); log_messages_.push_back(std::make_pair(timestamp, message)); - base::PostTask(FROM_HERE, {content::BrowserThread::UI}, - base::BindOnce(&WebUIInfoSingleton::NotifyLogMessageListeners, + content::GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&WebUIInfoSingleton::NotifyLogMessageListeners, timestamp, message)); } @@ -271,6 +272,26 @@ void WebUIInfoSingleton::AddToDeepScanRequests( request.request_token(), deep_scan_requests_[request.request_token()]); } +void WebUIInfoSingleton::AddToDeepScanRequests( + const enterprise_connectors::ContentAnalysisRequest& request) { + if (!HasListener()) + return; + + // Only update the request time the first time we see a token. + if (deep_scan_requests_.find(request.request_token()) == + deep_scan_requests_.end()) { + deep_scan_requests_[request.request_token()].request_time = + base::Time::Now(); + } + + deep_scan_requests_[request.request_token()].content_analysis_request = + request; + + for (auto* webui_listener : webui_instances_) + webui_listener->NotifyDeepScanJsListener( + request.request_token(), deep_scan_requests_[request.request_token()]); +} + void WebUIInfoSingleton::AddToDeepScanResponses( const std::string& token, const std::string& status, @@ -286,6 +307,21 @@ void WebUIInfoSingleton::AddToDeepScanResponses( webui_listener->NotifyDeepScanJsListener(token, deep_scan_requests_[token]); } +void WebUIInfoSingleton::AddToDeepScanResponses( + const std::string& token, + const std::string& status, + const enterprise_connectors::ContentAnalysisResponse& response) { + if (!HasListener()) + return; + + deep_scan_requests_[token].response_time = base::Time::Now(); + deep_scan_requests_[token].response_status = status; + deep_scan_requests_[token].content_analysis_response = response; + + for (auto* webui_listener : webui_instances_) + webui_listener->NotifyDeepScanJsListener(token, deep_scan_requests_[token]); +} + void WebUIInfoSingleton::ClearDeepScans() { base::flat_map<std::string, DeepScanDebugData>().swap(deep_scan_requests_); } @@ -369,34 +405,33 @@ void AddStoreInfo(const DatabaseManagerInfo::DatabaseInfo::StoreInfo store_info, database_info_list->Append(base::Value("Unknown store")); } - std::string store_info_string = "<blockquote>"; + base::Value store_info_list(base::Value::Type::LIST); if (store_info.has_file_size_bytes()) { - store_info_string += - "Size (in bytes): " + std::to_string(store_info.file_size_bytes()) + - "<br>"; + store_info_list.Append( + "Size (in bytes): " + + base::UTF16ToUTF8(base::FormatNumber(store_info.file_size_bytes()))); } if (store_info.has_update_status()) { - store_info_string += - "Update status: " + std::to_string(store_info.update_status()) + "<br>"; + store_info_list.Append( + "Update status: " + + base::UTF16ToUTF8(base::FormatNumber(store_info.update_status()))); } if (store_info.has_last_apply_update_time_millis()) { - store_info_string += "Last update time: " + - UserReadableTimeFromMillisSinceEpoch( - store_info.last_apply_update_time_millis()) - .GetString() + - "<br>"; + store_info_list.Append("Last update time: " + + UserReadableTimeFromMillisSinceEpoch( + store_info.last_apply_update_time_millis()) + .GetString()); } if (store_info.has_checks_attempted()) { - store_info_string += "Number of database checks: " + - std::to_string(store_info.checks_attempted()) + "<br>"; + store_info_list.Append( + "Number of database checks: " + + base::UTF16ToUTF8(base::FormatNumber(store_info.checks_attempted()))); } - store_info_string += "</blockquote>"; - - database_info_list->Append(base::Value(store_info_string)); + database_info_list->Append(std::move(store_info_list)); } void AddDatabaseInfo(const DatabaseManagerInfo::DatabaseInfo database_info, @@ -1304,6 +1339,52 @@ base::Value SerializeReportingEvent(const base::Value& event) { } #if BUILDFLAG(FULL_SAFE_BROWSING) +std::string SerializeContentAnalysisRequest( + const enterprise_connectors::ContentAnalysisRequest& request) { + base::DictionaryValue request_dict; + + request_dict.SetKey("device_token", base::Value(request.device_token())); + request_dict.SetKey("fcm_notification_token", + base::Value(request.fcm_notification_token())); + switch (request.analysis_connector()) { + case enterprise_connectors::ANALYSIS_CONNECTOR_UNSPECIFIED: + request_dict.SetStringKey("analysis_connector", "UNSPECIFIED"); + break; + case enterprise_connectors::FILE_ATTACHED: + request_dict.SetStringKey("analysis_connector", "FILE_ATTACHED"); + break; + case enterprise_connectors::FILE_DOWNLOADED: + request_dict.SetStringKey("analysis_connector", "FILE_DOWNLOADED"); + break; + case enterprise_connectors::BULK_DATA_ENTRY: + request_dict.SetStringKey("analysis_connector", "BULK_DATA_ENTRY"); + break; + } + + if (request.has_request_data()) { + base::DictionaryValue request_data; + request_data.SetStringKey("url", request.request_data().url()); + request_data.SetStringKey("filename", request.request_data().filename()); + request_data.SetStringKey("digest", request.request_data().digest()); + // TODO(domfc): Improve this once csd is populated for this proto. + request_data.SetStringKey("csd", + request.request_data().csd().SerializeAsString()); + request_dict.SetKey("request_data", std::move(request_data)); + } + + base::ListValue tags; + for (const std::string& tag : request.tags()) + tags.Append(base::Value(tag)); + request_dict.SetKey("tags", std::move(tags)); + request_dict.SetKey("request_token", base::Value(request.request_token())); + + std::string request_serialized; + JSONStringValueSerializer serializer(&request_serialized); + serializer.set_pretty_print(true); + serializer.Serialize(request_dict); + return request_serialized; +} + std::string SerializeDeepScanningRequest( const DeepScanningClientRequest& request) { base::DictionaryValue request_dict; @@ -1362,6 +1443,68 @@ std::string SerializeDeepScanningRequest( return request_serialized; } +std::string SerializeContentAnalysisResponse( + const enterprise_connectors::ContentAnalysisResponse& response) { + base::DictionaryValue response_dict; + + response_dict.SetStringKey("token", response.request_token()); + + base::ListValue result_values; + for (const auto& result : response.results()) { + base::DictionaryValue result_value; + switch (result.status()) { + case enterprise_connectors::ContentAnalysisResponse::Result:: + STATUS_UNKNOWN: + result_value.SetStringKey("status", "STATUS_UNKNOWN"); + break; + case enterprise_connectors::ContentAnalysisResponse::Result::SUCCESS: + result_value.SetStringKey("status", "SUCCESS"); + break; + case enterprise_connectors::ContentAnalysisResponse::Result::FAILURE: + result_value.SetStringKey("status", "FAILURE"); + break; + } + result_value.SetStringKey("tag", result.tag()); + + base::ListValue triggered_rules; + for (const auto& rule : result.triggered_rules()) { + base::DictionaryValue rule_value; + + switch (rule.action()) { + case enterprise_connectors::ContentAnalysisResponse::Result:: + TriggeredRule::ACTION_UNSPECIFIED: + rule_value.SetStringKey("action", "ACTION_UNSPECIFIED"); + break; + case enterprise_connectors::ContentAnalysisResponse::Result:: + TriggeredRule::REPORT_ONLY: + rule_value.SetStringKey("action", "REPORT_ONLY"); + break; + case enterprise_connectors::ContentAnalysisResponse::Result:: + TriggeredRule::WARN: + rule_value.SetStringKey("action", "WARN"); + break; + case enterprise_connectors::ContentAnalysisResponse::Result:: + TriggeredRule::BLOCK: + rule_value.SetStringKey("action", "BLOCK"); + break; + } + + rule_value.SetStringKey("rule_name", rule.rule_name()); + rule_value.SetStringKey("rule_id", rule.rule_id()); + triggered_rules.Append(std::move(rule_value)); + } + result_value.SetKey("triggered_rules", std::move(triggered_rules)); + result_values.Append(std::move(result_value)); + } + response_dict.SetKey("results", std::move(result_values)); + + std::string response_serialized; + JSONStringValueSerializer serializer(&response_serialized); + serializer.set_pretty_print(true); + serializer.Serialize(response_dict); + return response_serialized; +} + std::string SerializeDeepScanningResponse( const DeepScanningClientResponse& response) { base::DictionaryValue response_dict; @@ -1471,6 +1614,9 @@ base::Value SerializeDeepScanDebugData(const std::string& token, if (data.request.has_value()) { value.SetStringKey("request", SerializeDeepScanningRequest(data.request.value())); + } else if (data.content_analysis_request.has_value()) { + value.SetStringKey("request", SerializeContentAnalysisRequest( + data.content_analysis_request.value())); } if (!data.response_time.is_null()) { @@ -1484,6 +1630,9 @@ base::Value SerializeDeepScanDebugData(const std::string& token, if (data.response.has_value()) { value.SetStringKey("response", SerializeDeepScanningResponse(data.response.value())); + } else if (data.content_analysis_response.has_value()) { + value.SetStringKey("response", SerializeContentAnalysisResponse( + data.content_analysis_response.value())); } return std::move(value); diff --git a/chromium/components/safe_browsing/content/web_ui/safe_browsing_ui.h b/chromium/components/safe_browsing/content/web_ui/safe_browsing_ui.h index d7b296688be..8e10f4700ff 100644 --- a/chromium/components/safe_browsing/content/web_ui/safe_browsing_ui.h +++ b/chromium/components/safe_browsing/content/web_ui/safe_browsing_ui.h @@ -7,6 +7,7 @@ #include "base/bind.h" #include "base/macros.h" +#include "components/enterprise/common/proto/connectors.pb.h" #include "components/safe_browsing/buildflags.h" #include "components/safe_browsing/core/browser/safe_browsing_network_context.h" #include "components/safe_browsing/core/proto/csd.pb.h" @@ -42,10 +43,14 @@ struct DeepScanDebugData { base::Time request_time; base::Optional<DeepScanningClientRequest> request; + base::Optional<enterprise_connectors::ContentAnalysisRequest> + content_analysis_request; base::Time response_time; std::string response_status; base::Optional<DeepScanningClientResponse> response; + base::Optional<enterprise_connectors::ContentAnalysisResponse> + content_analysis_response; }; #endif @@ -327,12 +332,18 @@ class WebUIInfoSingleton { // identifier that can be used in |AddToDeepScanResponses| to correlate a ping // and response. void AddToDeepScanRequests(const DeepScanningClientRequest& request); + void AddToDeepScanRequests( + const enterprise_connectors::ContentAnalysisRequest& request); // Add the new response to |deep_scan_requests_| and send it to all the open // chrome://safe-browsing tabs. void AddToDeepScanResponses(const std::string& token, const std::string& status, const DeepScanningClientResponse& response); + void AddToDeepScanResponses( + const std::string& token, + const std::string& status, + const enterprise_connectors::ContentAnalysisResponse& response); // Clear the list of deep scan requests and responses. void ClearDeepScans(); |