blob: fabf9c5a643e5a79c23b0e56d334c7cbf86a2b2f [file] [log] [blame]
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// This test creates a fake safebrowsing service, where we can inject known-
// threat urls. It then uses a real browser to go to these urls, and sends
// "goback" or "proceed" commands and verifies they work.
#include <algorithm>
#include <map>
#include <memory>
#include <optional>
#include <string_view>
#include <utility>
#include "base/command_line.h"
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/simple_test_clock.h"
#include "base/test/test_future.h"
#include "base/values.h"
#include "build/build_config.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/history/history_service_factory.h"
#include "chrome/browser/interstitials/security_interstitial_idn_test.h"
#include "chrome/browser/password_manager/password_manager_test_base.h"
#include "chrome/browser/password_manager/passwords_navigation_observer.h"
#include "chrome/browser/policy/dm_token_utils.h"
#include "chrome/browser/policy/policy_test_utils.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/renderer_context_menu/render_view_context_menu_test_util.h"
#include "chrome/browser/safe_browsing/chrome_safe_browsing_blocking_page_factory.h"
#include "chrome/browser/safe_browsing/safe_browsing_metrics_collector_factory.h"
#include "chrome/browser/safe_browsing/safe_browsing_navigation_observer_manager_factory.h"
#include "chrome/browser/safe_browsing/test_safe_browsing_service.h"
#include "chrome/browser/safe_browsing/url_lookup_service_factory.h"
#include "chrome/browser/safe_browsing/user_interaction_observer.h"
#include "chrome/browser/safe_browsing/verdict_cache_manager_factory.h"
#include "chrome/browser/ssl/cert_verifier_browser_test.h"
#include "chrome/browser/ssl/security_state_tab_helper.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/browser_tabstrip.h"
#include "chrome/browser/ui/hats/mock_trust_safety_sentiment_service.h"
#include "chrome/browser/ui/hats/trust_safety_sentiment_service_factory.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/views/location_bar/location_icon_view.h"
#include "chrome/browser/ui/views/page_info/page_info_bubble_view.h"
#include "chrome/browser/ui/views/page_info/page_info_bubble_view_base.h"
#include "chrome/browser/ui/views/page_info/page_info_view_factory.h"
#include "chrome/browser/ui/views/toolbar/toolbar_view.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/google/core/common/google_util.h"
#include "components/grit/components_resources.h"
#include "components/omnibox/browser/omnibox_prefs.h"
#include "components/page_info/core/features.h"
#include "components/permissions/permission_util.h"
#include "components/policy/core/common/policy_map.h"
#include "components/policy/core/common/policy_types.h"
#include "components/policy/policy_constants.h"
#include "components/prefs/pref_service.h"
#include "components/safe_browsing/content/browser/async_check_tracker.h"
#include "components/safe_browsing/content/browser/safe_browsing_blocking_page.h"
#include "components/safe_browsing/content/browser/safe_browsing_blocking_page_factory.h"
#include "components/safe_browsing/content/browser/threat_details.h"
#include "components/safe_browsing/content/browser/ui_manager.h"
#include "components/safe_browsing/content/browser/unsafe_resource_util.h"
#include "components/safe_browsing/content/common/safe_browsing.mojom.h"
#include "components/safe_browsing/core/browser/db/database_manager.h"
#include "components/safe_browsing/core/browser/db/fake_database_manager.h"
#include "components/safe_browsing/core/browser/db/util.h"
#include "components/safe_browsing/core/browser/db/v4_protocol_manager_util.h"
#include "components/safe_browsing/core/browser/safe_browsing_metrics_collector.h"
#include "components/safe_browsing/core/browser/verdict_cache_manager.h"
#include "components/safe_browsing/core/common/features.h"
#include "components/safe_browsing/core/common/hashprefix_realtime/hash_realtime_utils.h"
#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
#include "components/safe_browsing/core/common/web_ui_constants.h"
#include "components/security_interstitials/content/security_interstitial_controller_client.h"
#include "components/security_interstitials/content/security_interstitial_tab_helper.h"
#include "components/security_interstitials/content/ssl_blocking_page.h"
#include "components/security_interstitials/core/controller_client.h"
#include "components/security_interstitials/core/metrics_helper.h"
#include "components/security_interstitials/core/unsafe_resource.h"
#include "components/security_interstitials/core/urls.h"
#include "components/security_state/core/security_state.h"
#include "components/strings/grit/components_strings.h"
#include "components/ukm/test_ukm_recorder.h"
#include "components/unified_consent/pref_names.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/disallow_activation_reason.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/fenced_frame_test_util.h"
#include "content/public/test/prerender_test_util.h"
#include "content/public/test/render_view_test.h"
#include "content/public/test/test_frame_navigation_observer.h"
#include "content/public/test/test_navigation_observer.h"
#include "content/public/test/test_utils.h"
#include "net/cert/cert_verify_result.h"
#include "net/cert/mock_cert_verifier.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/url_request/url_request_mock_http_job.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/public/mojom/fetch_api.mojom.h"
#include "testing/gmock/include/gmock/gmock-matchers.h"
#include "third_party/blink/public/common/features.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/window_open_disposition.h"
#include "ui/events/test/test_event.h"
#include "ui/views/controls/styled_label.h"
#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "chrome/browser/metrics/desktop_session_duration/desktop_session_duration_tracker.h"
#endif
using chrome_browser_interstitials::SecurityInterstitialIDNTest;
using content::BrowserThread;
using content::NavigationController;
using content::RenderFrameHost;
using content::WebContents;
using security_interstitials::BaseSafeBrowsingErrorUI;
namespace safe_browsing {
namespace {
const char kEmptyPage[] = "/empty.html";
const char kHTTPSPage[] = "/ssl/google.html";
const char kMaliciousPage[] = "/safe_browsing/malware.html";
const char kCrossSiteMaliciousPage[] = "/safe_browsing/malware2.html";
const char kMaliciousIframe[] = "/safe_browsing/malware_iframe.html";
const char kRedirectToMalware[] = "/safe_browsing/redirect_to_malware.html";
const char kUnrelatedUrl[] = "https://www.google.com";
const char kEnhancedProtectionUrl[] = "chrome://settings/security?q=enhanced";
const char kMaliciousJsPage[] = "/safe_browsing/malware_js.html";
const char kMaliciousJs[] = "/safe_browsing/script.js";
const char kInterstitialCloseHistogram[] = "interstitial.CloseReason";
const char kInterstitialPreCommitPageHistogramSuffix[] = ".before_page_shown";
std::string GetHistogramPrefix(const SBThreatType& threat_type) {
if (threat_type == SBThreatType::SB_THREAT_TYPE_URL_MALWARE) {
return "malware";
} else if (threat_type == SBThreatType::SB_THREAT_TYPE_URL_PHISHING) {
return "phishing";
} else if (threat_type == SBThreatType::SB_THREAT_TYPE_URL_UNWANTED) {
return "harmful";
} else {
NOTREACHED();
return "";
}
}
} // namespace
enum Visibility { VISIBILITY_ERROR = -1, HIDDEN = 0, VISIBLE = 1 };
bool IsShowingInterstitial(WebContents* contents) {
security_interstitials::SecurityInterstitialTabHelper* helper =
security_interstitials::SecurityInterstitialTabHelper::FromWebContents(
contents);
return helper &&
(helper->GetBlockingPageForCurrentlyCommittedNavigationForTesting() !=
nullptr);
}
content::RenderFrameHost* GetRenderFrameHost(Browser* browser) {
return browser->tab_strip_model()
->GetActiveWebContents()
->GetPrimaryMainFrame();
}
views::BubbleDialogDelegateView* OpenPageInfo(Browser* browser) {
BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser);
LocationIconView* location_icon_view =
browser_view->toolbar()->location_bar()->location_icon_view();
ui::test::TestEvent event;
location_icon_view->ShowBubble(event);
views::BubbleDialogDelegateView* page_info =
PageInfoBubbleViewBase::GetPageInfoBubbleForTesting();
page_info->set_close_on_deactivate(false);
return page_info;
}
bool WaitForReady(Browser* browser) {
WebContents* contents = browser->tab_strip_model()->GetActiveWebContents();
if (!content::WaitForRenderFrameReady(contents->GetPrimaryMainFrame())) {
return false;
}
return IsShowingInterstitial(contents);
}
Visibility GetVisibility(Browser* browser, const std::string& node_id) {
content::RenderFrameHost* rfh = GetRenderFrameHost(browser);
if (!rfh) {
return VISIBILITY_ERROR;
}
// clang-format off
std::string jsFindVisibility = R"(
(function isNodeVisible(node) {
if (!node) return 'node not found';
if (node.offsetWidth === 0 || node.offsetHeight === 0) return false;
// Do not check opacity, since the css transition may actually leave
// opacity at 0 after it's been unhidden
if (node.classList.contains('hidden')) return false;
// Otherwise, we must check all parent nodes
var parentVisibility = isNodeVisible(node.parentElement);
if (parentVisibility === 'node not found') {
return true; // none of the parents are set invisible
}
return parentVisibility;
}(document.getElementById(')" + node_id + R"(')));)";
// clang-format on
content::EvalJsResult result = content::EvalJs(rfh, jsFindVisibility);
if (result != true && result != false) {
return VISIBILITY_ERROR;
}
return result == true ? VISIBLE : HIDDEN;
}
bool Click(Browser* browser, const std::string& node_id) {
DCHECK(node_id == "primary-button" || node_id == "proceed-link" ||
node_id == "whitepaper-link" || node_id == "details-button" ||
node_id == "opt-in-checkbox" || node_id == "enhanced-protection-link")
<< "Unexpected node_id: " << node_id;
content::RenderFrameHost* rfh = GetRenderFrameHost(browser);
if (!rfh) {
return false;
}
// We don't use EvalJs for this one, since clicking
// the button/link may navigate away before the injected javascript can
// reply, hanging the test.
rfh->ExecuteJavaScriptForTests(u"document.getElementById('" +
base::ASCIIToUTF16(node_id) +
u"').click();\n",
base::NullCallback());
return true;
}
bool ClickAndWaitForDetach(Browser* browser, const std::string& node_id) {
// We wait for interstitial_detached rather than nav_entry_committed, as
// going back from a main-frame safe browsing interstitial page will not
// cause a nav entry committed event.
content::TestNavigationObserver observer(
browser->tab_strip_model()->GetActiveWebContents());
if (!Click(browser, node_id)) {
return false;
}
observer.WaitForNavigationFinished();
return true;
}
void ExpectSecurityIndicatorDowngrade(content::WebContents* tab,
net::CertStatus cert_status) {
SecurityStateTabHelper* helper = SecurityStateTabHelper::FromWebContents(tab);
ASSERT_TRUE(helper);
EXPECT_EQ(security_state::DANGEROUS, helper->GetSecurityLevel());
EXPECT_NE(security_state::MALICIOUS_CONTENT_STATUS_NONE,
helper->GetVisibleSecurityState()->malicious_content_status);
// TODO(felt): Restore this check when https://crbug.com/641187 is fixed.
// EXPECT_EQ(cert_status, helper->GetSecurityInfo().cert_status);
}
void ExpectNoSecurityIndicatorDowngrade(content::WebContents* tab) {
SecurityStateTabHelper* helper = SecurityStateTabHelper::FromWebContents(tab);
ASSERT_TRUE(helper);
EXPECT_EQ(security_state::NONE, helper->GetSecurityLevel());
EXPECT_EQ(security_state::MALICIOUS_CONTENT_STATUS_NONE,
helper->GetVisibleSecurityState()->malicious_content_status);
}
// A SafeBrowsingUIManager class that allows intercepting malware details.
class FakeSafeBrowsingUIManager : public TestSafeBrowsingUIManager {
public:
explicit FakeSafeBrowsingUIManager(
std::unique_ptr<SafeBrowsingBlockingPageFactory> blocking_page_factory)
: TestSafeBrowsingUIManager(std::move(blocking_page_factory)) {}
FakeSafeBrowsingUIManager(const FakeSafeBrowsingUIManager&) = delete;
FakeSafeBrowsingUIManager& operator=(const FakeSafeBrowsingUIManager&) =
delete;
MOCK_METHOD0(OnAttachThreatDetailsAndLaunchSurvey, void());
// Overrides SafeBrowsingUIManager.
void AttachThreatDetailsAndLaunchSurvey(
content::BrowserContext* browser_context,
std::unique_ptr<ClientSafeBrowsingReportRequest> report) override {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
ValidateReportForHats(report->SerializeAsString());
OnAttachThreatDetailsAndLaunchSurvey();
}
void ValidateReportForHats(std::string report_string) {
if (expect_empty_report_for_hats_) {
EXPECT_TRUE(report_string.empty());
} else {
EXPECT_FALSE(report_string.empty());
}
ClientSafeBrowsingReportRequest report;
report.ParseFromString(report_string);
if (expect_report_url_for_hats_) {
EXPECT_TRUE(base::EndsWith(report.url(), "empty.html"));
} else {
EXPECT_TRUE(report.url().empty());
}
if (expect_interstitial_interactions_) {
EXPECT_EQ(report.interstitial_interactions_size(), 2);
} else {
EXPECT_EQ(report.interstitial_interactions_size(), 0);
}
}
// Overrides SafeBrowsingUIManager
void SendThreatDetails(
content::BrowserContext* browser_context,
std::unique_ptr<ClientSafeBrowsingReportRequest> report) override {
std::string serialized;
report->SerializeToString(&serialized);
// Notify the UI thread that we got a report.
content::GetUIThreadTaskRunner({})->PostTask(
FROM_HERE,
base::BindOnce(&FakeSafeBrowsingUIManager::OnThreatDetailsDone, this,
serialized));
}
void OnThreatDetailsDone(const std::string& serialized) {
if (threat_details_done_) {
return;
}
EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::UI));
report_ = serialized;
ASSERT_TRUE(threat_details_done_callback_);
std::move(threat_details_done_callback_).Run();
threat_details_done_ = true;
}
void MaybeReportSafeBrowsingHit(std::unique_ptr<HitReport> hit_report,
WebContents* web_contents) override {
if (SafeBrowsingUIManager::ShouldSendHitReport(hit_report.get(),
web_contents)) {
hit_report_count_++;
hit_report_sent_threat_source_ = hit_report.get()->threat_source;
}
}
void MaybeSendClientSafeBrowsingWarningShownReport(
std::unique_ptr<ClientSafeBrowsingReportRequest> report,
WebContents* web_contents) override {
if (SafeBrowsingUIManager::ShouldSendClientSafeBrowsingWarningShownReport(
web_contents)) {
report_sent_ = true;
if (report->has_client_properties() &&
report->client_properties().has_is_async_check()) {
report_sent_is_async_check_ =
report->client_properties().is_async_check();
}
}
}
bool hit_report_sent() { return hit_report_count_ > 0; }
int hit_report_count() { return hit_report_count_; }
bool report_sent() { return report_sent_; }
std::optional<ThreatSource> hit_report_sent_threat_source() {
return hit_report_sent_threat_source_;
}
std::optional<bool> report_sent_is_async_check() {
return report_sent_is_async_check_;
}
void set_threat_details_done_callback(base::OnceClosure callback) {
EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::UI));
EXPECT_FALSE(threat_details_done_callback_);
threat_details_done_callback_ = std::move(callback);
}
std::string GetReport() {
EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::UI));
return report_;
}
void SetExpectEmptyReportForHats(bool expect_empty_report_for_hats) {
expect_empty_report_for_hats_ = expect_empty_report_for_hats;
}
void SetExpectReportUrlForHats(bool expect_report_url_for_hats) {
expect_report_url_for_hats_ = expect_report_url_for_hats;
}
void SetExpectInterstitialInteractions(
bool expect_interstitial_interactions) {
expect_interstitial_interactions_ = expect_interstitial_interactions;
}
protected:
~FakeSafeBrowsingUIManager() override {}
private:
std::string report_;
base::OnceClosure threat_details_done_callback_;
bool threat_details_done_ = false;
int hit_report_count_ = 0;
bool report_sent_ = false;
bool expect_empty_report_for_hats_ = true;
bool expect_report_url_for_hats_ = false;
bool expect_interstitial_interactions_ = false;
std::optional<ThreatSource> hit_report_sent_threat_source_;
std::optional<bool> report_sent_is_async_check_;
};
class TestThreatDetailsFactory : public ThreatDetailsFactory {
public:
TestThreatDetailsFactory() : details_() {}
~TestThreatDetailsFactory() override {}
std::unique_ptr<ThreatDetails> CreateThreatDetails(
BaseUIManager* delegate,
WebContents* web_contents,
const security_interstitials::UnsafeResource& unsafe_resource,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
history::HistoryService* history_service,
ReferrerChainProvider* referrer_chain_provider,
bool trim_to_ad_tags,
ThreatDetailsDoneCallback done_callback) override {
auto details = base::WrapUnique(new ThreatDetails(
delegate, web_contents, unsafe_resource, url_loader_factory,
history_service, referrer_chain_provider, trim_to_ad_tags,
std::move(done_callback)));
details_ = details.get();
details->StartCollection();
return details;
}
ThreatDetails* get_details() { return details_; }
private:
raw_ptr<ThreatDetails, AcrossTasksDanglingUntriaged> details_;
};
// A SafeBrowingBlockingPage class that lets us wait until it's hidden.
class TestSafeBrowsingBlockingPage : public SafeBrowsingBlockingPage {
public:
TestSafeBrowsingBlockingPage(
BaseUIManager* manager,
WebContents* web_contents,
const GURL& main_frame_url,
const UnsafeResourceList& unsafe_resources,
const BaseSafeBrowsingErrorUI::SBErrorDisplayOptions& display_options,
bool should_trigger_reporting,
bool is_proceed_anyway_disabled,
bool is_safe_browsing_surveys_enabled,
base::OnceCallback<void(bool, SBThreatType)>
trust_safety_sentiment_service_trigger,
std::optional<base::TimeTicks> blocked_page_shown_timestamp)
: SafeBrowsingBlockingPage(
manager,
web_contents,
main_frame_url,
unsafe_resources,
ChromeSafeBrowsingBlockingPageFactory::CreateControllerClient(
web_contents,
unsafe_resources,
manager,
blocked_page_shown_timestamp),
display_options,
should_trigger_reporting,
HistoryServiceFactory::GetForProfile(
Profile::FromBrowserContext(web_contents->GetBrowserContext()),
ServiceAccessType::EXPLICIT_ACCESS),
SafeBrowsingNavigationObserverManagerFactory::GetForBrowserContext(
web_contents->GetBrowserContext()),
SafeBrowsingMetricsCollectorFactory::GetForProfile(
Profile::FromBrowserContext(web_contents->GetBrowserContext())),
g_browser_process->safe_browsing_service()->trigger_manager(),
is_proceed_anyway_disabled,
is_safe_browsing_surveys_enabled,
std::move(trust_safety_sentiment_service_trigger),
/*url_loader_for_testing=*/nullptr) {
// Don't wait the whole 3 seconds for the browser test.
SetThreatDetailsProceedDelayForTesting(100);
}
// SecurityInterstitialPage methods:
void CommandReceived(const std::string& command) override {
SafeBrowsingBlockingPage::CommandReceived(command);
}
};
void AssertNoInterstitial(Browser* browser) {
WebContents* contents = browser->tab_strip_model()->GetActiveWebContents();
ASSERT_FALSE(IsShowingInterstitial(contents));
return;
}
class TestSafeBrowsingBlockingPageFactory
: public SafeBrowsingBlockingPageFactory {
public:
TestSafeBrowsingBlockingPageFactory() : always_show_back_to_safety_(true) {}
~TestSafeBrowsingBlockingPageFactory() override {}
void SetAlwaysShowBackToSafety(bool value) {
always_show_back_to_safety_ = value;
}
MockTrustSafetySentimentService* GetMockSentimentService() {
return mock_sentiment_service_;
}
SafeBrowsingBlockingPage* CreateSafeBrowsingPage(
BaseUIManager* delegate,
WebContents* web_contents,
const GURL& main_frame_url,
const SafeBrowsingBlockingPage::UnsafeResourceList& unsafe_resources,
bool should_trigger_reporting,
std::optional<base::TimeTicks> blocked_page_shown_timestamp) override {
PrefService* prefs =
Profile::FromBrowserContext(web_contents->GetBrowserContext())
->GetPrefs();
bool is_extended_reporting_opt_in_allowed =
prefs->GetBoolean(prefs::kSafeBrowsingExtendedReportingOptInAllowed);
bool is_proceed_anyway_disabled =
prefs->GetBoolean(prefs::kSafeBrowsingProceedAnywayDisabled);
bool is_safe_browsing_surveys_enabled =
IsSafeBrowsingSurveysEnabled(*prefs);
BaseSafeBrowsingErrorUI::SBErrorDisplayOptions display_options(
BaseBlockingPage::IsMainPageLoadPending(unsafe_resources),
BaseBlockingPage::IsSubresource(unsafe_resources),
is_extended_reporting_opt_in_allowed,
web_contents->GetBrowserContext()->IsOffTheRecord(),
IsExtendedReportingEnabled(*prefs),
IsExtendedReportingPolicyManaged(*prefs),
IsEnhancedProtectionEnabled(*prefs), is_proceed_anyway_disabled,
true, // should_open_links_in_new_tab
always_show_back_to_safety_,
/*is_enhanced_protection_message_enabled=*/true,
IsSafeBrowsingPolicyManaged(*prefs),
"cpn_safe_browsing" /* help_center_article_link */);
mock_sentiment_service_ = static_cast<MockTrustSafetySentimentService*>(
TrustSafetySentimentServiceFactory::GetInstance()
->SetTestingFactoryAndUse(
Profile::FromBrowserContext(web_contents->GetBrowserContext()),
base::BindRepeating(&BuildMockTrustSafetySentimentService)));
return new TestSafeBrowsingBlockingPage(
delegate, web_contents, main_frame_url, unsafe_resources,
display_options, should_trigger_reporting, is_proceed_anyway_disabled,
is_safe_browsing_surveys_enabled,
is_safe_browsing_surveys_enabled
? base::BindOnce(&MockTrustSafetySentimentService::
InteractedWithSafeBrowsingInterstitial,
base::Unretained(mock_sentiment_service_))
: base::NullCallback(),
blocked_page_shown_timestamp);
}
security_interstitials::SecurityInterstitialPage* CreateEnterpriseWarnPage(
BaseUIManager* ui_manager,
content::WebContents* web_contents,
const GURL& main_frame_url,
const SafeBrowsingBlockingPage::UnsafeResourceList& unsafe_resources)
override {
NOTREACHED();
return nullptr;
}
security_interstitials::SecurityInterstitialPage* CreateEnterpriseBlockPage(
BaseUIManager* ui_manager,
content::WebContents* web_contents,
const GURL& main_frame_url,
const SafeBrowsingBlockingPage::UnsafeResourceList& unsafe_resources)
override {
NOTREACHED();
return nullptr;
}
private:
raw_ptr<MockTrustSafetySentimentService, DanglingUntriaged>
mock_sentiment_service_;
bool always_show_back_to_safety_;
};
class SafeBrowsingBlockingPageTestHelper {
public:
SafeBrowsingBlockingPageTestHelper() {}
SafeBrowsingBlockingPageTestHelper(
const SafeBrowsingBlockingPageTestHelper&) = delete;
SafeBrowsingBlockingPageTestHelper& operator=(
const SafeBrowsingBlockingPageTestHelper&) = delete;
static void MaybeWaitForAsyncChecksToComplete(
content::WebContents* web_contents,
scoped_refptr<SafeBrowsingUIManager> ui_manager,
bool wait_for_load_stop) {
AsyncCheckTracker* tracker =
safe_browsing::AsyncCheckTracker::GetOrCreateForWebContents(
web_contents, std::move(ui_manager));
// If all pending async checks are already resolved or were never created,
// don't wait for the tracker to say the checkers size reached 0, because
// that will never occur.
if (tracker->PendingCheckersSizeForTesting() > 0u) {
base::RunLoop async_checks_completed_run_loop;
tracker->SetOnAllCheckersCompletedForTesting(
async_checks_completed_run_loop.QuitClosure());
async_checks_completed_run_loop.Run();
}
if (wait_for_load_stop) {
// When all pending checks have been resolved, we may still need to wait
// for the interstitial to load.
content::WaitForLoadStop(web_contents);
}
}
};
// Tests the safe browsing blocking page in a browser.
class SafeBrowsingBlockingPageBrowserTest
: public CertVerifierBrowserTest,
public testing::WithParamInterface<testing::tuple<SBThreatType, bool>> {
public:
SafeBrowsingBlockingPageBrowserTest()
: https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {
// NOTE: Value copied from the renderer-side threat_dom_details.cc, as
// threat_dom_details.h can't be depended on from this browser-side code.
const char kTagAndAttributeParamName[] = "tag_attribute_csv";
std::map<std::string, std::string> parameters = {
{kTagAndAttributeParamName, "div,foo,div,baz"}};
base::test::FeatureRefAndParams tag_and_attribute(
safe_browsing::kThreatDomDetailsTagAndAttributeFeature, parameters);
base::test::FeatureRefAndParams add_warning_shown_timestamp_csbrrs(
safe_browsing::kAddWarningShownTSToClientSafeBrowsingReport, {});
base::test::FeatureRefAndParams create_warning_shown_csbrrs(
safe_browsing::kCreateWarningShownClientSafeBrowsingReports, {});
scoped_feature_list_.InitWithFeaturesAndParameters(
{tag_and_attribute, add_warning_shown_timestamp_csbrrs,
create_warning_shown_csbrrs},
{});
}
SafeBrowsingBlockingPageBrowserTest(
const SafeBrowsingBlockingPageBrowserTest&) = delete;
SafeBrowsingBlockingPageBrowserTest& operator=(
const SafeBrowsingBlockingPageBrowserTest&) = delete;
~SafeBrowsingBlockingPageBrowserTest() override {}
void CreatedBrowserMainParts(
content::BrowserMainParts* browser_main_parts) override {
CertVerifierBrowserTest::CreatedBrowserMainParts(browser_main_parts);
// Test UI manager and test database manager should be set before
// the browser is started but after threads are created.
auto blocking_page_factory =
std::make_unique<TestSafeBrowsingBlockingPageFactory>();
raw_blocking_page_factory_ = blocking_page_factory.get();
factory_.SetTestUIManager(
new FakeSafeBrowsingUIManager(std::move(blocking_page_factory)));
factory_.SetTestDatabaseManager(new FakeSafeBrowsingDatabaseManager(
content::GetUIThreadTaskRunner({}),
content::GetIOThreadTaskRunner({})));
SafeBrowsingService::RegisterFactory(&factory_);
ThreatDetails::RegisterFactory(&details_factory_);
}
void TearDown() override {
InProcessBrowserTest::TearDown();
SafeBrowsingService::RegisterFactory(nullptr);
ThreatDetails::RegisterFactory(nullptr);
}
void SetUpCommandLine(base::CommandLine* command_line) override {
CertVerifierBrowserTest::SetUpCommandLine(command_line);
if (testing::get<1>(GetParam())) {
content::IsolateAllSitesForTesting(command_line);
}
// TODO(crbug.com/1491942): This fails with the field trial testing config.
command_line->AppendSwitch("disable-field-trial-config");
}
void SetUpOnMainThread() override {
host_resolver()->AddRule("*", "127.0.0.1");
content::SetupCrossSiteRedirector(embedded_test_server());
ASSERT_TRUE(embedded_test_server()->Start());
}
SBThreatType GetThreatType() const { return testing::get<0>(GetParam()); }
void SetURLThreatType(const GURL& url, SBThreatType threat_type) {
TestSafeBrowsingService* service = factory_.test_safe_browsing_service();
ASSERT_TRUE(service);
static_cast<FakeSafeBrowsingDatabaseManager*>(
service->database_manager().get())
->AddDangerousUrl(url, threat_type);
}
void SetURLThreatPatternType(const GURL& url,
ThreatPatternType threat_pattern_type) {
TestSafeBrowsingService* service = factory_.test_safe_browsing_service();
ASSERT_TRUE(service);
static_cast<FakeSafeBrowsingDatabaseManager*>(
service->database_manager().get())
->AddDangerousUrlPattern(url, threat_pattern_type);
}
void ClearBadURL(const GURL& url) {
TestSafeBrowsingService* service = factory_.test_safe_browsing_service();
ASSERT_TRUE(service);
static_cast<FakeSafeBrowsingDatabaseManager*>(
service->database_manager().get())
->ClearDangerousUrl(url);
}
// The basic version of this method, which uses an HTTP test URL.
GURL SetupWarningAndNavigate(Browser* browser) {
return SetupWarningAndNavigateToURL(
embedded_test_server()->GetURL(kEmptyPage), browser);
}
// The basic version of this method, which uses an HTTP test URL.
GURL SetupWarningAndNavigateInNewTab(Browser* browser) {
return SetupWarningAndNavigateToURLInNewTab(
embedded_test_server()->GetURL(kEmptyPage), browser);
}
// Navigates to a warning on a valid HTTPS website.
GURL SetupWarningAndNavigateToValidHTTPS() {
EXPECT_TRUE(https_server_.Start());
scoped_refptr<net::X509Certificate> cert(https_server_.GetCertificate());
net::CertVerifyResult verify_result;
verify_result.verified_cert = cert;
verify_result.cert_status = 0;
mock_cert_verifier()->AddResultForCert(cert.get(), verify_result, net::OK);
GURL url = https_server_.GetURL(kHTTPSPage);
return SetupWarningAndNavigateToURL(url, browser());
}
// Navigates through an HTTPS interstitial, then opens up a SB warning on that
// same URL.
GURL SetupWarningAndNavigateToInvalidHTTPS() {
https_server_.SetSSLConfig(net::EmbeddedTestServer::CERT_EXPIRED);
EXPECT_TRUE(https_server_.Start());
GURL url = https_server_.GetURL(kHTTPSPage);
// Proceed through the HTTPS interstitial.
EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
content::WebContents* contents =
browser()->tab_strip_model()->GetActiveWebContents();
security_interstitials::SecurityInterstitialPage* ssl_blocking_page;
EXPECT_TRUE(WaitForRenderFrameReady(contents->GetPrimaryMainFrame()));
security_interstitials::SecurityInterstitialTabHelper* helper =
security_interstitials::SecurityInterstitialTabHelper::FromWebContents(
contents);
EXPECT_TRUE(helper);
ssl_blocking_page =
helper->GetBlockingPageForCurrentlyCommittedNavigationForTesting();
EXPECT_EQ(SSLBlockingPage::kTypeForTesting,
ssl_blocking_page->GetTypeForTesting());
content::TestNavigationObserver observer(
browser()->tab_strip_model()->GetActiveWebContents());
ssl_blocking_page->CommandReceived(base::NumberToString(
security_interstitials::SecurityInterstitialCommand::CMD_PROCEED));
// When both SB and SSL interstitials are committed navigations, we need
// to wait for two navigations here, one is from the SSL interstitial to
// the blocked site (which does not complete since SB blocks it) and the
// second one is to the actual SB interstitial.
observer.WaitForNavigationFinished();
return SetupWarningAndNavigateToURL(url, browser());
}
// Adds a safebrowsing threat results to the fake safebrowsing service,
// navigates to a page with a subresource containing the threat site, and
// returns the url of the parent page.
GURL SetupThreatOnSubresourceAndNavigate(std::string_view main_frame_url,
std::string_view subresource_url) {
GURL url = embedded_test_server()->GetURL(main_frame_url);
GURL embedded_url = embedded_test_server()->GetURL(subresource_url);
SetURLThreatType(embedded_url, GetThreatType());
EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
WebContents* contents =
browser()->tab_strip_model()->GetActiveWebContents();
EXPECT_TRUE(
content::WaitForRenderFrameReady(contents->GetPrimaryMainFrame()));
return url;
}
GURL GetWhitePaperUrl() {
return google_util::AppendGoogleLocaleParam(
GURL(security_interstitials::kSafeBrowsingWhitePaperUrl),
factory_.test_safe_browsing_service()
->ui_manager()
.get()
->app_locale());
}
void SendCommand(
security_interstitials::SecurityInterstitialCommand command) {
WebContents* contents =
browser()->tab_strip_model()->GetActiveWebContents();
SafeBrowsingBlockingPage* interstitial_page;
security_interstitials::SecurityInterstitialTabHelper* helper =
security_interstitials::SecurityInterstitialTabHelper::FromWebContents(
contents);
ASSERT_TRUE(helper);
interstitial_page = static_cast<SafeBrowsingBlockingPage*>(
helper->GetBlockingPageForCurrentlyCommittedNavigationForTesting());
ASSERT_TRUE(interstitial_page);
ASSERT_EQ(SafeBrowsingBlockingPage::kTypeForTesting,
interstitial_page->GetTypeForTesting());
interstitial_page->CommandReceived(base::NumberToString(command));
}
void SetReportSentCallback(base::OnceClosure callback) {
static_cast<FakeSafeBrowsingUIManager*>(
factory_.test_safe_browsing_service()->ui_manager().get())
->set_threat_details_done_callback(std::move(callback));
}
std::string GetReportSent() {
return static_cast<FakeSafeBrowsingUIManager*>(
factory_.test_safe_browsing_service()->ui_manager().get())
->GetReport();
}
void SetExpectEmptyReportForHats(bool expect_empty_report_for_hats) {
static_cast<FakeSafeBrowsingUIManager*>(
factory_.test_safe_browsing_service()->ui_manager().get())
->SetExpectEmptyReportForHats(expect_empty_report_for_hats);
}
void SetExpectReportUrlForHats(bool expect_report_url_for_hats) {
static_cast<FakeSafeBrowsingUIManager*>(
factory_.test_safe_browsing_service()->ui_manager().get())
->SetExpectReportUrlForHats(expect_report_url_for_hats);
}
void SetExpectInterstitialInteractions(
bool expect_interstitial_interactions) {
static_cast<FakeSafeBrowsingUIManager*>(
factory_.test_safe_browsing_service()->ui_manager().get())
->SetExpectInterstitialInteractions(expect_interstitial_interactions);
}
FakeSafeBrowsingUIManager* GetSafeBrowsingUiManager() {
return static_cast<FakeSafeBrowsingUIManager*>(
factory_.test_safe_browsing_service()->ui_manager().get());
}
content::RenderFrameHost* GetRenderFrameHost() {
return ::safe_browsing::GetRenderFrameHost(browser());
}
Visibility GetVisibility(const std::string& node_id) {
return ::safe_browsing::GetVisibility(browser(), node_id);
}
bool Click(const std::string& node_id) {
return ::safe_browsing::Click(browser(), node_id);
}
bool ClickAndWaitForDetach(const std::string& node_id) {
return ::safe_browsing::ClickAndWaitForDetach(browser(), node_id);
}
void AssertNoInterstitial() {
return ::safe_browsing::AssertNoInterstitial(browser());
}
void TestReportingDisabledAndDontProceed(const GURL& url) {
SetURLThreatType(url, GetThreatType());
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
ASSERT_TRUE(WaitForReady(browser()));
EXPECT_EQ(HIDDEN, GetVisibility("extended-reporting-opt-in"));
EXPECT_EQ(HIDDEN, GetVisibility("opt-in-checkbox"));
EXPECT_EQ(HIDDEN, GetVisibility("proceed-link"));
EXPECT_EQ(VISIBLE, GetVisibility("learn-more-link"));
EXPECT_TRUE(Click("details-button"));
EXPECT_EQ(VISIBLE, GetVisibility("proceed-link"));
EXPECT_TRUE(ClickAndWaitForDetach("primary-button"));
AssertNoInterstitial(); // Assert the interstitial is gone
EXPECT_EQ(GURL(url::kAboutBlankURL), // Back to "about:blank"
browser()
->tab_strip_model()
->GetActiveWebContents()
->GetLastCommittedURL());
}
void VerifyResource(
const ClientSafeBrowsingReportRequest& report,
const ClientSafeBrowsingReportRequest::Resource& actual_resource,
const std::string& expected_url,
const std::string& expected_parent,
int expected_child_size,
const std::string& expected_tag_name) {
EXPECT_EQ(expected_url, actual_resource.url());
// Finds the parent url by comparing resource ids.
for (auto resource : report.resources()) {
if (actual_resource.parent_id() == resource.id()) {
EXPECT_EQ(expected_parent, resource.url());
break;
}
}
EXPECT_EQ(expected_child_size, actual_resource.child_ids_size());
EXPECT_EQ(expected_tag_name, actual_resource.tag_name());
}
void VerifyInteractionOccurrenceCount(
const ClientSafeBrowsingReportRequest& report,
const ClientSafeBrowsingReportRequest::InterstitialInteraction&
actual_interaction,
const ClientSafeBrowsingReportRequest::InterstitialInteraction::
SecurityInterstitialInteraction& expected_interaction_type,
const int& expected_occurrence_count) {
// Find the interaction within the report by comparing
// security_interstitial_interaction.
for (auto interaction : report.interstitial_interactions()) {
if (actual_interaction.security_interstitial_interaction() ==
interaction.security_interstitial_interaction()) {
EXPECT_EQ(expected_interaction_type,
interaction.security_interstitial_interaction());
EXPECT_EQ(expected_occurrence_count, interaction.occurrence_count());
break;
}
}
}
void VerifyElement(
const ClientSafeBrowsingReportRequest& report,
const HTMLElement& actual_element,
const std::string& expected_tag_name,
int expected_child_ids_size,
const std::vector<mojom::AttributeNameValuePtr>& expected_attributes) {
EXPECT_EQ(expected_tag_name, actual_element.tag());
EXPECT_EQ(expected_child_ids_size, actual_element.child_ids_size());
ASSERT_EQ(static_cast<int>(expected_attributes.size()),
actual_element.attribute_size());
for (size_t i = 0; i < expected_attributes.size(); ++i) {
const mojom::AttributeNameValue& expected_attribute =
*expected_attributes[i];
const HTMLElement::Attribute& actual_attribute_pb =
actual_element.attribute(i);
EXPECT_EQ(expected_attribute.name, actual_attribute_pb.name());
EXPECT_EQ(expected_attribute.value, actual_attribute_pb.value());
}
}
void ExpectSecurityIndicatorDowngrade(content::WebContents* tab,
net::CertStatus cert_status) {
::safe_browsing::ExpectSecurityIndicatorDowngrade(tab, cert_status);
}
void ExpectNoSecurityIndicatorDowngrade(content::WebContents* tab) {
::safe_browsing::ExpectNoSecurityIndicatorDowngrade(tab);
}
bool hit_report_sent() {
return static_cast<FakeSafeBrowsingUIManager*>(
factory_.test_safe_browsing_service()->ui_manager().get())
->hit_report_sent();
}
bool report_sent() {
return static_cast<FakeSafeBrowsingUIManager*>(
factory_.test_safe_browsing_service()->ui_manager().get())
->report_sent();
}
// Helper method for LearnMore test below. Implemented as a test fixture
// method instead of in the test below because the whole test fixture class
// is friended by SafeBrowsingBlockingPage.
void MockHelpCenterUrl(SafeBrowsingBlockingPage* sb_interstitial) {
ASSERT_TRUE(https_server_.Start());
scoped_refptr<net::X509Certificate> cert(https_server_.GetCertificate());
net::CertVerifyResult verify_result;
verify_result.verified_cert = cert;
verify_result.cert_status = 0;
mock_cert_verifier()->AddResultForCert(cert.get(), verify_result, net::OK);
security_interstitials::SecurityInterstitialControllerClient* client =
sb_interstitial->controller();
client->SetBaseHelpCenterUrlForTesting(
https_server_.GetURL("/title1.html"));
}
void SetAlwaysShowBackToSafety(bool val) {
raw_blocking_page_factory_->SetAlwaysShowBackToSafety(val);
}
MockTrustSafetySentimentService* mock_sentiment_service() {
return raw_blocking_page_factory_->GetMockSentimentService();
}
protected:
TestThreatDetailsFactory details_factory_;
net::EmbeddedTestServer& https_server() { return https_server_; }
private:
// Adds a safebrowsing result of the current test threat to the fake
// safebrowsing service, navigates to that page, and returns the url.
// The various wrappers supply different URLs.
GURL SetupWarningAndNavigateToURL(GURL url, Browser* browser) {
SetURLThreatType(url, GetThreatType());
EXPECT_TRUE(ui_test_utils::NavigateToURL(browser, url));
EXPECT_TRUE(WaitForReady(browser));
return url;
}
// Adds a safebrowsing result of the current test threat to the fake
// safebrowsing service, navigates to that page, and returns the url.
// The various wrappers supply different URLs.
GURL SetupWarningAndNavigateToURLInNewTab(GURL url, Browser* browser) {
SetURLThreatType(url, GetThreatType());
ui_test_utils::NavigateToURLWithDisposition(
browser, url, WindowOpenDisposition::NEW_FOREGROUND_TAB,
ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB);
content::TestNavigationObserver observer(
browser->tab_strip_model()->GetActiveWebContents());
observer.WaitForNavigationFinished();
EXPECT_TRUE(WaitForReady(browser));
return url;
}
base::test::ScopedFeatureList scoped_feature_list_;
TestSafeBrowsingServiceFactory factory_;
raw_ptr<TestSafeBrowsingBlockingPageFactory, DanglingUntriaged>
raw_blocking_page_factory_;
net::EmbeddedTestServer https_server_;
};
class SafeBrowsingHatsSurveyBrowserTest
: public SafeBrowsingBlockingPageBrowserTest {
public:
SafeBrowsingHatsSurveyBrowserTest() {
scoped_feature_list_.InitAndEnableFeature(kRedWarningSurvey);
}
~SafeBrowsingHatsSurveyBrowserTest() override = default;
void SetUp() override { SafeBrowsingBlockingPageBrowserTest::SetUp(); }
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageBrowserTest, HardcodedUrls) {
const GURL urls[] = {GURL(kChromeUISafeBrowsingMatchMalwareUrl),
GURL(kChromeUISafeBrowsingMatchPhishingUrl),
GURL(kChromeUISafeBrowsingMatchUnwantedUrl)};
for (const GURL& url : urls) {
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
EXPECT_TRUE(WaitForReady(browser()));
EXPECT_EQ(VISIBLE, GetVisibility("primary-button"));
EXPECT_EQ(HIDDEN, GetVisibility("details"));
EXPECT_EQ(HIDDEN, GetVisibility("proceed-link"));
EXPECT_EQ(HIDDEN, GetVisibility("error-code"));
EXPECT_TRUE(Click("details-button"));
EXPECT_EQ(VISIBLE, GetVisibility("details"));
EXPECT_EQ(VISIBLE, GetVisibility("proceed-link"));
EXPECT_EQ(HIDDEN, GetVisibility("error-code"));
EXPECT_TRUE(ClickAndWaitForDetach("primary-button"));
AssertNoInterstitial(); // Assert the interstitial is gone
EXPECT_EQ(GURL(url::kAboutBlankURL), // Back to "about:blank"
browser()
->tab_strip_model()
->GetActiveWebContents()
->GetLastCommittedURL());
}
}
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageBrowserTest, DontProceed) {
SetupWarningAndNavigate(browser());
EXPECT_EQ(VISIBLE, GetVisibility("primary-button"));
EXPECT_EQ(HIDDEN, GetVisibility("details"));
EXPECT_EQ(HIDDEN, GetVisibility("proceed-link"));
EXPECT_EQ(HIDDEN, GetVisibility("error-code"));
EXPECT_TRUE(Click("details-button"));
EXPECT_EQ(VISIBLE, GetVisibility("details"));
EXPECT_EQ(VISIBLE, GetVisibility("proceed-link"));
EXPECT_EQ(HIDDEN, GetVisibility("error-code"));
EXPECT_TRUE(ClickAndWaitForDetach("primary-button"));
AssertNoInterstitial(); // Assert the interstitial is gone
EXPECT_EQ(GURL(url::kAboutBlankURL), // Back to "about:blank"
browser()
->tab_strip_model()
->GetActiveWebContents()
->GetLastCommittedURL());
}
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageBrowserTest, DontProceed_RTL) {
base::i18n::SetICUDefaultLocale("ar");
ASSERT_TRUE(base::i18n::IsRTL());
SetupWarningAndNavigate(browser());
EXPECT_EQ(VISIBLE, GetVisibility("primary-button"));
EXPECT_EQ(HIDDEN, GetVisibility("details"));
EXPECT_EQ(HIDDEN, GetVisibility("proceed-link"));
EXPECT_EQ(HIDDEN, GetVisibility("error-code"));
EXPECT_TRUE(Click("details-button"));
EXPECT_EQ(VISIBLE, GetVisibility("details"));
EXPECT_EQ(VISIBLE, GetVisibility("proceed-link"));
EXPECT_EQ(HIDDEN, GetVisibility("error-code"));
EXPECT_TRUE(ClickAndWaitForDetach("primary-button"));
AssertNoInterstitial(); // Assert the interstitial is gone
EXPECT_EQ(GURL(url::kAboutBlankURL), // Back to "about:blank"
browser()
->tab_strip_model()
->GetActiveWebContents()
->GetLastCommittedURL());
}
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageBrowserTest, Proceed) {
GURL url = SetupWarningAndNavigate(browser());
EXPECT_TRUE(ClickAndWaitForDetach("proceed-link"));
AssertNoInterstitial(); // Assert the interstitial is gone.
EXPECT_EQ(url, browser()
->tab_strip_model()
->GetActiveWebContents()
->GetLastCommittedURL());
}
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageBrowserTest, Proceed_RTL) {
base::i18n::SetICUDefaultLocale("ar");
ASSERT_TRUE(base::i18n::IsRTL());
GURL url = SetupWarningAndNavigate(browser());
EXPECT_TRUE(ClickAndWaitForDetach("proceed-link"));
AssertNoInterstitial(); // Assert the interstitial is gone.
EXPECT_EQ(url, browser()
->tab_strip_model()
->GetActiveWebContents()
->GetLastCommittedURL());
}
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageBrowserTest, IframeNoWarning) {
SetupThreatOnSubresourceAndNavigate(kCrossSiteMaliciousPage,
kMaliciousIframe);
AssertNoInterstitial();
}
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageBrowserTest, JsNoWarning) {
SetupThreatOnSubresourceAndNavigate(kMaliciousJsPage, kMaliciousJs);
AssertNoInterstitial();
}
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageBrowserTest,
MainFrameBlockedShouldHaveNoDOMDetailsWhenDontProceed) {
SetExtendedReportingPrefForTests(browser()->profile()->GetPrefs(), true);
const bool expect_threat_details =
SafeBrowsingBlockingPage::ShouldReportThreatDetails(GetThreatType());
base::RunLoop threat_report_sent_loop;
if (expect_threat_details)
SetReportSentCallback(threat_report_sent_loop.QuitClosure());
// Navigate to a safe page which contains multiple potential DOM details.
// (Despite the name, kMaliciousPage is not the page flagged as bad in this
// test.)
GURL safe_url(embedded_test_server()->GetURL(kMaliciousPage));
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), safe_url));
EXPECT_EQ(nullptr, details_factory_.get_details());
// Start navigation to bad page (kEmptyPage), which will be blocked before it
// is committed.
content::TestNavigationObserver observer(
browser()->tab_strip_model()->GetActiveWebContents());
GURL url = SetupWarningAndNavigate(browser());
observer.WaitForNavigationFinished();
ThreatDetails* threat_details = details_factory_.get_details();
EXPECT_EQ(expect_threat_details, threat_details != nullptr);
// Go back.
EXPECT_TRUE(ClickAndWaitForDetach("primary-button"));
AssertNoInterstitial(); // Assert the interstitial is gone
EXPECT_TRUE(IsExtendedReportingEnabled(*browser()->profile()->GetPrefs()));
EXPECT_EQ(safe_url, browser()
->tab_strip_model()
->GetActiveWebContents()
->GetLastCommittedURL());
if (expect_threat_details) {
threat_report_sent_loop.Run();
std::string serialized = GetReportSent();
ClientSafeBrowsingReportRequest report;
ASSERT_TRUE(report.ParseFromString(serialized));
// Verify the report is complete.
EXPECT_TRUE(report.complete());
EXPECT_EQ(url.spec(), report.page_url());
EXPECT_EQ(url.spec(), report.url());
ASSERT_EQ(1, report.resources_size());
EXPECT_EQ(url.spec(), report.resources(0).url());
}
}
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageBrowserTest,
MainFrameBlockedShouldHaveNoDOMDetailsWhenProceeding) {
SetExtendedReportingPrefForTests(browser()->profile()->GetPrefs(), true);
const bool expect_threat_details =
SafeBrowsingBlockingPage::ShouldReportThreatDetails(GetThreatType());
base::RunLoop threat_report_sent_loop;
if (expect_threat_details)
SetReportSentCallback(threat_report_sent_loop.QuitClosure());
// Navigate to a safe page which contains multiple potential DOM details.
// (Despite the name, kMaliciousPage is not the page flagged as bad in this
// test.)
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL(kMaliciousPage)));
EXPECT_EQ(nullptr, details_factory_.get_details());
// Start navigation to bad page (kEmptyPage), which will be blocked before it
// is committed.
GURL url = SetupWarningAndNavigate(browser());
ThreatDetails* threat_details = details_factory_.get_details();
EXPECT_EQ(expect_threat_details, threat_details != nullptr);
// Proceed through the warning.
EXPECT_TRUE(ClickAndWaitForDetach("proceed-link"));
AssertNoInterstitial(); // Assert the interstitial is gone
EXPECT_TRUE(IsExtendedReportingEnabled(*browser()->profile()->GetPrefs()));
EXPECT_EQ(url, browser()
->tab_strip_model()
->GetActiveWebContents()
->GetLastCommittedURL());
if (expect_threat_details) {
threat_report_sent_loop.Run();
std::string serialized = GetReportSent();
ClientSafeBrowsingReportRequest report;
ASSERT_TRUE(report.ParseFromString(serialized));
// Verify the report is complete.
EXPECT_TRUE(report.complete());
EXPECT_EQ(url.spec(), report.page_url());
EXPECT_EQ(url.spec(), report.url());
ASSERT_EQ(1, report.resources_size());
EXPECT_EQ(url.spec(), report.resources(0).url());
}
}
// Verifies that the "proceed anyway" link isn't available when it is disabled
// by the corresponding policy. Also verifies that sending the "proceed"
// command anyway doesn't advance to the unsafe site.
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageBrowserTest, ProceedDisabled) {
// Simulate a policy disabling the "proceed anyway" link.
browser()->profile()->GetPrefs()->SetBoolean(
prefs::kSafeBrowsingProceedAnywayDisabled, true);
SetupWarningAndNavigate(browser());
EXPECT_EQ(VISIBLE, GetVisibility("primary-button"));
EXPECT_EQ(HIDDEN, GetVisibility("details"));
EXPECT_EQ(HIDDEN, GetVisibility("proceed-link"));
EXPECT_EQ(HIDDEN, GetVisibility("final-paragraph"));
EXPECT_TRUE(Click("details-button"));
EXPECT_EQ(HIDDEN, GetVisibility("proceed-link"));
EXPECT_EQ(HIDDEN, GetVisibility("final-paragraph"));
content::TestNavigationObserver observer(
browser()->tab_strip_model()->GetActiveWebContents());
SendCommand(security_interstitials::CMD_PROCEED);
observer.WaitForNavigationFinished();
// The "proceed" command should go back instead, if proceeding is disabled.
AssertNoInterstitial();
EXPECT_EQ(GURL(url::kAboutBlankURL), // Back to "about:blank"
browser()
->tab_strip_model()
->GetActiveWebContents()
->GetLastCommittedURL());
}
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageBrowserTest, NoBackToSafety) {
SetAlwaysShowBackToSafety(false);
SetupWarningAndNavigateInNewTab(browser());
EXPECT_EQ(HIDDEN, GetVisibility("primary-button"));
EXPECT_EQ(HIDDEN, GetVisibility("details"));
EXPECT_EQ(HIDDEN, GetVisibility("proceed-link"));
EXPECT_EQ(HIDDEN, GetVisibility("error-code"));
EXPECT_TRUE(Click("details-button"));
EXPECT_EQ(VISIBLE, GetVisibility("details"));
EXPECT_EQ(VISIBLE, GetVisibility("proceed-link"));
EXPECT_EQ(HIDDEN, GetVisibility("error-code"));
}
// Verifies that the reporting checkbox is hidden when opt-in is
// disabled by policy. However, reports can still be sent if extended
// reporting is enabled (eg: by its own policy).
// Note: this combination will be deprecated along with the OptInAllowed
// policy, to be replaced by a policy on the SBER setting itself.
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageBrowserTest,
ReportingDisabledByPolicy) {
SetExtendedReportingPrefForTests(browser()->profile()->GetPrefs(), true);
browser()->profile()->GetPrefs()->SetBoolean(
prefs::kSafeBrowsingExtendedReportingOptInAllowed, false);
base::RunLoop threat_report_sent_loop;
SetReportSentCallback(threat_report_sent_loop.QuitClosure());
TestReportingDisabledAndDontProceed(
embedded_test_server()->GetURL(kEmptyPage));
}
// Verifies that the enhanced protection message is still shown if the page is
// reloaded while the interstitial is showing.
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageBrowserTest,
ReloadWhileInterstitialShowing) {
safe_browsing::SetSafeBrowsingState(
browser()->profile()->GetPrefs(),
safe_browsing::SafeBrowsingState::STANDARD_PROTECTION);
browser()->profile()->GetPrefs()->SetBoolean(
prefs::kSafeBrowsingSurveysEnabled, false);
// Start navigation to bad page (kEmptyPage), which will be blocked before it
// is committed.
const GURL url = SetupWarningAndNavigate(browser());
// Checkbox should be showing.
EXPECT_EQ(VISIBLE, GetVisibility("enhanced-protection-message"));
WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(tab);
// Security indicator should be showing.
ExpectSecurityIndicatorDowngrade(tab, 0u);
// Check navigation entry state.
NavigationController& controller = tab->GetController();
ASSERT_TRUE(controller.GetVisibleEntry());
EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL());
// "Reload" the tab.
SetupWarningAndNavigate(browser());
// Checkbox should be showing.
EXPECT_EQ(VISIBLE, GetVisibility("enhanced-protection-message"));
// Security indicator should be showing.
ExpectSecurityIndicatorDowngrade(tab, 0u);
// Check navigation entry state.
ASSERT_TRUE(controller.GetVisibleEntry());
EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL());
}
#if (BUILDFLAG(IS_MAC) && !defined(NDEBUG)) || defined(MEMORY_SANITIZER)
// TODO(crbug.com/1132307): Address flaky timeout.
#define MAYBE_LearnMore DISABLED_LearnMore
#else
#define MAYBE_LearnMore LearnMore
#endif
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageBrowserTest, MAYBE_LearnMore) {
SetupWarningAndNavigate(browser());
SafeBrowsingBlockingPage* sb_interstitial;
WebContents* interstitial_tab =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(interstitial_tab);
security_interstitials::SecurityInterstitialTabHelper* helper =
security_interstitials::SecurityInterstitialTabHelper::FromWebContents(
interstitial_tab);
ASSERT_TRUE(helper);
sb_interstitial = static_cast<SafeBrowsingBlockingPage*>(
helper->GetBlockingPageForCurrentlyCommittedNavigationForTesting());
MockHelpCenterUrl(sb_interstitial);
EXPECT_EQ(1, browser()->tab_strip_model()->count());
content::TestNavigationObserver nav_observer(nullptr);
nav_observer.StartWatchingNewWebContents();
SendCommand(security_interstitials::CMD_OPEN_HELP_CENTER);
nav_observer.Wait();
// A new tab has been opened.
EXPECT_EQ(2, browser()->tab_strip_model()->count());
// Interstitial does not display in the foreground tab.
EXPECT_EQ(1, browser()->tab_strip_model()->active_index());
WebContents* new_tab = browser()->tab_strip_model()->GetWebContentsAt(1);
ASSERT_TRUE(new_tab);
EXPECT_FALSE(IsShowingInterstitial(new_tab));
// Interstitial still displays in the background tab.
browser()->tab_strip_model()->ActivateTabAt(
0, TabStripUserGestureDetails(
TabStripUserGestureDetails::GestureType::kOther));
EXPECT_EQ(0, browser()->tab_strip_model()->active_index());
EXPECT_EQ(interstitial_tab,
browser()->tab_strip_model()->GetActiveWebContents());
EXPECT_TRUE(IsShowingInterstitial(
browser()->tab_strip_model()->GetActiveWebContents()));
}
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageBrowserTest,
Histograms_DontProceed) {
ukm::TestAutoSetUkmRecorder test_ukm_recorder;
WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
SafeBrowsingMetricsCollector* metrics_collector =
SafeBrowsingMetricsCollectorFactory::GetForProfile(
Profile::FromBrowserContext(web_contents->GetBrowserContext()));
EXPECT_EQ(std::nullopt,
metrics_collector->GetLatestEventTimestamp(
SafeBrowsingMetricsCollector::EventType::
SECURITY_SENSITIVE_SAFE_BROWSING_INTERSTITIAL));
base::HistogramTester histograms;
SBThreatType threat_type = GetThreatType();
std::string prefix = GetHistogramPrefix(threat_type);
const std::string decision_histogram = "interstitial." + prefix + ".decision";
const std::string interaction_histogram =
"interstitial." + prefix + ".interaction";
const std::string delay_histogram = "interstitial." + prefix + ".show_delay";
const std::string threat_source = ".from_device_v4";
// TODO(nparker): Check for *.from_device as well.
// Histograms should start off empty.
histograms.ExpectTotalCount(decision_histogram, 0);
histograms.ExpectTotalCount(interaction_histogram, 0);
// After navigating to the page, the totals should be set.
SetupWarningAndNavigate(browser());
histograms.ExpectTotalCount(decision_histogram, 1);
histograms.ExpectBucketCount(decision_histogram,
security_interstitials::MetricsHelper::SHOW, 1);
histograms.ExpectBucketCount(
decision_histogram + kInterstitialPreCommitPageHistogramSuffix,
security_interstitials::MetricsHelper::SHOW, 1);
histograms.ExpectTimeBucketCount(delay_histogram, base::TimeDelta::Min(), 1);
histograms.ExpectTimeBucketCount(delay_histogram + threat_source,
base::TimeDelta::Min(), 1);
histograms.ExpectTotalCount(interaction_histogram, 2);
histograms.ExpectBucketCount(
interaction_histogram,
security_interstitials::MetricsHelper::TOTAL_VISITS, 1);
histograms.ExpectBucketCount(
interaction_histogram,
security_interstitials::MetricsHelper::SHOW_ENHANCED_PROTECTION, 1);
// Check if security sensitive event is added to prefs.
EXPECT_NE(std::nullopt,
metrics_collector->GetLatestEventTimestamp(
SafeBrowsingMetricsCollector::EventType::
SECURITY_SENSITIVE_SAFE_BROWSING_INTERSTITIAL));
// Decision should be recorded.
EXPECT_TRUE(ClickAndWaitForDetach("primary-button"));
AssertNoInterstitial(); // Assert the interstitial is gone
histograms.ExpectTotalCount(decision_histogram, 2);
histograms.ExpectBucketCount(
decision_histogram, security_interstitials::MetricsHelper::DONT_PROCEED,
1);
histograms.ExpectBucketCount(
decision_histogram + kInterstitialPreCommitPageHistogramSuffix,
security_interstitials::MetricsHelper::DONT_PROCEED, 1);
histograms.ExpectTotalCount(interaction_histogram, 2);
histograms.ExpectBucketCount(
interaction_histogram,
security_interstitials::MetricsHelper::TOTAL_VISITS, 1);
histograms.ExpectBucketCount(
interaction_histogram,
security_interstitials::MetricsHelper::SHOW_ENHANCED_PROTECTION, 1);
// CloseReason histograms.
histograms.ExpectTotalCount(kInterstitialCloseHistogram, 2);
histograms.ExpectBucketCount(
kInterstitialCloseHistogram,
security_interstitials::SecurityInterstitialTabHelper::
InterstitialCloseReason::INTERSTITIAL_SHOWN,
1);
histograms.ExpectBucketCount(
kInterstitialCloseHistogram,
security_interstitials::SecurityInterstitialTabHelper::
InterstitialCloseReason::NAVIGATE_AWAY,
1);
// Check that we are recording the UKM when interstitial is shown and we do
// not record for the interstitial bypassed.
auto ukm_entries =
test_ukm_recorder.GetEntriesByName("SafeBrowsingInterstitial");
EXPECT_EQ(1u, ukm_entries.size());
test_ukm_recorder.ExpectEntryMetric(ukm_entries[0], "Shown", true);
}
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageBrowserTest,
Histograms_Proceed) {
ukm::TestAutoSetUkmRecorder test_ukm_recorder;
base::HistogramTester histograms;
SBThreatType threat_type = GetThreatType();
std::string prefix = GetHistogramPrefix(threat_type);
const std::string decision_histogram = "interstitial." + prefix + ".decision";
const std::string interaction_histogram =
"interstitial." + prefix + ".interaction";
const std::string delay_histogram = "interstitial." + prefix + ".show_delay";
const std::string threat_source = ".from_device_v4";
// Histograms should start off empty.
histograms.ExpectTotalCount(decision_histogram, 0);
histograms.ExpectTotalCount(interaction_histogram, 0);
// After navigating to the page, the totals should be set.
GURL url = SetupWarningAndNavigate(browser());
histograms.ExpectTotalCount(decision_histogram, 1);
histograms.ExpectBucketCount(decision_histogram,
security_interstitials::MetricsHelper::SHOW, 1);
histograms.ExpectBucketCount(
decision_histogram + kInterstitialPreCommitPageHistogramSuffix,
security_interstitials::MetricsHelper::SHOW, 1);
histograms.ExpectTimeBucketCount(delay_histogram, base::TimeDelta::Min(), 1);
histograms.ExpectTimeBucketCount(delay_histogram + threat_source,
base::TimeDelta::Min(), 1);
histograms.ExpectTotalCount(interaction_histogram, 2);
histograms.ExpectBucketCount(
interaction_histogram,
security_interstitials::MetricsHelper::TOTAL_VISITS, 1);
histograms.ExpectBucketCount(
interaction_histogram,
security_interstitials::MetricsHelper::SHOW_ENHANCED_PROTECTION, 1);
// Decision should be recorded.
EXPECT_TRUE(ClickAndWaitForDetach("proceed-link"));
AssertNoInterstitial(); // Assert the interstitial is gone.
histograms.ExpectTotalCount(decision_histogram, 2);
histograms.ExpectBucketCount(
decision_histogram, security_interstitials::MetricsHelper::PROCEED, 1);
histograms.ExpectBucketCount(
decision_histogram + kInterstitialPreCommitPageHistogramSuffix,
security_interstitials::MetricsHelper::PROCEED, 1);
histograms.ExpectTotalCount(interaction_histogram, 2);
histograms.ExpectBucketCount(
interaction_histogram,
security_interstitials::MetricsHelper::TOTAL_VISITS, 1);
histograms.ExpectBucketCount(
interaction_histogram,
security_interstitials::MetricsHelper::SHOW_ENHANCED_PROTECTION, 1);
histograms.ExpectBucketCount(
interaction_histogram,
security_interstitials::MetricsHelper::CLOSE_INTERSTITIAL_WITHOUT_UI, 0);
// CloseReason histograms.
histograms.ExpectTotalCount(kInterstitialCloseHistogram, 2);
histograms.ExpectBucketCount(
kInterstitialCloseHistogram,
security_interstitials::SecurityInterstitialTabHelper::
InterstitialCloseReason::INTERSTITIAL_SHOWN,
1);
histograms.ExpectBucketCount(
kInterstitialCloseHistogram,
security_interstitials::SecurityInterstitialTabHelper::
InterstitialCloseReason::NAVIGATE_AWAY,
1);
// Check that we are recording the UKM when interstitial is shown and we do
// not record for the interstitial bypassed.
auto ukm_entries =
test_ukm_recorder.GetEntriesByName("SafeBrowsingInterstitial");
EXPECT_EQ(2u, ukm_entries.size());
test_ukm_recorder.ExpectEntryMetric(ukm_entries[0], "Shown", true);
test_ukm_recorder.ExpectEntryMetric(ukm_entries[1], "Bypassed", true);
}
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageBrowserTest,
Histograms_UserMadeNoDecision) {
base::HistogramTester histograms;
SBThreatType threat_type = GetThreatType();
std::string prefix = GetHistogramPrefix(threat_type);
const std::string interaction_histogram =
"interstitial." + prefix + ".interaction";
// Histograms should start off empty.
histograms.ExpectTotalCount(interaction_histogram, 0);
// Navigate to the page and show warning.
GURL url = SetupWarningAndNavigate(browser());
// Close tab without making an explicit choice on interstitial.
chrome::CloseTab(browser());
histograms.ExpectBucketCount(
interaction_histogram,
security_interstitials::MetricsHelper::CLOSE_INTERSTITIAL_WITHOUT_UI, 1);
// CloseReason histograms.
histograms.ExpectTotalCount(kInterstitialCloseHistogram, 2);
histograms.ExpectBucketCount(
kInterstitialCloseHistogram,
security_interstitials::SecurityInterstitialTabHelper::
InterstitialCloseReason::INTERSTITIAL_SHOWN,
1);
histograms.ExpectBucketCount(
kInterstitialCloseHistogram,
security_interstitials::SecurityInterstitialTabHelper::
InterstitialCloseReason::CLOSE_TAB,
1);
}
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageBrowserTest, AllowlistRevisit) {
GURL url = SetupWarningAndNavigate(browser());
EXPECT_TRUE(ClickAndWaitForDetach("proceed-link"));
AssertNoInterstitial(); // Assert the interstitial is gone.
EXPECT_EQ(url, browser()
->tab_strip_model()
->GetActiveWebContents()
->GetLastCommittedURL());
// Unrelated pages should not be allowlisted now.
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL(kUnrelatedUrl)));
AssertNoInterstitial();
// The allowlisted page should remain allowlisted.
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
AssertNoInterstitial();
}
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageBrowserTest, AllowlistUnsaved) {
GURL url = SetupWarningAndNavigate(browser());
// Navigate without making a decision.
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL(kUnrelatedUrl)));
AssertNoInterstitial();
// The non-allowlisted page should now show an interstitial.
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
EXPECT_TRUE(WaitForReady(browser()));
EXPECT_TRUE(ClickAndWaitForDetach("proceed-link"));
AssertNoInterstitial();
}
#if (BUILDFLAG(IS_MAC) && !defined(NDEBUG)) || defined(MEMORY_SANITIZER)
// TODO(crbug.com/1132307): Address flay failure.
#define MAYBE_VerifyHitReportSentOnSBERAndNotIncognito \
DISABLED_VerifyHitReportSentOnSBERAndNotIncognito
#else
#define MAYBE_VerifyHitReportSentOnSBERAndNotIncognito \
VerifyHitReportSentOnSBERAndNotIncognito
#endif
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageBrowserTest,
MAYBE_VerifyHitReportSentOnSBERAndNotIncognito) {
// The extended reporting opt-in is presented in the interstitial for malware,
// phishing, and UwS threats.
const bool expect_threat_details =
SafeBrowsingBlockingPage::ShouldReportThreatDetails(GetThreatType());
base::RunLoop threat_report_sent_loop;
if (expect_threat_details)
SetReportSentCallback(threat_report_sent_loop.QuitClosure());
browser()->profile()->GetPrefs()->SetBoolean(
prefs::kSafeBrowsingScoutReportingEnabled, true);
GURL url = SetupWarningAndNavigate(browser()); // not incognito
EXPECT_TRUE(hit_report_sent());
EXPECT_TRUE(report_sent());
}
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageBrowserTest,
VerifyHitReportNotSentOnIncognito) {
browser()->profile()->GetPrefs()->SetBoolean(
prefs::kSafeBrowsingSurveysEnabled, false);
// The extended reporting opt-in is presented in the interstitial for malware,
// phishing, and UwS threats.
const bool expect_threat_details =
SafeBrowsingBlockingPage::ShouldReportThreatDetails(GetThreatType());
base::RunLoop threat_report_sent_loop;
if (expect_threat_details)
SetReportSentCallback(threat_report_sent_loop.QuitClosure());
Browser* incognito_browser = CreateIncognitoBrowser();
incognito_browser->profile()->GetPrefs()->SetBoolean(
prefs::kSafeBrowsingScoutReportingEnabled, true); // set up SBER
GURL url = SetupWarningAndNavigate(incognito_browser); // incognito
// Check enhanced protection message is not shown.
EXPECT_EQ(HIDDEN, ::safe_browsing::GetVisibility(
incognito_browser, "enhanced-protection-message"));
EXPECT_FALSE(hit_report_sent());
EXPECT_FALSE(report_sent());
}
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageBrowserTest,
VerifyHitReportNotSentWithoutSBER) {
// The extended reporting opt-in is presented in the interstitial for malware,
// phishing, and UwS threats.
const bool expect_threat_details =
SafeBrowsingBlockingPage::ShouldReportThreatDetails(GetThreatType());
base::RunLoop threat_report_sent_loop;
if (expect_threat_details)
SetReportSentCallback(threat_report_sent_loop.QuitClosure());
browser()->profile()->GetPrefs()->SetBoolean(
prefs::kSafeBrowsingScoutReportingEnabled, false); // set up SBER
GURL url = SetupWarningAndNavigate(browser()); // not incognito
EXPECT_FALSE(hit_report_sent());
EXPECT_FALSE(report_sent());
}
namespace {
class SecurityStyleTestObserver : public content::WebContentsObserver {
public:
explicit SecurityStyleTestObserver(content::WebContents* web_contents)
: content::WebContentsObserver(web_contents) {}
SecurityStyleTestObserver(const SecurityStyleTestObserver&) = delete;
SecurityStyleTestObserver& operator=(const SecurityStyleTestObserver&) =
delete;
std::optional<security_state::SecurityLevel> latest_security_level() const {
return latest_security_level_;
}
// WebContentsObserver:
void DidChangeVisibleSecurityState() override {
auto* helper = SecurityStateTabHelper::FromWebContents(web_contents());
latest_security_level_ = helper->GetSecurityLevel();
}
private:
std::optional<security_state::SecurityLevel> latest_security_level_;
};
} // namespace
// Test that the security indicator does not stay downgraded after
// clicking back from a Safe Browsing interstitial. Regression test for
// https://crbug.com/659709.
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageBrowserTest,
SecurityStateGoBack) {
// Navigate to a page so that there is somewhere to go back to.
GURL start_url = GURL(kUnrelatedUrl);
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), start_url));
// The security indicator should be downgraded while the interstitial shows.
GURL bad_url = embedded_test_server()->GetURL(kEmptyPage);
SetupWarningAndNavigate(browser());
WebContents* error_tab = browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(error_tab);
ExpectSecurityIndicatorDowngrade(error_tab, 0u);
content::NavigationEntry* entry =
error_tab->GetController().GetVisibleEntry();
ASSERT_TRUE(entry);
ASSERT_EQ(bad_url, entry->GetURL());
// Go back.
EXPECT_EQ(VISIBLE, GetVisibility("primary-button"));
EXPECT_EQ(HIDDEN, GetVisibility("details"));
EXPECT_EQ(HIDDEN, GetVisibility("proceed-link"));
EXPECT_EQ(HIDDEN, GetVisibility("error-code"));
EXPECT_TRUE(Click("details-button"));
EXPECT_EQ(VISIBLE, GetVisibility("details"));
EXPECT_EQ(VISIBLE, GetVisibility("proceed-link"));
EXPECT_EQ(HIDDEN, GetVisibility("error-code"));
EXPECT_TRUE(ClickAndWaitForDetach("primary-button"));
// The security indicator should *not* still be downgraded after going back.
AssertNoInterstitial();
WebContents* post_tab = browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(post_tab);
entry = post_tab->GetController().GetVisibleEntry();
ASSERT_TRUE(entry);
EXPECT_EQ(start_url, entry->GetURL());
ExpectNoSecurityIndicatorDowngrade(post_tab);
ClearBadURL(bad_url);
// Navigate to the URL that the interstitial was on, and check that it
// is no longer marked as dangerous.
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), bad_url));
ExpectNoSecurityIndicatorDowngrade(
browser()->tab_strip_model()->GetActiveWebContents());
}
// Test that the security indicator is downgraded after clicking through a
// Safe Browsing interstitial.
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageBrowserTest,
SecurityState_HTTP) {
// The security indicator should be downgraded while the interstitial shows.
SetupWarningAndNavigate(browser());
WebContents* error_tab = browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(error_tab);
ExpectSecurityIndicatorDowngrade(error_tab, 0u);
// The security indicator should still be downgraded post-interstitial.
EXPECT_TRUE(ClickAndWaitForDetach("proceed-link"));
AssertNoInterstitial();
WebContents* post_tab = browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(post_tab);
ExpectSecurityIndicatorDowngrade(post_tab, 0u);
}
// Test that the security indicator is downgraded even if the website has valid
// HTTPS (meaning that the SB state overrides the HTTPS state).
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageBrowserTest,
SecurityState_ValidHTTPS) {
WebContents* error_tab = browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(error_tab);
SecurityStyleTestObserver observer(error_tab);
// The security indicator should be downgraded while the interstitial shows.
SetupWarningAndNavigateToValidHTTPS();
ExpectSecurityIndicatorDowngrade(error_tab, 0u);
// The security indicator should still be downgraded post-interstitial.
EXPECT_TRUE(ClickAndWaitForDetach("proceed-link"));
AssertNoInterstitial();
WebContents* post_tab = browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(post_tab);
ExpectSecurityIndicatorDowngrade(post_tab, 0u);
}
// Test that the security indicator is still downgraded after two interstitials
// are shown in a row (one for Safe Browsing, one for invalid HTTPS).
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageBrowserTest,
SecurityState_InvalidHTTPS) {
// The security indicator should be downgraded while the interstitial shows.
SetupWarningAndNavigateToInvalidHTTPS();
WebContents* error_tab = browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(error_tab);
ExpectSecurityIndicatorDowngrade(error_tab, 0u);
// The security indicator should still be downgraded post-interstitial.
EXPECT_TRUE(ClickAndWaitForDetach("proceed-link"));
AssertNoInterstitial();
WebContents* post_tab = browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(post_tab);
// TODO(felt): Sometimes the cert status here is 0u, which is wrong.
// Filed https://crbug.com/641187 to investigate.
ExpectSecurityIndicatorDowngrade(post_tab, net::CERT_STATUS_INVALID);
}
// Test that no safe browsing interstitial will be shown, if URL matches
// enterprise safe browsing allowlist domains.
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageBrowserTest,
VerifyEnterpriseAllowlist) {
GURL url = embedded_test_server()->GetURL(kEmptyPage);
// Add test server domain into the enterprise allowlist.
base::Value::List allowlist;
allowlist.Append(url.host());
browser()->profile()->GetPrefs()->SetList(
prefs::kSafeBrowsingAllowlistDomains, std::move(allowlist));
SetURLThreatType(url, GetThreatType());
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
base::RunLoop().RunUntilIdle();
WebContents* contents = browser()->tab_strip_model()->GetActiveWebContents();
EXPECT_TRUE(
content::WaitForRenderFrameReady(contents->GetPrimaryMainFrame()));
EXPECT_FALSE(IsShowingInterstitial(contents));
}
INSTANTIATE_TEST_SUITE_P(
SafeBrowsingBlockingPageBrowserTestWithThreatTypeAndIsolationSetting,
SafeBrowsingBlockingPageBrowserTest,
testing::Combine(
testing::Values(
SBThreatType::SB_THREAT_TYPE_URL_MALWARE, // Threat types
SBThreatType::SB_THREAT_TYPE_URL_PHISHING,
SBThreatType::SB_THREAT_TYPE_URL_UNWANTED),
testing::Bool())); // If isolate all sites for testing.
// Check back and forward work correctly after clicking through an interstitial.
#if (BUILDFLAG(IS_MAC) && !defined(NDEBUG)) || defined(MEMORY_SANITIZER)
// TODO(crbug.com/1132307): Address flay failure.
#define MAYBE_NavigatingBackAndForth DISABLED_NavigatingBackAndForth
#else
#define MAYBE_NavigatingBackAndForth NavigatingBackAndForth
#endif
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageBrowserTest,
MAYBE_NavigatingBackAndForth) {
// Load a safe page. (Despite the name, kMaliciousPage is not the page flagged
// as bad in this test.)
GURL safe_url(embedded_test_server()->GetURL(kMaliciousPage));
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), safe_url));
// Navigate to a site that triggers a warning and click through it.
const GURL bad_url = SetupWarningAndNavigate(browser());
EXPECT_TRUE(ClickAndWaitForDetach("proceed-link"));
AssertNoInterstitial();
// Go back and check we are back on the safe site.
WebContents* contents = browser()->tab_strip_model()->GetActiveWebContents();
content::TestNavigationObserver back_observer(contents);
contents->GetController().GoBack();
back_observer.Wait();
EXPECT_EQ(safe_url, contents->GetLastCommittedURL());
// Check forward takes us back to the flagged site with no interstitial.
content::TestNavigationObserver forward_observer(contents);
contents->GetController().GoForward();
forward_observer.Wait();
WaitForReady(browser());
AssertNoInterstitial();
EXPECT_EQ(bad_url, contents->GetLastCommittedURL());
}
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageBrowserTest,
TimestampInCSBRRClickedThroughBlockingPage) {
SetExtendedReportingPrefForTests(browser()->profile()->GetPrefs(), true);
content::TestNavigationObserver observer(
browser()->tab_strip_model()->GetActiveWebContents());
SetupWarningAndNavigate(browser());
// Proceed to unsafe site, sending CSBRR.
EXPECT_TRUE(ClickAndWaitForDetach("proceed-link"));
observer.WaitForNavigationFinished();
// The "proceed" command should go back instead, if proceeding is disabled.
AssertNoInterstitial();
base::RunLoop threat_report_sent_loop;
SetReportSentCallback(threat_report_sent_loop.QuitClosure());
threat_report_sent_loop.Run();
std::string serialized = GetReportSent();
ClientSafeBrowsingReportRequest report;
ASSERT_TRUE(report.ParseFromString(serialized));
// The timstamp of the warning shown should be in CSBRRs.
EXPECT_TRUE(report.has_warning_shown_timestamp_msec());
}
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageBrowserTest,
TimestampInFallbackCSBRRSent) {
SetExtendedReportingPrefForTests(browser()->profile()->GetPrefs(), true);
content::TestNavigationObserver observer(
browser()->tab_strip_model()->GetActiveWebContents());
base::RunLoop threat_report_sent_loop;
SetReportSentCallback(threat_report_sent_loop.QuitClosure());
SetupWarningAndNavigate(browser());
ASSERT_TRUE(IsShowingInterstitial(
browser()->tab_strip_model()->GetActiveWebContents()));
// Send CSBRR without interactions.
chrome::CloseTab(browser());
observer.WaitForNavigationFinished();
threat_report_sent_loop.Run();
std::string serialized = GetReportSent();
ClientSafeBrowsingReportRequest report;
ASSERT_TRUE(report.ParseFromString(serialized));
// The timstamp of the warning shown should be in CSBRRs.
EXPECT_TRUE(report.has_warning_shown_timestamp_msec());
}
class AntiPhishingTelemetryBrowserTest
: public SafeBrowsingBlockingPageBrowserTest {};
INSTANTIATE_TEST_SUITE_P(
AntiPhishingTelemetryBrowserTestWithThreatTypeAndIsolationSetting,
AntiPhishingTelemetryBrowserTest,
testing::Combine(
testing::Values(
SBThreatType::SB_THREAT_TYPE_URL_PHISHING, // Threat types
SBThreatType::SB_THREAT_TYPE_URL_CLIENT_SIDE_PHISHING),
testing::Bool())); // If isolate all sites for testing.
IN_PROC_BROWSER_TEST_P(AntiPhishingTelemetryBrowserTest,
CheckReportListsInteractions) {
SetExtendedReportingPrefForTests(browser()->profile()->GetPrefs(), true);
content::TestNavigationObserver observer(
browser()->tab_strip_model()->GetActiveWebContents());
SetupWarningAndNavigate(browser());
// Show details 3x to make sure map records all 3 occurrences in interstitial
// interaction map.
EXPECT_TRUE(Click("details-button"));
SendCommand(security_interstitials::CMD_SHOW_MORE_SECTION);
SendCommand(security_interstitials::CMD_SHOW_MORE_SECTION);
// Proceed to unsafe site, sending CSBRR.
EXPECT_TRUE(ClickAndWaitForDetach("proceed-link"));
observer.WaitForNavigationFinished();
// The "proceed" command should go back instead, if proceeding is disabled.
AssertNoInterstitial();
scoped_refptr<content::MessageLoopRunner> threat_report_sent_runner(
new content::MessageLoopRunner);
SetReportSentCallback(threat_report_sent_runner->QuitClosure());
threat_report_sent_runner->Run();
std::string serialized = GetReportSent();
ClientSafeBrowsingReportRequest report;
ASSERT_TRUE(report.ParseFromString(serialized));
EXPECT_EQ(report.url(), embedded_test_server()->GetURL(kEmptyPage));
SBThreatType threat_type = GetThreatType();
// SB_THREAT_TYPE_URL_CLIENT_SIDE_PHISHING does not set the page_url because
// its resource's navigation_url is empty.
if (threat_type == SBThreatType::SB_THREAT_TYPE_URL_PHISHING) {
EXPECT_EQ(report.page_url(), embedded_test_server()->GetURL(kEmptyPage));
}
// Create sorted vector of interstitial interactions. Sorted by
// security_interstitial_interaction numeric value.
std::vector<ClientSafeBrowsingReportRequest::InterstitialInteraction>
interactions;
for (auto interaction : report.interstitial_interactions()) {
interactions.push_back(interaction);
}
std::sort(
interactions.begin(), interactions.end(),
[](const ClientSafeBrowsingReportRequest::InterstitialInteraction& a,
const ClientSafeBrowsingReportRequest::InterstitialInteraction& b)
-> bool {
return a.security_interstitial_interaction() <
b.security_interstitial_interaction();
});
// Verify the report interactions are complete and correct.
EXPECT_EQ(report.interstitial_interactions_size(), 2);
VerifyInteractionOccurrenceCount(
report, interactions[0],
ClientSafeBrowsingReportRequest::InterstitialInteraction::CMD_PROCEED, 1);
VerifyInteractionOccurrenceCount(
report, interactions[1],
ClientSafeBrowsingReportRequest::InterstitialInteraction::
CMD_SHOW_MORE_SECTION,
3);
}
IN_PROC_BROWSER_TEST_P(AntiPhishingTelemetryBrowserTest,
CheckReportCloseTabOnInterstitial) {
SetExtendedReportingPrefForTests(browser()->profile()->GetPrefs(), true);
content::TestNavigationObserver observer(
browser()->tab_strip_model()->GetActiveWebContents());
scoped_refptr<content::MessageLoopRunner> threat_report_sent_runner(
new content::MessageLoopRunner);
SetReportSentCallback(threat_report_sent_runner->QuitClosure());
SetupWarningAndNavigate(browser());
ASSERT_TRUE(IsShowingInterstitial(
browser()->tab_strip_model()->GetActiveWebContents()));
// Send CSBRR without interactions.
chrome::CloseTab(browser());
observer.WaitForNavigationFinished();
threat_report_sent_runner->Run();
std::string serialized = GetReportSent();
ClientSafeBrowsingReportRequest report;
ASSERT_TRUE(report.ParseFromString(serialized));
EXPECT_EQ(report.url(), embedded_test_server()->GetURL(kEmptyPage));
// Verify the report interactions only contain interstitial interactions.
SBThreatType threat_type = GetThreatType();
if (threat_type == SBThreatType::SB_THREAT_TYPE_URL_PHISHING) {
EXPECT_EQ(report.type(),
ClientSafeBrowsingReportRequest_ReportType_URL_PHISHING);
EXPECT_EQ(report.page_url(), embedded_test_server()->GetURL(kEmptyPage));
}
if (threat_type == SBThreatType::SB_THREAT_TYPE_URL_CLIENT_SIDE_PHISHING) {
EXPECT_EQ(
report.type(),
ClientSafeBrowsingReportRequest_ReportType_URL_CLIENT_SIDE_PHISHING);
}
EXPECT_EQ(report.interstitial_interactions_size(), 1);
EXPECT_EQ(
report.interstitial_interactions(0).security_interstitial_interaction(),
ClientSafeBrowsingReportRequest::InterstitialInteraction::
CMD_CLOSE_INTERSTITIAL_WITHOUT_UI);
EXPECT_EQ(report.interstitial_interactions(0).occurrence_count(), 1);
}
IN_PROC_BROWSER_TEST_P(
AntiPhishingTelemetryBrowserTest,
CheckReportListsInteractionsNoExplicitInterstitialDecision) {
SetExtendedReportingPrefForTests(browser()->profile()->GetPrefs(), true);
content::TestNavigationObserver observer(
browser()->tab_strip_model()->GetActiveWebContents());
scoped_refptr<content::MessageLoopRunner> threat_report_sent_runner(
new content::MessageLoopRunner);
SetReportSentCallback(threat_report_sent_runner->QuitClosure());
// Navigate to the page and show warning.
SetupWarningAndNavigate(browser());
// Navigate away from interstitial without making an explicit choice through
// the UI.
ASSERT_TRUE(
ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL)));
observer.WaitForNavigationFinished();
threat_report_sent_runner->Run();
std::string serialized = GetReportSent();
ClientSafeBrowsingReportRequest report;
ASSERT_TRUE(report.ParseFromString(serialized));
EXPECT_EQ(report.url(), embedded_test_server()->GetURL(kEmptyPage));
SBThreatType threat_type = GetThreatType();
if (threat_type == SBThreatType::SB_THREAT_TYPE_URL_PHISHING) {
EXPECT_EQ(report.page_url(), embedded_test_server()->GetURL(kEmptyPage));
}
// Verify the report interaction only contains a
// CMD_CLOSE_INTERSTITIAL_WITHOUT_UI interaction.
EXPECT_EQ(report.interstitial_interactions_size(), 1);
EXPECT_EQ(
report.interstitial_interactions(0).security_interstitial_interaction(),
ClientSafeBrowsingReportRequest::InterstitialInteraction::
CMD_CLOSE_INTERSTITIAL_WITHOUT_UI);
EXPECT_EQ(report.interstitial_interactions(0).occurrence_count(), 1);
}
INSTANTIATE_TEST_SUITE_P(
SafeBrowsingHatsSurveyBrowserTestWithThreatTypeAndIsolationSetting,
SafeBrowsingHatsSurveyBrowserTest,
testing::Combine(
// Threat types.
testing::Values(SBThreatType::SB_THREAT_TYPE_URL_MALWARE),
// If isolate all sites for testing.
testing::Bool()));
IN_PROC_BROWSER_TEST_P(SafeBrowsingHatsSurveyBrowserTest,
ReportNotSentToSbButAttachedForHats) {
SetExtendedReportingPrefForTests(browser()->profile()->GetPrefs(), false);
SetExpectEmptyReportForHats(false);
SetExpectReportUrlForHats(true);
SetExpectInterstitialInteractions(true);
content::TestNavigationObserver observer(
browser()->tab_strip_model()->GetActiveWebContents());
SetupWarningAndNavigate(browser());
ASSERT_TRUE(IsShowingInterstitial(
browser()->tab_strip_model()->GetActiveWebContents()));
EXPECT_CALL(*GetSafeBrowsingUiManager(), OnAttachThreatDetailsAndLaunchSurvey)
.Times(1);
// Generate interstitial interactions.
EXPECT_TRUE(Click("details-button"));
SendCommand(security_interstitials::CMD_SHOW_MORE_SECTION);
SendCommand(security_interstitials::CMD_SHOW_MORE_SECTION);
// Bypass warning.
// This triggers AttachThreatDetailsAndLaunchSurvey.
EXPECT_TRUE(ClickAndWaitForDetach("proceed-link"));
observer.WaitForNavigationFinished();
std::string report = GetReportSent();
EXPECT_TRUE(report.empty());
}
IN_PROC_BROWSER_TEST_P(SafeBrowsingHatsSurveyBrowserTest,
ReportSentToSbAndAttachedForHats) {
SetExtendedReportingPrefForTests(browser()->profile()->GetPrefs(), true);
SetExpectEmptyReportForHats(false);
SetExpectReportUrlForHats(true);
SetExpectInterstitialInteractions(true);
content::TestNavigationObserver observer(
browser()->tab_strip_model()->GetActiveWebContents());
scoped_refptr<content::MessageLoopRunner> threat_report_sent_runner(
new content::MessageLoopRunner);
SetReportSentCallback(threat_report_sent_runner->QuitClosure());
SetupWarningAndNavigate(browser());
ASSERT_TRUE(IsShowingInterstitial(
browser()->tab_strip_model()->GetActiveWebContents()));
EXPECT_CALL(*GetSafeBrowsingUiManager(), OnAttachThreatDetailsAndLaunchSurvey)
.Times(1);
// Generate interstitial interactions.
EXPECT_TRUE(Click("details-button"));
SendCommand(security_interstitials::CMD_SHOW_MORE_SECTION);
SendCommand(security_interstitials::CMD_SHOW_MORE_SECTION);
// Bypass warning.
// This triggers AttachThreatDetailsAndLaunchSurvey.
EXPECT_TRUE(ClickAndWaitForDetach("proceed-link"));
observer.WaitForNavigationFinished();
threat_report_sent_runner->Run();
std::string report = GetReportSent();
EXPECT_FALSE(report.empty());
}
IN_PROC_BROWSER_TEST_P(SafeBrowsingHatsSurveyBrowserTest,
NoHatsSurveyWhenProceedDisabled) {
browser()->profile()->GetPrefs()->SetBoolean(
prefs::kSafeBrowsingProceedAnywayDisabled, true);
SetExtendedReportingPrefForTests(browser()->profile()->GetPrefs(), false);
SetExpectEmptyReportForHats(true);
content::TestNavigationObserver observer(
browser()->tab_strip_model()->GetActiveWebContents());
SetupWarningAndNavigate(browser());
ASSERT_TRUE(IsShowingInterstitial(
browser()->tab_strip_model()->GetActiveWebContents()));
EXPECT_CALL(*GetSafeBrowsingUiManager(), OnAttachThreatDetailsAndLaunchSurvey)
.Times(0);
// Generate interstitial interactions.
EXPECT_TRUE(Click("details-button"));
SendCommand(security_interstitials::CMD_SHOW_MORE_SECTION);
SendCommand(security_interstitials::CMD_SHOW_MORE_SECTION);
// Navigate away from the page.
// This would trigger AttachThreatDetailsAndLaunchSurvey but does not because
// proceed is disabled.
ASSERT_TRUE(
ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL)));
observer.WaitForNavigationFinished();
}
IN_PROC_BROWSER_TEST_P(SafeBrowsingHatsSurveyBrowserTest,
NoHatsSurveyWhenSafeBrowsingSurveysDisabled) {
browser()->profile()->GetPrefs()->SetBoolean(
prefs::kSafeBrowsingSurveysEnabled, false);
SetExtendedReportingPrefForTests(browser()->profile()->GetPrefs(), false);
SetExpectEmptyReportForHats(true);
content::TestNavigationObserver observer(
browser()->tab_strip_model()->GetActiveWebContents());
SetupWarningAndNavigate(browser());
ASSERT_TRUE(IsShowingInterstitial(
browser()->tab_strip_model()->GetActiveWebContents()));
EXPECT_CALL(*GetSafeBrowsingUiManager(), OnAttachThreatDetailsAndLaunchSurvey)
.Times(0);
// Generate interstitial interactions.
EXPECT_TRUE(Click("details-button"));
SendCommand(security_interstitials::CMD_SHOW_MORE_SECTION);
SendCommand(security_interstitials::CMD_SHOW_MORE_SECTION);
// Bypass warning.
// This would trigger AttachThreatDetailsAndLaunchSurvey but does not because
// Safe Browsing surveys are disabled.
EXPECT_TRUE(ClickAndWaitForDetach("proceed-link"));
observer.WaitForNavigationFinished();
}
class TrustSafetySentimentSurveyV2BrowserTest
: public SafeBrowsingBlockingPageBrowserTest {
public:
TrustSafetySentimentSurveyV2BrowserTest() {
scoped_feature_list_.InitAndEnableFeature(
features::kTrustSafetySentimentSurveyV2);
}
~TrustSafetySentimentSurveyV2BrowserTest() override = default;
void SetUp() override {
#if BUILDFLAG(IS_CHROMEOS_ASH)
metrics::DesktopSessionDurationTracker::Initialize();
#endif
SafeBrowsingBlockingPageBrowserTest::SetUp();
}
void TearDown() override {
#if BUILDFLAG(IS_CHROMEOS_ASH)
metrics::DesktopSessionDurationTracker::CleanupForTesting();
#endif
}
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
INSTANTIATE_TEST_SUITE_P(
TrustSafetySentimentSurveyV2BrowserTestWithThreatTypeAndIsolationSetting,
TrustSafetySentimentSurveyV2BrowserTest,
testing::Combine(
testing::Values(
SBThreatType::SB_THREAT_TYPE_URL_PHISHING, // Threat types
SBThreatType::SB_THREAT_TYPE_URL_CLIENT_SIDE_PHISHING,
SBThreatType::SB_THREAT_TYPE_URL_MALWARE,
SBThreatType::SB_THREAT_TYPE_URL_UNWANTED),
testing::Bool())); // If isolate all sites for testing.
IN_PROC_BROWSER_TEST_P(TrustSafetySentimentSurveyV2BrowserTest,
TrustSafetySentimentTriggerredOnProceed) {
GURL url = SetupWarningAndNavigate(browser());
EXPECT_CALL(*mock_sentiment_service(),
InteractedWithSafeBrowsingInterstitial(/*did_proceed=*/true,
GetThreatType()));
EXPECT_TRUE(ClickAndWaitForDetach("proceed-link"));
AssertNoInterstitial(); // Assert the interstitial is gone.
}
IN_PROC_BROWSER_TEST_P(TrustSafetySentimentSurveyV2BrowserTest,
TrustSafetySentimentTriggerredOnPrimaryButtonClick) {
GURL url = SetupWarningAndNavigate(browser());
EXPECT_CALL(*mock_sentiment_service(),
InteractedWithSafeBrowsingInterstitial(/*did_proceed=*/false,
GetThreatType()));
EXPECT_TRUE(ClickAndWaitForDetach("primary-button"));
AssertNoInterstitial(); // Assert the interstitial is gone.
}
IN_PROC_BROWSER_TEST_P(TrustSafetySentimentSurveyV2BrowserTest,
TrustSafetySentimentTriggeredOnCloseInterstitialTab) {
content::TestNavigationObserver observer(
browser()->tab_strip_model()->GetActiveWebContents());
GURL url = SetupWarningAndNavigate(browser());
EXPECT_CALL(*mock_sentiment_service(),
InteractedWithSafeBrowsingInterstitial(/*did_proceed=*/false,
GetThreatType()));
chrome::CloseTab(browser());
observer.WaitForNavigationFinished();
}
using RedInterstitialFaceliftBrowserTest = SafeBrowsingBlockingPageBrowserTest;
INSTANTIATE_TEST_SUITE_P(
RedInterstitialFaceliftBrowserTestWithThreatTypeAndIsolationSetting,
RedInterstitialFaceliftBrowserTest,
testing::Combine(
testing::Values(
SBThreatType::SB_THREAT_TYPE_URL_PHISHING, // Threat types
SBThreatType::SB_THREAT_TYPE_URL_MALWARE,
SBThreatType::SB_THREAT_TYPE_URL_CLIENT_SIDE_PHISHING,
SBThreatType::SB_THREAT_TYPE_URL_UNWANTED),
testing::Bool())); // If isolate all sites for testing.
IN_PROC_BROWSER_TEST_P(RedInterstitialFaceliftBrowserTest,
TestInterstitialPageStringsEnhancedEnabled) {
safe_browsing::SetSafeBrowsingState(
browser()->profile()->GetPrefs(),
safe_browsing::SafeBrowsingState::ENHANCED_PROTECTION);
auto threat_report_sent_runner = std::make_unique<base::RunLoop>();
SetReportSentCallback(threat_report_sent_runner->QuitClosure());
GURL url = SetupWarningAndNavigate(browser());
WebContents* contents = browser()->tab_strip_model()->GetActiveWebContents();
SafeBrowsingBlockingPage* interstitial_page;
security_interstitials::SecurityInterstitialTabHelper* helper =
security_interstitials::SecurityInterstitialTabHelper::FromWebContents(
contents);
ASSERT_TRUE(helper);
interstitial_page = static_cast<SafeBrowsingBlockingPage*>(
helper->GetBlockingPageForCurrentlyCommittedNavigationForTesting());
BaseSafeBrowsingErrorUI* temp_var = interstitial_page->sb_error_ui();
base::Value::Dict load_time_data;
temp_var->PopulateStringsForHtml(load_time_data);
// Safe browsing blocking page should use new heading and primary,
// explanation, and proceed paragraph strings.
ASSERT_EQ(
load_time_data.Find("heading")->GetString(),
base::UTF16ToUTF8(l10n_util::GetStringUTF16(IDS_SAFEBROWSING_HEADING)));
SBThreatType threat_type = GetThreatType();
if (threat_type == SBThreatType::SB_THREAT_TYPE_URL_PHISHING ||
threat_type == SBThreatType::SB_THREAT_TYPE_URL_CLIENT_SIDE_PHISHING) {
ASSERT_EQ(load_time_data.Find("primaryParagraph")->GetString(),
base::UTF16ToUTF8(l10n_util::GetStringUTF16(
IDS_PHISHING_V4_PRIMARY_PARAGRAPH)));
ASSERT_EQ(load_time_data.Find("explanationParagraph")->GetString(),
base::UTF16ToUTF8(l10n_util::GetStringUTF16(
IDS_PHISHING_V4_EXPLANATION_PARAGRAPH)));
ASSERT_EQ(load_time_data.Find("finalParagraph")->GetString(),
base::UTF16ToUTF8(l10n_util::GetStringUTF16(
IDS_PHISHING_V4_PROCEED_PARAGRAPH)));
} else if (threat_type == SBThreatType::SB_THREAT_TYPE_URL_MALWARE) {
ASSERT_EQ(load_time_data.Find("primaryParagraph")->GetString(),
base::UTF16ToUTF8(
l10n_util::GetStringUTF16(IDS_MALWARE_V3_PRIMARY_PARAGRAPH)));
ASSERT_EQ(load_time_data.Find("explanationParagraph")->GetString(),
base::UTF16ToUTF8(l10n_util::GetStringUTF16(
IDS_MALWARE_V3_EXPLANATION_PARAGRAPH)));
ASSERT_EQ(load_time_data.Find("finalParagraph")->GetString(),
base::UTF16ToUTF8(
l10n_util::GetStringUTF16(IDS_MALWARE_V3_PROCEED_PARAGRAPH)));
} else {
ASSERT_EQ(load_time_data.Find("primaryParagraph")->GetString(),
base::UTF16ToUTF8(
l10n_util::GetStringUTF16(IDS_HARMFUL_V3_PRIMARY_PARAGRAPH)));
ASSERT_EQ(load_time_data.Find("explanationParagraph")->GetString(),
base::UTF16ToUTF8(l10n_util::GetStringUTF16(
IDS_HARMFUL_V3_EXPLANATION_PARAGRAPH)));
ASSERT_EQ(load_time_data.Find("finalParagraph")->GetString(),
base::UTF16ToUTF8(
l10n_util::GetStringUTF16(IDS_HARMFUL_V3_PROCEED_PARAGRAPH)));
}
}
IN_PROC_BROWSER_TEST_P(RedInterstitialFaceliftBrowserTest,
TestInterstitialPageStringsStandardEnabled) {
safe_browsing::SetSafeBrowsingState(
browser()->profile()->GetPrefs(),
safe_browsing::SafeBrowsingState::STANDARD_PROTECTION);
auto threat_report_sent_runner = std::make_unique<base::RunLoop>();
SetReportSentCallback(threat_report_sent_runner->QuitClosure());
GURL url = SetupWarningAndNavigate(browser());
WebContents* contents = browser()->tab_strip_model()->GetActiveWebContents();
SafeBrowsingBlockingPage* interstitial_page;
security_interstitials::SecurityInterstitialTabHelper* helper =
security_interstitials::SecurityInterstitialTabHelper::FromWebContents(
contents);
ASSERT_TRUE(helper);
interstitial_page = static_cast<SafeBrowsingBlockingPage*>(
helper->GetBlockingPageForCurrentlyCommittedNavigationForTesting());
BaseSafeBrowsingErrorUI* temp_var = interstitial_page->sb_error_ui();
base::Value::Dict load_time_data;
temp_var->PopulateStringsForHtml(load_time_data);
// Safe browsing blocking page should use new header and enhanced protection
// promo message strings.
ASSERT_EQ(
load_time_data.Find("heading")->GetString(),
base::UTF16ToUTF8(l10n_util::GetStringUTF16(IDS_SAFEBROWSING_HEADING)));
ASSERT_EQ(
load_time_data.Find(security_interstitials::kEnhancedProtectionMessage)
->GetString(),
base::UTF16ToUTF8(l10n_util::GetStringUTF16(
IDS_SAFE_BROWSING_ENHANCED_PROTECTION_MESSAGE)));
}
class SafeBrowsingBlockingPageDelayedWarningBrowserTest
: public InProcessBrowserTest,
public testing::WithParamInterface<
testing::tuple<bool /* IsolateAllSitesForTesting */,
bool /* Show warning on mouse click */>> {
public:
SafeBrowsingBlockingPageDelayedWarningBrowserTest() = default;
SafeBrowsingBlockingPageDelayedWarningBrowserTest(
const SafeBrowsingBlockingPageDelayedWarningBrowserTest&) = delete;
SafeBrowsingBlockingPageDelayedWarningBrowserTest& operator=(
const SafeBrowsingBlockingPageDelayedWarningBrowserTest&) = delete;
void SetUp() override {
std::vector<base::test::FeatureRefAndParams> enabled_features;
if (warning_on_mouse_click_enabled()) {
enabled_features.push_back(base::test::FeatureRefAndParams(
kDelayedWarnings, {{"mouse", "true"}}));
} else {
enabled_features.push_back(
base::test::FeatureRefAndParams(kDelayedWarnings, {}));
}
std::vector<base::test::FeatureRef> disabled_features;
GetAdditionalFeatures(&enabled_features, &disabled_features);
scoped_feature_list_.InitWithFeaturesAndParameters(enabled_features,
disabled_features);
InProcessBrowserTest::SetUp();
}
void SetUpCommandLine(base::CommandLine* command_line) override {
if (testing::get<0>(GetParam())) {
content::IsolateAllSitesForTesting(command_line);
}
// TODO(crbug.com/1491942): This fails with the field trial testing config.
command_line->AppendSwitch("disable-field-trial-config");
}
void SetUpOnMainThread() override {
host_resolver()->AddRule("*", "127.0.0.1");
content::SetupCrossSiteRedirector(embedded_test_server());
ASSERT_TRUE(embedded_test_server()->Start());
}
void CreatedBrowserMainParts(
content::BrowserMainParts* browser_main_parts) override {
InProcessBrowserTest::CreatedBrowserMainParts(browser_main_parts);
// Test UI manager and test database manager should be set before
// the browser is started but after threads are created.
factory_.SetTestUIManager(new FakeSafeBrowsingUIManager(
std::make_unique<TestSafeBrowsingBlockingPageFactory>()));
factory_.SetTestDatabaseManager(new FakeSafeBrowsingDatabaseManager(
content::GetUIThreadTaskRunner({}),
content::GetIOThreadTaskRunner({})));
SafeBrowsingService::RegisterFactory(&factory_);
ThreatDetails::RegisterFactory(&details_factory_);
}
static bool TypeAndWaitForInterstitial(Browser* browser) {
// Type something. An interstitial should be shown.
content::WebContents* contents =
browser->tab_strip_model()->GetActiveWebContents();
content::TestNavigationObserver observer(contents);
content::NativeWebKeyboardEvent event(
blink::WebKeyboardEvent::Type::kRawKeyDown,
blink::WebInputEvent::kNoModifiers,
blink::WebInputEvent::GetStaticTimeStampForTests());
event.text[0] = 'a';
content::RenderWidgetHost* rwh =
contents->GetPrimaryMainFrame()->GetRenderViewHost()->GetWidget();
rwh->ForwardKeyboardEvent(event);
observer.WaitForNavigationFinished();
return WaitForReady(browser);
}
static void MouseClick(Browser* browser) {
blink::WebMouseEvent event(
blink::WebInputEvent::Type::kMouseDown,
blink::WebInputEvent::kNoModifiers,
blink::WebInputEvent::GetStaticTimeStampForTests());
event.button = blink::WebMouseEvent::Button::kLeft;
event.SetPositionInWidget(100, 100);
event.click_count = 1;
content::WebContents* contents =
browser->tab_strip_model()->GetActiveWebContents();
content::RenderWidgetHost* rwh =
contents->GetPrimaryMainFrame()->GetRenderViewHost()->GetWidget();
rwh->ForwardMouseEvent(event);
}
static bool MouseClickAndWaitForInterstitial(Browser* browser) {
content::WebContents* contents =
browser->tab_strip_model()->GetActiveWebContents();
content::TestNavigationObserver observer(contents);
MouseClick(browser);
observer.WaitForNavigationFinished();
return WaitForReady(browser);
}
static bool FullscreenAndWaitForInterstitial(Browser* browser) {
content::WebContents* contents =
browser->tab_strip_model()->GetActiveWebContents();
content::TestNavigationObserver observer(contents);
const char* const kScript = "document.body.webkitRequestFullscreen()";
EXPECT_TRUE(content::ExecJs(contents, kScript));
observer.WaitForNavigationFinished();
return WaitForReady(browser);
}
static bool RequestPermissionAndWaitForInterstitial(Browser* browser) {
content::WebContents* contents =
browser->tab_strip_model()->GetActiveWebContents();
content::TestNavigationObserver observer(contents);
const char* const kScript = "Notification.requestPermission(function(){})";
EXPECT_TRUE(content::ExecJs(contents, kScript));
observer.WaitForNavigationFinished();
return WaitForReady(browser);
}
static bool RequestDesktopCaptureAndWaitForInterstitial(Browser* browser) {
content::WebContents* contents =
browser->tab_strip_model()->GetActiveWebContents();
content::TestNavigationObserver observer(contents);
const char* const kScript = "navigator.mediaDevices.getDisplayMedia()";
EXPECT_TRUE(content::ExecJs(contents, kScript,
content::EXECUTE_SCRIPT_NO_RESOLVE_PROMISES));
observer.WaitForNavigationFinished();
return WaitForReady(browser);
}
protected:
// Subclasses can override to enable/disable features in SetUp().
virtual void GetAdditionalFeatures(
std::vector<base::test::FeatureRefAndParams>* enabled_features,
std::vector<base::test::FeatureRef>* disabled_features) {}
// Initiates a download and waits for it to be completed or cancelled.
static void DownloadAndWaitForNavigation(Browser* browser) {
content::WebContents* contents =
browser->tab_strip_model()->GetActiveWebContents();
content::TestNavigationObserver observer(contents);
content::WebContentsConsoleObserver console_observer(contents);
console_observer.SetPattern(
"A SafeBrowsing warning is pending on this page*");
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser, GURL("data:application/octet-stream;base64,SGVsbG8=")));
observer.WaitForNavigationFinished();
ASSERT_TRUE(console_observer.Wait());
ASSERT_EQ(1u, console_observer.messages().size());
}
void NavigateAndAssertNoInterstitial() {
const GURL top_frame = embedded_test_server()->GetURL("/iframe.html");
SetURLThreatType(top_frame, SBThreatType::SB_THREAT_TYPE_URL_PHISHING);
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), top_frame));
AssertNoInterstitial(browser());
}
bool warning_on_mouse_click_enabled() const {
return testing::get<1>(GetParam());
}
void SetURLThreatType(const GURL& url, SBThreatType threat_type) {
TestSafeBrowsingService* service = factory_.test_safe_browsing_service();
ASSERT_TRUE(service);
static_cast<FakeSafeBrowsingDatabaseManager*>(
service->database_manager().get())
->AddDangerousUrl(url, threat_type);
}
std::u16string GetSecuritySummaryTextFromPageInfo() {
auto* page_info = PageInfoBubbleView::GetPageInfoBubbleForTesting();
auto* summary_label = page_info->GetViewByID(
PageInfoViewFactory::VIEW_ID_PAGE_INFO_SECURITY_SUMMARY_LABEL);
return static_cast<views::StyledLabel*>(summary_label)->GetText();
}
protected:
base::test::ScopedFeatureList scoped_feature_list_;
private:
TestSafeBrowsingServiceFactory factory_;
TestThreatDetailsFactory details_factory_;
};
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageDelayedWarningBrowserTest,
NoInteraction_WarningNotShown) {
base::HistogramTester histograms;
NavigateAndAssertNoInterstitial();
// Navigate away without interacting with the page.
ASSERT_TRUE(
ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL)));
}
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageDelayedWarningBrowserTest,
NotPhishing_WarningNotDelayed) {
base::HistogramTester histograms;
// Navigate to a non-phishing page. The warning should not be delayed.
const GURL url = embedded_test_server()->GetURL("/empty.html");
SetURLThreatType(url, SBThreatType::SB_THREAT_TYPE_URL_MALWARE);
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
EXPECT_TRUE(WaitForReady(browser()));
// Navigate to about:blank to "flush" metrics, if any.
ASSERT_TRUE(
ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL)));
}
// Close the tab while a user interaction observer is attached to the tab. It
// shouldn't crash.
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageDelayedWarningBrowserTest,
CloseTab_ShouldNotCrash) {
base::HistogramTester histograms;
chrome::NewTab(browser());
NavigateAndAssertNoInterstitial();
chrome::CloseTab(browser());
}
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageDelayedWarningBrowserTest,
KeyPress_WarningShown) {
constexpr int kTimeOnPage = 10;
base::HistogramTester histograms;
NavigateAndAssertNoInterstitial();
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
// Inject a test clock to test the histogram that records the time on the
// delayed warning page before the warning shows or the user leaves the page.
base::SimpleTestClock clock;
SafeBrowsingUserInteractionObserver* observer =
SafeBrowsingUserInteractionObserver::FromWebContents(web_contents);
ASSERT_TRUE(observer);
clock.SetNow(observer->GetCreationTimeForTesting());
observer->SetClockForTesting(&clock);
clock.Advance(base::Seconds(kTimeOnPage));
// Type something. An interstitial should be shown.
EXPECT_TRUE(TypeAndWaitForInterstitial(browser()));
EXPECT_TRUE(ClickAndWaitForDetach(browser(), "primary-button"));
AssertNoInterstitial(browser()); // Assert the interstitial is gone
EXPECT_EQ(GURL(url::kAboutBlankURL), // Back to "about:blank"
browser()
->tab_strip_model()
->GetActiveWebContents()
->GetLastCommittedURL());
}
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageDelayedWarningBrowserTest,
KeyPress_ESC_WarningNotShown) {
base::HistogramTester histograms;
NavigateAndAssertNoInterstitial();
// Press ESC key. The interstitial should not be shown.
content::WebContents* contents =
browser()->tab_strip_model()->GetActiveWebContents();
content::NativeWebKeyboardEvent event(
blink::WebKeyboardEvent::Type::kRawKeyDown,
blink::WebInputEvent::kNoModifiers,
blink::WebInputEvent::GetStaticTimeStampForTests());
event.windows_key_code = ui::VKEY_ESCAPE;
// Browser expects a non-synthesized event to have an os_event. Make the
// browser ignore this event instead.
event.skip_if_unhandled = true;
contents->GetPrimaryMainFrame()
->GetRenderViewHost()
->GetWidget()
->ForwardKeyboardEvent(event);
AssertNoInterstitial(browser());
// Navigate to about:blank twice to "flush" metrics, if any. The delayed
// warning user interaction observer may not have been deleted after the first
// navigation.
ASSERT_TRUE(
ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL)));
ASSERT_TRUE(
ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL)));
}
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageDelayedWarningBrowserTest,
KeyPress_ModifierKey_WarningNotShown) {
base::HistogramTester histograms;
NavigateAndAssertNoInterstitial();
// Press CTRL+A key. The interstitial should not be shown because we ignore
// the CTRL modifier unless it's CTRL+C or CTRL+V.
content::WebContents* contents =
browser()->tab_strip_model()->GetActiveWebContents();
content::NativeWebKeyboardEvent event(
blink::WebKeyboardEvent::Type::kRawKeyDown,
blink::WebInputEvent::kControlKey,
blink::WebInputEvent::GetStaticTimeStampForTests());
event.windows_key_code = ui::VKEY_A;
// Browser expects a non-synthesized event to have an os_event. Make the
// browser ignore this event instead.
event.skip_if_unhandled = true;
contents->GetPrimaryMainFrame()
->GetRenderViewHost()
->GetWidget()
->ForwardKeyboardEvent(event);
AssertNoInterstitial(browser());
// Navigate to about:blank twice to "flush" metrics, if any. The delayed
// warning user interaction observer may not have been deleted after the first
// navigation.
ASSERT_TRUE(
ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL)));
}
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageDelayedWarningBrowserTest,
KeyPress_CtrlC_WarningShown) {
base::HistogramTester histograms;
NavigateAndAssertNoInterstitial();
// Press CTRL+C. The interstitial should be shown.
content::WebContents* contents =
browser()->tab_strip_model()->GetActiveWebContents();
content::TestNavigationObserver observer(contents);
content::NativeWebKeyboardEvent event(
blink::WebKeyboardEvent::Type::kRawKeyDown,
blink::WebInputEvent::kControlKey,
blink::WebInputEvent::GetStaticTimeStampForTests());
event.windows_key_code = ui::VKEY_C;
event.native_key_code = ui::VKEY_C;
// We don't set event.skip_if_unhandled = true here because the event will be
// consumed by UserInteractionObserver and not passed to the browser.
contents->GetPrimaryMainFrame()
->GetRenderViewHost()
->GetWidget()
->ForwardKeyboardEvent(event);
observer.WaitForNavigationFinished();
EXPECT_TRUE(WaitForReady(browser()));
EXPECT_TRUE(ClickAndWaitForDetach(browser(), "primary-button"));
AssertNoInterstitial(browser()); // Assert the interstitial is gone
EXPECT_EQ(GURL(url::kAboutBlankURL), // Back to "about:blank"
browser()
->tab_strip_model()
->GetActiveWebContents()
->GetLastCommittedURL());
}
// Similar to KeyPress_ESC_WarningNotShown, but a character key is pressed after
// ESC. The warning should be shown.
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageDelayedWarningBrowserTest,
KeyPress_ESCAndCharacterKey_WarningShown) {
base::HistogramTester histograms;
NavigateAndAssertNoInterstitial();
// Press ESC key. The interstitial should not be shown.
content::WebContents* contents =
browser()->tab_strip_model()->GetActiveWebContents();
content::NativeWebKeyboardEvent event(
blink::WebKeyboardEvent::Type::kRawKeyDown,
blink::WebInputEvent::kNoModifiers,
blink::WebInputEvent::GetStaticTimeStampForTests());
event.windows_key_code = ui::VKEY_ESCAPE;
// Browser expects a non-synthesized event to have an os_event. Make the
// browser ignore this event instead.
event.skip_if_unhandled = true;
contents->GetPrimaryMainFrame()
->GetRenderViewHost()
->GetWidget()
->ForwardKeyboardEvent(event);
base::RunLoop().RunUntilIdle();
AssertNoInterstitial(browser());
// Now type something. The interstitial should be shown.
EXPECT_TRUE(TypeAndWaitForInterstitial(browser()));
EXPECT_TRUE(ClickAndWaitForDetach(browser(), "primary-button"));
AssertNoInterstitial(browser()); // Assert the interstitial is gone
EXPECT_EQ(GURL(url::kAboutBlankURL), // Back to "about:blank"
browser()
->tab_strip_model()
->GetActiveWebContents()
->GetLastCommittedURL());
}
// Disabled due to flakiness. https://crbug.com/332097746.
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageDelayedWarningBrowserTest,
DISABLED_Fullscreen_WarningShown) {
base::HistogramTester histograms;
NavigateAndAssertNoInterstitial();
// Page tries to enter fullscreen. An interstitial should be shown.
EXPECT_TRUE(FullscreenAndWaitForInterstitial(browser()));
EXPECT_FALSE(
browser()->tab_strip_model()->GetActiveWebContents()->IsFullscreen());
EXPECT_TRUE(ClickAndWaitForDetach(browser(), "primary-button"));
AssertNoInterstitial(browser()); // Assert the interstitial is gone
EXPECT_EQ(GURL(url::kAboutBlankURL), // Back to "about:blank"
browser()
->tab_strip_model()
->GetActiveWebContents()
->GetLastCommittedURL());
}
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageDelayedWarningBrowserTest,
PermissionRequest_WarningShown) {
base::HistogramTester histograms;
NavigateAndAssertNoInterstitial();
// Page tries to request a notification permission. The prompt should be
// cancelled and an interstitial should be shown.
EXPECT_TRUE(RequestPermissionAndWaitForInterstitial(browser()));
EXPECT_TRUE(ClickAndWaitForDetach(browser(), "primary-button"));
AssertNoInterstitial(browser()); // Assert the interstitial is gone
EXPECT_EQ(GURL(url::kAboutBlankURL), // Back to "about:blank"
browser()
->tab_strip_model()
->GetActiveWebContents()
->GetLastCommittedURL());
histograms.ExpectTotalCount("Permissions.Action.Notifications", 1);
histograms.ExpectBucketCount(
"Permissions.Action.Notifications",
static_cast<int>(permissions::PermissionAction::DENIED), 1);
}
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageDelayedWarningBrowserTest,
JavaScriptDialog_WarningShown) {
base::HistogramTester histograms;
NavigateAndAssertNoInterstitial();
// Page tries to show a JavaScript dialog. The dialog should be
// cancelled and an interstitial should be shown.
content::WebContents* contents =
browser()->tab_strip_model()->GetActiveWebContents();
content::TestNavigationObserver observer(contents);
EXPECT_TRUE(content::ExecJs(contents, "alert('test')"));
observer.WaitForNavigationFinished();
EXPECT_TRUE(WaitForReady(browser()));
EXPECT_TRUE(ClickAndWaitForDetach(browser(), "primary-button"));
AssertNoInterstitial(browser()); // Assert the interstitial is gone
EXPECT_EQ(GURL(url::kAboutBlankURL), // Back to "about:blank"
browser()
->tab_strip_model()
->GetActiveWebContents()
->GetLastCommittedURL());
}
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageDelayedWarningBrowserTest,
DesktopCaptureRequest_WarningShown) {
base::HistogramTester histograms;
NavigateAndAssertNoInterstitial();
// Page tries to request a desktop capture permission. The request should be
// cancelled and an interstitial should be shown.
EXPECT_TRUE(RequestDesktopCaptureAndWaitForInterstitial(browser()));
EXPECT_FALSE(
browser()->tab_strip_model()->GetActiveWebContents()->IsFullscreen());
EXPECT_TRUE(ClickAndWaitForDetach(browser(), "primary-button"));
AssertNoInterstitial(browser()); // Assert the interstitial is gone
EXPECT_EQ(GURL(url::kAboutBlankURL), // Back to "about:blank"
browser()
->tab_strip_model()
->GetActiveWebContents()
->GetLastCommittedURL());
}
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageDelayedWarningBrowserTest,
Paste_WarningShown) {
base::HistogramTester histograms;
NavigateAndAssertNoInterstitial();
// Create a test context menu and send a paste command through it. This
// should show the delayed interstitial.
content::WebContents* contents =
browser()->tab_strip_model()->GetActiveWebContents();
content::TestNavigationObserver observer(contents);
std::unique_ptr<TestRenderViewContextMenu> menu(
TestRenderViewContextMenu::Create(contents,
contents->GetLastCommittedURL()));
menu->ExecuteCommand(IDC_CONTENT_CONTEXT_PASTE, 0);
observer.WaitForNavigationFinished();
EXPECT_TRUE(WaitForReady(browser()));
}
// The user clicks on the page. Feature isn't configured to show a warning on
// mouse clicks. We should record that the user interacted with the page, but
// shouldn't shown an interstitial.
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageDelayedWarningBrowserTest,
MouseClick_WarningNotShown) {
if (warning_on_mouse_click_enabled()) {
return;
}
base::HistogramTester histograms;
NavigateAndAssertNoInterstitial();
// Click on the page. An interstitial shouldn't be shown because the feature
// parameter is off.
MouseClick(browser());
AssertNoInterstitial(browser());
// Navigate away to "flush" the metrics.
ASSERT_TRUE(
ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL)));
}
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageDelayedWarningBrowserTest,
MouseClick_WarningShown) {
if (!warning_on_mouse_click_enabled()) {
return;
}
base::HistogramTester histograms;
NavigateAndAssertNoInterstitial();
// Click on the page. An interstitial should be shown because the feature
// parameter is on.
EXPECT_TRUE(MouseClickAndWaitForInterstitial(browser()));
EXPECT_TRUE(ClickAndWaitForDetach(browser(), "primary-button"));
AssertNoInterstitial(browser()); // Assert the interstitial is gone
EXPECT_EQ(GURL(url::kAboutBlankURL), // Back to "about:blank"
browser()
->tab_strip_model()
->GetActiveWebContents()
->GetLastCommittedURL());
}
// This test initiates a download when a warning is delayed. The download should
// be cancelled and the interstitial should not be shown.
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageDelayedWarningBrowserTest,
Download_CancelledWithNoInterstitial) {
base::HistogramTester histograms;
NavigateAndAssertNoInterstitial();
DownloadAndWaitForNavigation(browser());
AssertNoInterstitial(browser());
// Navigate away to "flush" the metrics.
ASSERT_TRUE(
ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL)));
}
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageDelayedWarningBrowserTest,
InteractionAfterNonCommittingNavigation_Interstitial) {
base::HistogramTester histograms;
NavigateAndAssertNoInterstitial();
const GURL url_204 = embedded_test_server()->GetURL("/page204.html");
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url_204));
AssertNoInterstitial(browser());
EXPECT_TRUE(TypeAndWaitForInterstitial(browser()));
// Navigate away to "flush" the metrics.
ASSERT_TRUE(
ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL)));
}
// This test navigates to a page with password form and submits a password. The
// warning should be delayed, the "Save Password" bubble should not be shown,
// and a histogram entry for the password save should be recorded.
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageDelayedWarningBrowserTest,
PasswordSaveDisabled) {
base::HistogramTester histograms;
// This is needed for tests using BubbleObserver
content::WebContents* contents = nullptr;
PasswordManagerBrowserTestBase::GetNewTab(browser(), &contents);
// Navigate to the page.
content::TestNavigationObserver observer1(contents);
const GURL url =
embedded_test_server()->GetURL("/password/password_form.html");
SetURLThreatType(url, SBThreatType::SB_THREAT_TYPE_URL_PHISHING);
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
observer1.Wait();
// Submit a password.
PasswordsNavigationObserver observer2(contents);
std::unique_ptr<BubbleObserver> prompt_observer(new BubbleObserver(contents));
std::string fill_and_submit =
"document.getElementById('retry_password_field').value = 'pw';"
"document.getElementById('retry_submit_button').click()";
ASSERT_TRUE(content::ExecJs(contents, fill_and_submit));
ASSERT_TRUE(observer2.Wait());
EXPECT_FALSE(prompt_observer->IsSavePromptShownAutomatically());
PasswordManagerBrowserTestBase::WaitForPasswordStore(browser());
AssertNoInterstitial(browser());
// Navigate away to "flush" the metrics.
ASSERT_TRUE(
ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL)));
}
INSTANTIATE_TEST_SUITE_P(
SafeBrowsingBlockingPageWithDelayedWarningsBrowserTest,
SafeBrowsingBlockingPageDelayedWarningBrowserTest,
testing::Combine(
testing::Values(false, true), /* IsolateAllSitesForTesting */
testing::Values(false, true) /* Show warning on mouse click */));
// Test that SafeBrowsingBlockingPage properly decodes IDN URLs that are
// displayed.
class SafeBrowsingBlockingPageIDNTest
: public SecurityInterstitialIDNTest,
public testing::WithParamInterface<SBThreatType> {
protected:
// SecurityInterstitialIDNTest implementation
security_interstitials::SecurityInterstitialPage* CreateInterstitial(
content::WebContents* contents,
const GURL& request_url) const override {
SafeBrowsingUIManager::CreateAllowlistForTesting(contents);
SafeBrowsingService* sb_service =
g_browser_process->safe_browsing_service();
auto* primary_main_frame = contents->GetPrimaryMainFrame();
const content::GlobalRenderFrameHostId primary_main_frame_id =
primary_main_frame->GetGlobalId();
SafeBrowsingBlockingPage::UnsafeResource resource;
resource.url = request_url;
resource.is_subresource = false;
resource.threat_type = GetParam();
resource.render_process_id = primary_main_frame_id.child_id;
resource.render_frame_token = primary_main_frame->GetFrameToken().value();
resource.threat_source = safe_browsing::ThreatSource::LOCAL_PVER4;
auto* ui_manager = sb_service->ui_manager().get();
return ui_manager->CreateBlockingPage(
contents, request_url, {resource}, /*forward_extension_event=*/false,
/*blocked_page_shown_timestamp=*/std::nullopt);
}
};
// TODO(crbug.com/40666794): VerifyIDNDecoded does not work with committed
// interstitials, this test should be re-enabled once it is adapted.
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageIDNTest,
DISABLED_SafeBrowsingBlockingPageDecodesIDN) {
EXPECT_TRUE(VerifyIDNDecoded());
}
INSTANTIATE_TEST_SUITE_P(
SafeBrowsingBlockingPageIDNTestWithThreatType,
SafeBrowsingBlockingPageIDNTest,
testing::Values(SBThreatType::SB_THREAT_TYPE_URL_MALWARE,
SBThreatType::SB_THREAT_TYPE_URL_PHISHING,
SBThreatType::SB_THREAT_TYPE_URL_UNWANTED));
class SafeBrowsingBlockingPageEnhancedProtectionMessageTest
: public policy::PolicyTest {
public:
SafeBrowsingBlockingPageEnhancedProtectionMessageTest() = default;
SafeBrowsingBlockingPageEnhancedProtectionMessageTest(
const SafeBrowsingBlockingPageEnhancedProtectionMessageTest&) = delete;
SafeBrowsingBlockingPageEnhancedProtectionMessageTest& operator=(
const SafeBrowsingBlockingPageEnhancedProtectionMessageTest&) = delete;
void SetUp() override { InProcessBrowserTest::SetUp(); }
void SetUpOnMainThread() override {
host_resolver()->AddRule("*", "127.0.0.1");
content::SetupCrossSiteRedirector(embedded_test_server());
ASSERT_TRUE(embedded_test_server()->Start());
}
void CreatedBrowserMainParts(
content::BrowserMainParts* browser_main_parts) override {
policy::PolicyTest::CreatedBrowserMainParts(browser_main_parts);
// Test UI manager and test database manager should be set before
// the browser is started but after threads are created.
factory_.SetTestUIManager(new FakeSafeBrowsingUIManager(
std::make_unique<TestSafeBrowsingBlockingPageFactory>()));
factory_.SetTestDatabaseManager(new FakeSafeBrowsingDatabaseManager(
content::GetUIThreadTaskRunner({}),
content::GetIOThreadTaskRunner({})));
SafeBrowsingService::RegisterFactory(&factory_);
ThreatDetails::RegisterFactory(&details_factory_);
}
protected:
void SetupWarningAndNavigateToURL(GURL url, Browser* browser) {
TestSafeBrowsingService* service = factory_.test_safe_browsing_service();
ASSERT_TRUE(service);
static_cast<FakeSafeBrowsingDatabaseManager*>(
service->database_manager().get())
->AddDangerousUrl(url, SBThreatType::SB_THREAT_TYPE_URL_MALWARE);
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser, url));
EXPECT_TRUE(WaitForReady(browser));
}
// A test should call this function if it is expected to trigger a threat
// report.
void SetReportSentCallback(base::OnceClosure callback) {
static_cast<FakeSafeBrowsingUIManager*>(
factory_.test_safe_browsing_service()->ui_manager().get())
->set_threat_details_done_callback(std::move(callback));
}
private:
TestSafeBrowsingServiceFactory factory_;
TestThreatDetailsFactory details_factory_;
base::test::ScopedFeatureList scoped_feature_list_;
};
IN_PROC_BROWSER_TEST_F(SafeBrowsingBlockingPageEnhancedProtectionMessageTest,
VerifyEnhancedProtectionMessageShownAndClicked) {
safe_browsing::SetExtendedReportingPrefForTests(
browser()->profile()->GetPrefs(), true);
safe_browsing::SetSafeBrowsingState(
browser()->profile()->GetPrefs(),
safe_browsing::SafeBrowsingState::STANDARD_PROTECTION);
SetupWarningAndNavigateToURL(embedded_test_server()->GetURL("/empty.html"),
browser());
// Check SBER opt in is not shown.
EXPECT_EQ(HIDDEN, ::safe_browsing::GetVisibility(
browser(), "extended-reporting-opt-in"));
// Check enhanced protection message is shown.
EXPECT_EQ(VISIBLE, ::safe_browsing::GetVisibility(
browser(), "enhanced-protection-message"));
WebContents* interstitial_tab =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(interstitial_tab);
ASSERT_TRUE(IsShowingInterstitial(
browser()->tab_strip_model()->GetActiveWebContents()));
content::TestNavigationObserver nav_observer(nullptr);
nav_observer.StartWatchingNewWebContents();
// Click the enhanced protection link.
EXPECT_TRUE(Click(browser(), "enhanced-protection-link"));
nav_observer.Wait();
// There are two tabs open.
EXPECT_EQ(2, browser()->tab_strip_model()->count());
// The second tab is visible.
EXPECT_EQ(1, browser()->tab_strip_model()->active_index());
// Assert the interstitial is not present in the foreground tab.
ASSERT_FALSE(IsShowingInterstitial(
browser()->tab_strip_model()->GetActiveWebContents()));
// Foreground tab displays the setting page.
WebContents* new_tab = browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(new_tab);
EXPECT_EQ(GURL(kEnhancedProtectionUrl), new_tab->GetLastCommittedURL());
// Interstitial should still display in the background tab.
browser()->tab_strip_model()->ActivateTabAt(
0, TabStripUserGestureDetails(
TabStripUserGestureDetails::GestureType::kOther));
EXPECT_EQ(0, browser()->tab_strip_model()->active_index());
EXPECT_EQ(interstitial_tab,
browser()->tab_strip_model()->GetActiveWebContents());
EXPECT_TRUE(IsShowingInterstitial(
browser()->tab_strip_model()->GetActiveWebContents()));
// Set threat report sent runner, since a report will be sent when web
// contents are destroyed.
auto threat_report_sent_runner = std::make_unique<base::RunLoop>();
SetReportSentCallback(threat_report_sent_runner->QuitClosure());
}
IN_PROC_BROWSER_TEST_F(SafeBrowsingBlockingPageEnhancedProtectionMessageTest,
VerifyEnhancedProtectionMessageNotShownAlreadyInEp) {
safe_browsing::SetExtendedReportingPrefForTests(
browser()->profile()->GetPrefs(), true);
safe_browsing::SetSafeBrowsingState(
browser()->profile()->GetPrefs(),
safe_browsing::SafeBrowsingState::ENHANCED_PROTECTION);
SetupWarningAndNavigateToURL(embedded_test_server()->GetURL("/empty.html"),
browser());
auto threat_report_sent_runner = std::make_unique<base::RunLoop>();
SetReportSentCallback(threat_report_sent_runner->QuitClosure());
EXPECT_TRUE(IsShowingInterstitial(
browser()->tab_strip_model()->GetActiveWebContents()));
// Check enhanced protection message is not shown.
EXPECT_EQ(HIDDEN, ::safe_browsing::GetVisibility(
browser(), "enhanced-protection-message"));
}
IN_PROC_BROWSER_TEST_F(SafeBrowsingBlockingPageEnhancedProtectionMessageTest,
VerifyEnhancedProtectionMessageNotShownManaged) {
policy::PolicyMap policies;
policies.Set(policy::key::kSafeBrowsingProtectionLevel,
policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_USER,
policy::POLICY_SOURCE_CLOUD,
base::Value(/* standard protection */ 1), nullptr);
UpdateProviderPolicy(policies);
SetupWarningAndNavigateToURL(embedded_test_server()->GetURL("/empty.html"),
browser());
EXPECT_TRUE(IsShowingInterstitial(
browser()->tab_strip_model()->GetActiveWebContents()));
// Check enhanced protection message is not shown.
EXPECT_EQ(HIDDEN, ::safe_browsing::GetVisibility(
browser(), "enhanced-protection-message"));
}
class SafeBrowsingBlockingPageAsyncChecksTest
: public InProcessBrowserTest,
public testing::WithParamInterface<bool> {
public:
SafeBrowsingBlockingPageAsyncChecksTest() = default;
void SetUp() override {
bool is_async_check_enabled = GetParam();
if (is_async_check_enabled) {
feature_list_.InitAndEnableFeature(kSafeBrowsingAsyncRealTimeCheck);
} else {
feature_list_.InitAndDisableFeature(kSafeBrowsingAsyncRealTimeCheck);
}
InProcessBrowserTest::SetUp();
}
void SetUpOnMainThread() override {
host_resolver()->AddRule("*", "127.0.0.1");
content::SetupCrossSiteRedirector(embedded_test_server());
ASSERT_TRUE(embedded_test_server()->Start());
}
void CreatedBrowserMainParts(
content::BrowserMainParts* browser_main_parts) override {
InProcessBrowserTest::CreatedBrowserMainParts(browser_main_parts);
// Test UI manager and test database manager should be set before
// the browser is started but after threads are created.
factory_.SetTestUIManager(new FakeSafeBrowsingUIManager(
std::make_unique<TestSafeBrowsingBlockingPageFactory>()));
factory_.SetTestDatabaseManager(new FakeSafeBrowsingDatabaseManager(
content::GetUIThreadTaskRunner({}),
content::GetIOThreadTaskRunner({})));
SafeBrowsingService::RegisterFactory(&factory_);
}
protected:
void SetupUrlRealTimeVerdictInCacheManager(GURL url,
Profile* profile,
bool is_unsafe) {
safe_browsing::VerdictCacheManagerFactory::GetForProfile(profile)
->CacheArtificialRealTimeUrlVerdict(url.spec(), is_unsafe);
}
void SetUpEnterpriseUrlCheck() {
browser()->profile()->GetPrefs()->SetInteger(
prefs::kSafeBrowsingEnterpriseRealTimeUrlCheckMode,
REAL_TIME_CHECK_FOR_MAINFRAME_ENABLED);
browser()->profile()->GetPrefs()->SetInteger(
prefs::kSafeBrowsingEnterpriseRealTimeUrlCheckScope,
policy::POLICY_SCOPE_MACHINE);
SetDMTokenForTesting(policy::DMToken::CreateValidToken("dm_token"));
}
void NavigateToURLAndWaitForAsyncChecks(GURL url) {
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
SafeBrowsingBlockingPageTestHelper::MaybeWaitForAsyncChecksToComplete(
browser()->tab_strip_model()->GetActiveWebContents(),
factory_.test_safe_browsing_service()->ui_manager().get(),
/*wait_for_load_stop=*/true);
}
TestSafeBrowsingServiceFactory factory_;
private:
base::test::ScopedFeatureList feature_list_;
};
INSTANTIATE_TEST_SUITE_P(AsyncCheckEnabled,
SafeBrowsingBlockingPageAsyncChecksTest,
testing::Bool());
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageAsyncChecksTest,
EnterpriseRealTimeUrlCheck) {
base::HistogramTester histogram_tester;
safe_browsing::SetSafeBrowsingState(
browser()->profile()->GetPrefs(),
safe_browsing::SafeBrowsingState::STANDARD_PROTECTION);
SetUpEnterpriseUrlCheck();
GURL url = embedded_test_server()->GetURL(kEmptyPage);
SetupUrlRealTimeVerdictInCacheManager(url, browser()->profile(),
/*is_unsafe=*/false);
NavigateToURLAndWaitForAsyncChecks(url);
ASSERT_FALSE(IsShowingInterstitial(
browser()->tab_strip_model()->GetActiveWebContents()));
// Whether or not async checks are enabled, only a sync check is performed
// (the enterprise URT check).
histogram_tester.ExpectTotalCount(
"SafeBrowsing.BrowserThrottle.TotalDelay2.EnterpriseFullUrlLookup",
/*expected_count=*/1);
}
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageAsyncChecksTest,
ConsumerRealTimeUrlCheck) {
base::HistogramTester histogram_tester;
safe_browsing::SetSafeBrowsingState(
browser()->profile()->GetPrefs(),
safe_browsing::SafeBrowsingState::STANDARD_PROTECTION);
browser()->profile()->GetPrefs()->SetBoolean(
unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled, true);
GURL url = embedded_test_server()->GetURL(kEmptyPage);
SetupUrlRealTimeVerdictInCacheManager(url, browser()->profile(),
/*is_unsafe=*/false);
NavigateToURLAndWaitForAsyncChecks(url);
ASSERT_FALSE(IsShowingInterstitial(
browser()->tab_strip_model()->GetActiveWebContents()));
bool is_async_check_enabled = GetParam();
if (is_async_check_enabled) {
// When async checks are enabled, the sync check is an HPD check.
histogram_tester.ExpectTotalCount(
"SafeBrowsing.BrowserThrottle.TotalDelay2.HashPrefixDatabaseCheck",
/*expected_count=*/1);
} else {
// When async checks are disabled, only a sync check is performed, which is
// a consumer URT check.
histogram_tester.ExpectTotalCount(
"SafeBrowsing.BrowserThrottle.TotalDelay2.ConsumerFullUrlLookup",
/*expected_count=*/1);
}
}
class SafeBrowsingBlockingPageAsyncChecksTimingTest
: public SafeBrowsingBlockingPageAsyncChecksTest {
public:
SafeBrowsingBlockingPageAsyncChecksTimingTest() = default;
void SetUp() override {
feature_list_.InitWithFeatures(
{kSafeBrowsingAsyncRealTimeCheck,
kCreateWarningShownClientSafeBrowsingReports},
{kRedWarningSurvey});
InProcessBrowserTest::SetUp();
}
void TearDown() override {
RealTimeUrlLookupServiceFactory::GetInstance()
->SetURLLoaderFactoryForTesting(nullptr);
InProcessBrowserTest::TearDown();
ThreatDetails::RegisterFactory(nullptr);
}
void CreatedBrowserMainParts(
content::BrowserMainParts* browser_main_parts) override {
SafeBrowsingBlockingPageAsyncChecksTest::CreatedBrowserMainParts(
browser_main_parts);
ThreatDetails::RegisterFactory(&details_factory_);
}
protected:
// Helper struct for test cases.
struct UrlAndIsUnsafe {
std::string relative_url;
bool is_unsafe;
};
void SetURLLoaderFactoryForTesting() {
auto ref_counted_url_loader_factory =
base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
&test_url_loader_factory_);
RealTimeUrlLookupServiceFactory::GetInstance()
->SetURLLoaderFactoryForTesting(ref_counted_url_loader_factory);
}
void EnableAsyncCheck() {
SetURLLoaderFactoryForTesting();
// Enable enhanced protection which enables real-time URL check which is
// conducted asynchronously.
safe_browsing::SetSafeBrowsingState(
browser()->profile()->GetPrefs(),
safe_browsing::SafeBrowsingState::ENHANCED_PROTECTION);
}
void SetURLThreatType(const GURL& url, SBThreatType threat_type) {
TestSafeBrowsingService* service = factory_.test_safe_browsing_service();
ASSERT_TRUE(service);
static_cast<FakeSafeBrowsingDatabaseManager*>(
service->database_manager().get())
->AddDangerousUrl(url, threat_type);
}
void ReturnUrlRealTimeVerdictInUrlLoader(GURL url, bool is_unsafe) {
constexpr char kRealTimeLookupUrl[] =
"https://safebrowsing.google.com/safebrowsing/clientreport/realtime";
RTLookupResponse response;
RTLookupResponse::ThreatInfo* new_threat_info = response.add_threat_info();
RTLookupResponse::ThreatInfo threat_info;
if (is_unsafe) {
threat_info.set_verdict_type(RTLookupResponse::ThreatInfo::DANGEROUS);
threat_info.set_threat_type(
RTLookupResponse::ThreatInfo::SOCIAL_ENGINEERING);
} else {
threat_info.set_verdict_type(RTLookupResponse::ThreatInfo::SAFE);
}
threat_info.set_cache_duration_sec(60);
threat_info.set_cache_expression_using_match_type(url.host());
threat_info.set_cache_expression_match_type(
RTLookupResponse::ThreatInfo::COVERING_MATCH);
*new_threat_info = threat_info;
std::string expected_response_str;
response.SerializeToString(&expected_response_str);
test_url_loader_factory_.AddResponse(kRealTimeLookupUrl,
expected_response_str);
}
// The following events happen in sequence:
// 1. WillProcessResponse is called.
// 2. Safe Browsing checks complete.
// 3. Navigation finished.
GURL SetupWarningShownBetweenProcessResponseAndFinishNavigationAndNavigate(
std::vector<UrlAndIsUnsafe> url_and_server_redirects) {
CHECK(!url_and_server_redirects.empty());
GURL original_url = embedded_test_server()->GetURL(
url_and_server_redirects.front().relative_url);
GURL final_url = embedded_test_server()->GetURL(
url_and_server_redirects.back().relative_url);
content::TestNavigationManager navigation_manager(
browser()->tab_strip_model()->GetActiveWebContents(), original_url);
ui_test_utils::NavigateToURLWithDisposition(
browser(), original_url, WindowOpenDisposition::CURRENT_TAB,
ui_test_utils::BROWSER_TEST_NO_WAIT);
EXPECT_TRUE(navigation_manager.WaitForResponse());
// At this point, WillProcessResponse is called so the async checker is
// transferred to AsyncCheckTracker.
AsyncCheckTracker* tracker =
safe_browsing::AsyncCheckTracker::GetOrCreateForWebContents(
browser()->tab_strip_model()->GetActiveWebContents(),
factory_.test_safe_browsing_service()->ui_manager().get());
EXPECT_EQ(tracker->PendingCheckersSizeForTesting(), 1u);
for (const auto& url_and_server_redirect : url_and_server_redirects) {
GURL url =
embedded_test_server()->GetURL(url_and_server_redirect.relative_url);
ReturnUrlRealTimeVerdictInUrlLoader(url,
url_and_server_redirect.is_unsafe);
}
SafeBrowsingBlockingPageTestHelper::MaybeWaitForAsyncChecksToComplete(
browser()->tab_strip_model()->GetActiveWebContents(),
factory_.test_safe_browsing_service()->ui_manager().get(),
/*wait_for_load_stop=*/false);
// At this point, the async check is completed, but the navigation has not
// yet finished.
navigation_manager.ResumeNavigation();
EXPECT_TRUE(navigation_manager.WaitForNavigationFinished());
// After the navigation is finished, we need to wait for the navigation of
// the interstitial to complete.
content::TestNavigationManager interstitial_navigation_manager(
browser()->tab_strip_model()->GetActiveWebContents(), final_url);
EXPECT_TRUE(interstitial_navigation_manager.WaitForNavigationFinished());
content::WaitForLoadStop(
browser()->tab_strip_model()->GetActiveWebContents());
EXPECT_TRUE(IsShowingInterstitial(
browser()->tab_strip_model()->GetActiveWebContents()));
// Reset dangerous response so future URLs are not accidentally flagged
// by real-time URL check.
test_url_loader_factory_.ClearResponses();
return final_url;
}
// The following events happen in sequence:
// 1. Navigation finished.
// 2. Safe Browsing checks complete.
GURL SetupWarningShownAfterFinishNavigationAndNavigate(
std::vector<UrlAndIsUnsafe> url_and_server_redirects) {
CHECK(!url_and_server_redirects.empty());
GURL original_url = embedded_test_server()->GetURL(
url_and_server_redirects.front().relative_url);
GURL final_url = embedded_test_server()->GetURL(
url_and_server_redirects.back().relative_url);
content::TestNavigationManager navigation_manager(
browser()->tab_strip_model()->GetActiveWebContents(), original_url);
ui_test_utils::NavigateToURLWithDisposition(
browser(), original_url, WindowOpenDisposition::CURRENT_TAB,
ui_test_utils::BROWSER_TEST_NO_WAIT);
EXPECT_TRUE(navigation_manager.WaitForNavigationFinished());
// At this point, the navigation has finished but the async check has not
// yet completed.
AsyncCheckTracker* tracker =
safe_browsing::AsyncCheckTracker::GetOrCreateForWebContents(
browser()->tab_strip_model()->GetActiveWebContents(),
factory_.test_safe_browsing_service()->ui_manager().get());
EXPECT_EQ(tracker->PendingCheckersSizeForTesting(), 1u);
for (const auto& url_and_server_redirect : url_and_server_redirects) {
GURL url =
embedded_test_server()->GetURL(url_and_server_redirect.relative_url);
ReturnUrlRealTimeVerdictInUrlLoader(url,
url_and_server_redirect.is_unsafe);
}
SafeBrowsingBlockingPageTestHelper::MaybeWaitForAsyncChecksToComplete(
browser()->tab_strip_model()->GetActiveWebContents(),
factory_.test_safe_browsing_service()->ui_manager().get(),
/*wait_for_load_stop=*/true);
EXPECT_TRUE(IsShowingInterstitial(
browser()->tab_strip_model()->GetActiveWebContents()));
// Reset dangerous response so future URLs are not accidentally
// flagged by real-time URL check.
test_url_loader_factory_.ClearResponses();
return final_url;
}
GURL SetupPostCommitInterstitialAndNavigate(
std::vector<UrlAndIsUnsafe> url_and_server_redirects,
base::OnceClosure report_sent_callback) {
// Call SetupUrlRealTimeVerdictInCacheManager with a random URL to ensure
// RealTimeUrlLookupServiceBase::CanCheckUrl returns true so the real time
// check is performed.
SetupUrlRealTimeVerdictInCacheManager(
GURL("https://random.url"), browser()->profile(), /*is_unsafe=*/false);
SetReportSentCallback(std::move(report_sent_callback));
bool check_complete_after_navigation_finish = GetParam();
if (check_complete_after_navigation_finish) {
return SetupWarningShownAfterFinishNavigationAndNavigate(
url_and_server_redirects);
} else {
return SetupWarningShownBetweenProcessResponseAndFinishNavigationAndNavigate(
url_and_server_redirects);
}
}
void SetReportSentCallback(base::OnceClosure callback) {
static_cast<FakeSafeBrowsingUIManager*>(
factory_.test_safe_browsing_service()->ui_manager().get())
->set_threat_details_done_callback(std::move(callback));
}
std::string GetReportSent() {
return static_cast<FakeSafeBrowsingUIManager*>(
factory_.test_safe_browsing_service()->ui_manager().get())
->GetReport();
}
std::optional<bool> shown_report_sent_is_async_check() {
return static_cast<FakeSafeBrowsingUIManager*>(
factory_.test_safe_browsing_service()->ui_manager().get())
->report_sent_is_async_check();
}
base::HistogramTester histogram_tester_;
TestThreatDetailsFactory details_factory_;
private:
network::TestURLLoaderFactory test_url_loader_factory_;
base::test::ScopedFeatureList feature_list_;
};
INSTANTIATE_TEST_SUITE_P(CheckCompleteAfterNavigationFinish,
SafeBrowsingBlockingPageAsyncChecksTimingTest,
testing::Bool());
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageAsyncChecksTimingTest,
VerifyHistogramsAndHitReport) {
EnableAsyncCheck();
auto threat_report_sent_runner = std::make_unique<base::RunLoop>();
GURL url = SetupPostCommitInterstitialAndNavigate(
{{kMaliciousPage, /* is_unsafe */ true}},
threat_report_sent_runner->QuitClosure());
int hit_report_count =
static_cast<FakeSafeBrowsingUIManager*>(
factory_.test_safe_browsing_service()->ui_manager().get())
->hit_report_count();
EXPECT_EQ(hit_report_count, 1);
EXPECT_TRUE(shown_report_sent_is_async_check().value());
histogram_tester_.ExpectUniqueSample(
"interstitial.phishing.decision.after_page_shown",
/*sample=*/security_interstitials::MetricsHelper::SHOW,
/*expected_bucket_count=*/1);
}
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageAsyncChecksTimingTest,
PostCommitInterstitialDontProceed) {
EnableAsyncCheck();
auto threat_report_sent_runner = std::make_unique<base::RunLoop>();
SetupPostCommitInterstitialAndNavigate(
{{kMaliciousPage, /* is_unsafe */ true}},
threat_report_sent_runner->QuitClosure());
EXPECT_EQ(VISIBLE, GetVisibility(browser(), "primary-button"));
EXPECT_EQ(HIDDEN, GetVisibility(browser(), "details"));
EXPECT_EQ(HIDDEN, GetVisibility(browser(), "proceed-link"));
EXPECT_EQ(HIDDEN, GetVisibility(browser(), "error-code"));
EXPECT_TRUE(Click(browser(), "details-button"));
EXPECT_EQ(VISIBLE, GetVisibility(browser(), "details"));
EXPECT_EQ(VISIBLE, GetVisibility(browser(), "proceed-link"));
EXPECT_EQ(HIDDEN, GetVisibility(browser(), "error-code"));
EXPECT_TRUE(ClickAndWaitForDetach(browser(), "primary-button"));
AssertNoInterstitial(browser()); // Assert the interstitial is gone
EXPECT_EQ(GURL(url::kAboutBlankURL), // Back to "about:blank"
browser()
->tab_strip_model()
->GetActiveWebContents()
->GetLastCommittedURL());
}
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageAsyncChecksTimingTest,
PostCommitInterstitialProceed) {
EnableAsyncCheck();
auto threat_report_sent_runner = std::make_unique<base::RunLoop>();
GURL url = SetupPostCommitInterstitialAndNavigate(
{{kMaliciousPage, /* is_unsafe */ true}},
threat_report_sent_runner->QuitClosure());
EXPECT_TRUE(ClickAndWaitForDetach(browser(), "proceed-link"));
AssertNoInterstitial(browser()); // Assert the interstitial is gone
EXPECT_EQ(url, browser()
->tab_strip_model()
->GetActiveWebContents()
->GetLastCommittedURL());
}
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageAsyncChecksTimingTest,
PostCommitInterstitialServerRedirect_OriginIsUnsafe) {
EnableAsyncCheck();
auto threat_report_sent_runner = std::make_unique<base::RunLoop>();
GURL url = SetupPostCommitInterstitialAndNavigate(
{{kRedirectToMalware, /* is_unsafe */ true},
{kMaliciousPage, /* is_unsafe */ false}},
threat_report_sent_runner->QuitClosure());
EXPECT_TRUE(ClickAndWaitForDetach(browser(), "proceed-link"));
AssertNoInterstitial(browser()); // Assert the interstitial is gone
EXPECT_EQ(url, browser()
->tab_strip_model()
->GetActiveWebContents()
->GetLastCommittedURL());
}
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageAsyncChecksTimingTest,
PostCommitInterstitialServerRedirect_RedirectIsUnsafe) {
EnableAsyncCheck();
auto threat_report_sent_runner = std::make_unique<base::RunLoop>();
GURL url = SetupPostCommitInterstitialAndNavigate(
{{kRedirectToMalware, /* is_unsafe */ false},
{kMaliciousPage, /* is_unsafe */ true}},
threat_report_sent_runner->QuitClosure());
EXPECT_TRUE(ClickAndWaitForDetach(browser(), "proceed-link"));
AssertNoInterstitial(browser()); // Assert the interstitial is gone
EXPECT_EQ(url, browser()
->tab_strip_model()
->GetActiveWebContents()
->GetLastCommittedURL());
}
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageAsyncChecksTimingTest,
PostCommitInterstitialReportThreatDetails) {
EnableAsyncCheck();
auto threat_report_sent_runner = std::make_unique<base::RunLoop>();
GURL url = SetupPostCommitInterstitialAndNavigate(
{{kMaliciousPage, /* is_unsafe */ true}},
threat_report_sent_runner->QuitClosure());
ThreatDetails* threat_details = details_factory_.get_details();
EXPECT_TRUE(threat_details != nullptr);
EXPECT_TRUE(ClickAndWaitForDetach(browser(), "proceed-link"));
AssertNoInterstitial(browser()); // Assert the interstitial is gone
EXPECT_EQ(url, browser()
->tab_strip_model()
->GetActiveWebContents()
->GetLastCommittedURL());
threat_report_sent_runner->Run();
std::string serialized = GetReportSent();
ClientSafeBrowsingReportRequest report;
ASSERT_TRUE(report.ParseFromString(serialized));
// Verify the report is complete.
EXPECT_TRUE(report.complete());
// Do some basic verification of report contents.
EXPECT_EQ(url.spec(), report.page_url());
EXPECT_EQ(url.spec(), report.url());
// We don't check the specific size of resources here. The size can be either
// 1 or 2 depending on whether DOM details have been collected when we
// proceed.
ASSERT_NE(0, report.resources_size());
}
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageAsyncChecksTimingTest,
PostCommitInterstitialAllowlistRevisit) {
EnableAsyncCheck();
auto threat_report_sent_runner = std::make_unique<base::RunLoop>();
GURL url = SetupPostCommitInterstitialAndNavigate(
{{kMaliciousPage, /* is_unsafe */ true}},
threat_report_sent_runner->QuitClosure());
EXPECT_TRUE(ClickAndWaitForDetach(browser(), "proceed-link"));
AssertNoInterstitial(browser()); // Assert the interstitial is gone.
EXPECT_EQ(url, browser()
->tab_strip_model()
->GetActiveWebContents()
->GetLastCommittedURL());
// Navigate to an unrelated page and revisit the allowlisted URL.
SetupUrlRealTimeVerdictInCacheManager(GURL(kUnrelatedUrl),
browser()->profile(),
/*is_unsafe=*/false);
NavigateToURLAndWaitForAsyncChecks(GURL(kUnrelatedUrl));
AssertNoInterstitial(browser());
// The allowlisted page should remain allowlisted.
NavigateToURLAndWaitForAsyncChecks(url);
AssertNoInterstitial(browser());
}
// Test that the security indicator gets updated on a Safe Browsing
// interstitial triggered post commit. Regression test for
// https://crbug.com/659713.
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageAsyncChecksTimingTest,
SecurityStateDowngradedForPostCommitInterstitial) {
EnableAsyncCheck();
WebContents* error_tab = browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(error_tab);
SecurityStyleTestObserver observer(error_tab);
auto threat_report_sent_runner = std::make_unique<base::RunLoop>();
GURL url = SetupPostCommitInterstitialAndNavigate(
{{kMaliciousPage, /* is_unsafe */ true}},
threat_report_sent_runner->QuitClosure());
// The security indicator should be downgraded while the interstitial shows.
ExpectSecurityIndicatorDowngrade(error_tab, 0u);
EXPECT_EQ(security_state::SecurityLevel::DANGEROUS,
observer.latest_security_level());
// The security indicator should still be downgraded post-interstitial.
EXPECT_TRUE(ClickAndWaitForDetach(browser(), "proceed-link"));
AssertNoInterstitial(browser());
WebContents* post_tab = browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(post_tab);
ExpectSecurityIndicatorDowngrade(post_tab, 0u);
}
// Test that the security indicator does not stay downgraded after
// clicking back from a Safe Browsing interstitial triggered post commit.
// Regression test for https://crbug.com/659709.
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageAsyncChecksTimingTest,
SecurityStateGoBackOnPostCommitInterstitial) {
EnableAsyncCheck();
// Navigate to a page so that there is somewhere to go back to.
GURL start_url = embedded_test_server()->GetURL(kEmptyPage);
NavigateToURLAndWaitForAsyncChecks(start_url);
// The security indicator should be downgraded while the interstitial
// shows.
auto threat_report_sent_runner = std::make_unique<base::RunLoop>();
GURL main_url = SetupPostCommitInterstitialAndNavigate(
{{kMaliciousPage, /* is_unsafe */ true}},
threat_report_sent_runner->QuitClosure());
WebContents* error_tab = browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(error_tab);
ExpectSecurityIndicatorDowngrade(error_tab, 0u);
// Go back.
EXPECT_EQ(VISIBLE, GetVisibility(browser(), "primary-button"));
EXPECT_EQ(HIDDEN, GetVisibility(browser(), "details"));
EXPECT_EQ(HIDDEN, GetVisibility(browser(), "proceed-link"));
EXPECT_EQ(HIDDEN, GetVisibility(browser(), "error-code"));
EXPECT_TRUE(Click(browser(), "details-button"));
EXPECT_EQ(VISIBLE, GetVisibility(browser(), "details"));
EXPECT_EQ(VISIBLE, GetVisibility(browser(), "proceed-link"));
EXPECT_EQ(HIDDEN, GetVisibility(browser(), "error-code"));
EXPECT_TRUE(ClickAndWaitForDetach(browser(), "primary-button"));
// The security indicator should *not* still be downgraded after going back.
AssertNoInterstitial(browser());
WebContents* post_tab = browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(post_tab);
content::NavigationEntry* entry = post_tab->GetController().GetVisibleEntry();
ASSERT_TRUE(entry);
EXPECT_EQ(start_url, entry->GetURL());
ExpectNoSecurityIndicatorDowngrade(post_tab);
}
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageAsyncChecksTimingTest,
SecurityStateGoBackFlaggedByBothChecks) {
EnableAsyncCheck();
// Navigate to a page so that there is somewhere to go back to.
GURL start_url = embedded_test_server()->GetURL(kEmptyPage);
NavigateToURLAndWaitForAsyncChecks(start_url);
auto threat_report_sent_runner = std::make_unique<base::RunLoop>();
SetReportSentCallback(threat_report_sent_runner->QuitClosure());
GURL url = embedded_test_server()->GetURL(kMaliciousPage);
// Mark the URL as dangerous for both checks.
SetupUrlRealTimeVerdictInCacheManager(url, browser()->profile(),
/*is_unsafe=*/true);
SetURLThreatType(url, SBThreatType::SB_THREAT_TYPE_URL_PHISHING);
NavigateToURLAndWaitForAsyncChecks(url);
// The security indicator should be downgraded while the interstitial
// shows.
WebContents* error_tab = browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(error_tab);
ExpectSecurityIndicatorDowngrade(error_tab, 0u);
// Go back.
EXPECT_TRUE(ClickAndWaitForDetach(browser(), "primary-button"));
// The security indicator should *not* still be downgraded after going back.
AssertNoInterstitial(browser());
WebContents* post_tab = browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(post_tab);
content::NavigationEntry* entry = post_tab->GetController().GetVisibleEntry();
ASSERT_TRUE(entry);
EXPECT_EQ(start_url, entry->GetURL());
ExpectNoSecurityIndicatorDowngrade(post_tab);
}
// Tests that commands work in a post commit interstitial if a pre commit
// interstitial has been shown previously on the same webcontents. Regression
// test for crbug.com/1021334
#if BUILDFLAG(IS_LINUX) && defined(MEMORY_SANITIZER)
// TODO(crbug.com/325491320): re-enable test
#define MAYBE_PostCommitInterstitialProceedAfterPreCommitInterstitial \
DISABLED_PostCommitInterstitialProceedAfterPreCommitInterstitial
#else
#define MAYBE_PostCommitInterstitialProceedAfterPreCommitInterstitial \
PostCommitInterstitialProceedAfterPreCommitInterstitial
#endif
IN_PROC_BROWSER_TEST_P(
SafeBrowsingBlockingPageAsyncChecksTimingTest,
MAYBE_PostCommitInterstitialProceedAfterPreCommitInterstitial) {
EnableAsyncCheck();
// Trigger a pre commit interstitial and go back.
GURL start_url = embedded_test_server()->GetURL(kEmptyPage);
SetURLThreatType(start_url, SBThreatType::SB_THREAT_TYPE_URL_PHISHING);
NavigateToURLAndWaitForAsyncChecks(start_url);
EXPECT_TRUE(ClickAndWaitForDetach(browser(), "primary-button"));
AssertNoInterstitial(browser());
// Trigger a post commit interstitial.
auto threat_report_sent_runner = std::make_unique<base::RunLoop>();
GURL main_url = SetupPostCommitInterstitialAndNavigate(
{{kMaliciousPage, /* is_unsafe */ true}},
threat_report_sent_runner->QuitClosure());
// Commands should work.
EXPECT_TRUE(ClickAndWaitForDetach(browser(), "proceed-link"));
AssertNoInterstitial(browser()); // Assert the interstitial is gone
EXPECT_EQ(main_url, browser()
->tab_strip_model()
->GetActiveWebContents()
->GetLastCommittedURL());
}
// Tests for real time URL check. To test it without making network requests to
// Safe Browsing servers, store an unsafe verdict in cache for the URL.
class SafeBrowsingBlockingPageRealTimeUrlCheckTest
: public InProcessBrowserTest,
public testing::WithParamInterface<bool> {
public:
SafeBrowsingBlockingPageRealTimeUrlCheckTest() = default;
SafeBrowsingBlockingPageRealTimeUrlCheckTest(
const SafeBrowsingBlockingPageRealTimeUrlCheckTest&) = delete;
SafeBrowsingBlockingPageRealTimeUrlCheckTest& operator=(
const SafeBrowsingBlockingPageRealTimeUrlCheckTest&) = delete;
void SetUp() override {
std::vector<base::test::FeatureRef> enabled_features = {};
std::vector<base::test::FeatureRef> disabled_features = {kDelayedWarnings};
if (GetParam()) {
enabled_features.push_back(kSafeBrowsingAsyncRealTimeCheck);
} else {
disabled_features.push_back(kSafeBrowsingAsyncRealTimeCheck);
}
scoped_feature_list_.InitWithFeatures(enabled_features, disabled_features);
InProcessBrowserTest::SetUp();
}
void SetUpOnMainThread() override {
host_resolver()->AddRule("*", "127.0.0.1");
content::SetupCrossSiteRedirector(embedded_test_server());
ASSERT_TRUE(embedded_test_server()->Start());
}
void CreatedBrowserMainParts(
content::BrowserMainParts* browser_main_parts) override {
InProcessBrowserTest::CreatedBrowserMainParts(browser_main_parts);
// Test UI manager and test database manager should be set before
// the browser is started but after threads are created.
factory_.SetTestUIManager(new FakeSafeBrowsingUIManager(
std::make_unique<TestSafeBrowsingBlockingPageFactory>()));
factory_.SetTestDatabaseManager(new FakeSafeBrowsingDatabaseManager(
content::GetUIThreadTaskRunner({}),
content::GetIOThreadTaskRunner({})));
SafeBrowsingService::RegisterFactory(&factory_);
}
protected:
void SetupUnsafeVerdict(GURL url, Profile* profile) {
auto* command_line = base::CommandLine::ForCurrentProcess();
command_line->AppendSwitchASCII(
"mark_as_real_time_phishing",
embedded_test_server()->GetURL("/empty.html").spec());
safe_browsing::VerdictCacheManagerFactory::GetForProfile(profile)
->CacheArtificialUnsafeRealTimeUrlVerdictFromSwitch();
}
void NavigateToURL(GURL url) {
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
SafeBrowsingBlockingPageTestHelper::MaybeWaitForAsyncChecksToComplete(
browser()->tab_strip_model()->GetActiveWebContents(),
factory_.test_safe_browsing_service()->ui_manager().get(),
/*wait_for_load_stop=*/true);
}
void SetReportSentCallback(base::OnceClosure callback) {
static_cast<FakeSafeBrowsingUIManager*>(
factory_.test_safe_browsing_service()->ui_manager().get())
->set_threat_details_done_callback(std::move(callback));
}
TestSafeBrowsingServiceFactory factory_;
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
INSTANTIATE_TEST_SUITE_P(AsyncCheckEnabled,
SafeBrowsingBlockingPageRealTimeUrlCheckTest,
testing::Bool());
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageRealTimeUrlCheckTest,
WarningShown_EnhancedProtectionEnabled) {
safe_browsing::SetSafeBrowsingState(
browser()->profile()->GetPrefs(),
safe_browsing::SafeBrowsingState::ENHANCED_PROTECTION);
GURL url = embedded_test_server()->GetURL("/empty.html");
SetupUnsafeVerdict(url, browser()->profile());
auto threat_report_sent_runner = std::make_unique<base::RunLoop>();
SetReportSentCallback(threat_report_sent_runner->QuitClosure());
NavigateToURL(url);
ASSERT_TRUE(IsShowingInterstitial(
browser()->tab_strip_model()->GetActiveWebContents()));
}
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageRealTimeUrlCheckTest,
WarningShown_MbbEnabled) {
safe_browsing::SetSafeBrowsingState(
browser()->profile()->GetPrefs(),
safe_browsing::SafeBrowsingState::STANDARD_PROTECTION);
browser()->profile()->GetPrefs()->SetBoolean(
unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled, true);
GURL url = embedded_test_server()->GetURL("/empty.html");
SetupUnsafeVerdict(url, browser()->profile());
NavigateToURL(url);
ASSERT_TRUE(IsShowingInterstitial(
browser()->tab_strip_model()->GetActiveWebContents()));
}
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageRealTimeUrlCheckTest,
WarningNotShown_MbbDisabled) {
safe_browsing::SetSafeBrowsingState(
browser()->profile()->GetPrefs(),
safe_browsing::SafeBrowsingState::STANDARD_PROTECTION);
browser()->profile()->GetPrefs()->SetBoolean(
unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled, false);
GURL url = embedded_test_server()->GetURL("/empty.html");
SetupUnsafeVerdict(url, browser()->profile());
NavigateToURL(url);
ASSERT_FALSE(IsShowingInterstitial(
browser()->tab_strip_model()->GetActiveWebContents()));
}
// Tests for hash-prefix real-time check. To avoid redundant testing of the
// HashRealTimeService, this populates the local cache instead of mocking
// network requests.
class SafeBrowsingBlockingPageHashRealTimeCheckTest
: public InProcessBrowserTest,
public testing::WithParamInterface<bool> {
public:
SafeBrowsingBlockingPageHashRealTimeCheckTest() = default;
SafeBrowsingBlockingPageHashRealTimeCheckTest(
const SafeBrowsingBlockingPageHashRealTimeCheckTest&) = delete;
SafeBrowsingBlockingPageHashRealTimeCheckTest& operator=(
const SafeBrowsingBlockingPageHashRealTimeCheckTest&) = delete;
void SetUp() override {
InitFeatures();
InProcessBrowserTest::SetUp();
}
void SetUpOnMainThread() override {
host_resolver()->AddRule("*", "127.0.0.1");
content::SetupCrossSiteRedirector(embedded_test_server());
ASSERT_TRUE(embedded_test_server()->Start());
}
void CreatedBrowserMainParts(
content::BrowserMainParts* browser_main_parts) override {
InProcessBrowserTest::CreatedBrowserMainParts(browser_main_parts);
// Test UI manager and test database manager should be set before
// the browser is started but after threads are created.
factory_.SetTestUIManager(new FakeSafeBrowsingUIManager(
std::make_unique<TestSafeBrowsingBlockingPageFactory>()));
factory_.SetTestDatabaseManager(new FakeSafeBrowsingDatabaseManager(
content::GetUIThreadTaskRunner({}),
content::GetIOThreadTaskRunner({})));
SafeBrowsingService::RegisterFactory(&factory_);
}
protected:
virtual void InitFeatures() {
std::vector<base::test::FeatureRef> enabled_features = {
kHashPrefixRealTimeLookups};
std::vector<base::test::FeatureRef> disabled_features = {};
if (GetParam()) {
enabled_features.push_back(kSafeBrowsingAsyncRealTimeCheck);
} else {
disabled_features.push_back(kSafeBrowsingAsyncRealTimeCheck);
}
scoped_feature_list_.InitWithFeatures(enabled_features, disabled_features);
}
void SetUpVerdict(GURL url, Profile* profile, bool is_unsafe) {
safe_browsing::VerdictCacheManagerFactory::GetForProfile(profile)
->CacheArtificialHashRealTimeLookupVerdict(url.spec(), is_unsafe);
}
void SetUpAndNavigateToUrl(bool is_unsafe) {
GURL url = embedded_test_server()->GetURL("/empty.html");
SetUpVerdict(url, browser()->profile(), is_unsafe);
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
SafeBrowsingBlockingPageTestHelper::MaybeWaitForAsyncChecksToComplete(
browser()->tab_strip_model()->GetActiveWebContents(),
factory_.test_safe_browsing_service()->ui_manager().get(),
/*wait_for_load_stop=*/true);
}
bool IsShowingInterstitial() {
return ::safe_browsing::IsShowingInterstitial(
browser()->tab_strip_model()->GetActiveWebContents());
}
std::optional<ThreatSource> hit_report_sent_threat_source() {
return static_cast<FakeSafeBrowsingUIManager*>(
factory_.test_safe_browsing_service()->ui_manager().get())
->hit_report_sent_threat_source();
}
std::string GetReportSent() {
return static_cast<FakeSafeBrowsingUIManager*>(
factory_.test_safe_browsing_service()->ui_manager().get())
->GetReport();
}
void SetReportSentCallback(base::OnceClosure callback) {
static_cast<FakeSafeBrowsingUIManager*>(
factory_.test_safe_browsing_service()->ui_manager().get())
->set_threat_details_done_callback(std::move(callback));
}
base::test::ScopedFeatureList scoped_feature_list_;
TestSafeBrowsingServiceFactory factory_;
private:
hash_realtime_utils::GoogleChromeBrandingPretenderForTesting apply_branding_;
};
class SafeBrowsingBlockingPageHashRealTimeCheckFeatureOffTest
: public SafeBrowsingBlockingPageHashRealTimeCheckTest {
protected:
void InitFeatures() override {
std::vector<base::test::FeatureRef> enabled_features = {};
std::vector<base::test::FeatureRef> disabled_features = {
kHashPrefixRealTimeLookups};
if (GetParam()) {
enabled_features.push_back(kSafeBrowsingAsyncRealTimeCheck);
} else {
disabled_features.push_back(kSafeBrowsingAsyncRealTimeCheck);
}
scoped_feature_list_.InitWithFeatures(enabled_features, disabled_features);
}
};
INSTANTIATE_TEST_SUITE_P(AsyncCheckEnabled,
SafeBrowsingBlockingPageHashRealTimeCheckTest,
testing::Bool());
INSTANTIATE_TEST_SUITE_P(
AsyncCheckEnabled,
SafeBrowsingBlockingPageHashRealTimeCheckFeatureOffTest,
testing::Bool());
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageHashRealTimeCheckTest,
ShowWarning) {
base::HistogramTester histogram_tester;
SetUpAndNavigateToUrl(/*is_unsafe=*/true);
ASSERT_TRUE(IsShowingInterstitial());
// The TotalDelay2 metric is logged for whichever check is run sync. When
// async checks are disabled, that's the hash-prefix real-time check. When
// they are enabled, it's the hash-prefix database check, since the
// hash-prefix real-time check is run async.
bool is_using_async_checks = GetParam();
histogram_tester.ExpectTotalCount(
"SafeBrowsing.BrowserThrottle.TotalDelay2.HashPrefixRealTimeCheck",
/*expected_count=*/is_using_async_checks ? 0 : 1);
histogram_tester.ExpectTotalCount(
"SafeBrowsing.BrowserThrottle.TotalDelay2.HashPrefixDatabaseCheck",
/*expected_count=*/is_using_async_checks ? 1 : 0);
histogram_tester.ExpectUniqueSample(
"SafeBrowsing.HPRT.Ineligible.IneligibleForSessionOrLocation",
/*sample=*/false,
/*expected_bucket_count=*/1);
histogram_tester.ExpectTotalCount(
"interstitial.phishing.decision.from_hash_prefix_real_time_check_v5",
/*expected_count=*/1);
}
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageHashRealTimeCheckTest,
DontShowWarning_PageIsSafe) {
base::HistogramTester histogram_tester;
SetUpAndNavigateToUrl(/*is_unsafe=*/false);
ASSERT_FALSE(IsShowingInterstitial());
// The TotalDelay2 metric is logged for whichever check is run sync. When
// async checks are disabled, that's the hash-prefix real-time check. When
// they are enabled, it's the hash-prefix database check, since the
// hash-prefix real-time check is run async.
bool is_using_async_checks = GetParam();
histogram_tester.ExpectTotalCount(
"SafeBrowsing.BrowserThrottle.TotalDelay2.HashPrefixRealTimeCheck",
/*expected_count=*/is_using_async_checks ? 0 : 1);
histogram_tester.ExpectTotalCount(
"SafeBrowsing.BrowserThrottle.TotalDelay2.HashPrefixDatabaseCheck",
/*expected_count=*/is_using_async_checks ? 1 : 0);
histogram_tester.ExpectUniqueSample(
"SafeBrowsing.HPRT.Ineligible.IneligibleForSessionOrLocation",
/*sample=*/false,
/*expected_bucket_count=*/1);
histogram_tester.ExpectTotalCount(
"interstitial.phishing.decision.from_hash_prefix_real_time_check_v5",
/*expected_count=*/0);
}
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageHashRealTimeCheckFeatureOffTest,
DontShowWarning_FeatureIsOff) {
base::HistogramTester histogram_tester;
SetUpAndNavigateToUrl(/*is_unsafe=*/true);
ASSERT_FALSE(IsShowingInterstitial());
histogram_tester.ExpectTotalCount(
"SafeBrowsing.BrowserThrottle.TotalDelay2.HashPrefixRealTimeCheck",
/*expected_count=*/0);
histogram_tester.ExpectTotalCount(
"SafeBrowsing.BrowserThrottle.TotalDelay2.HashPrefixDatabaseCheck",
/*expected_count=*/1);
histogram_tester.ExpectUniqueSample(
"SafeBrowsing.HPRT.Ineligible.IneligibleForSessionOrLocation",
/*sample=*/true,
/*expected_bucket_count=*/1);
histogram_tester.ExpectTotalCount(
"interstitial.phishing.decision.from_hash_prefix_real_time_check_v5",
/*expected_count=*/0);
}
IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageHashRealTimeCheckTest,
TriggerHitReportAndClientSafeBrowsingReportRequest) {
SetExtendedReportingPrefForTests(browser()->profile()->GetPrefs(), true);
SetUpAndNavigateToUrl(/*is_unsafe=*/true);
ASSERT_TRUE(IsShowingInterstitial());
// Verify correct hit report is sent.
EXPECT_EQ(hit_report_sent_threat_source(),
ThreatSource::NATIVE_PVER5_REAL_TIME);
// Verify correct CSBRR is sent.
auto threat_report_sent_runner = std::make_unique<base::RunLoop>();
SetReportSentCallback(threat_report_sent_runner->QuitClosure());
EXPECT_TRUE(ClickAndWaitForDetach(browser(), "proceed-link"));
ASSERT_FALSE(IsShowingInterstitial());
threat_report_sent_runner->Run();
std::string serialized_report = GetReportSent();
ClientSafeBrowsingReportRequest report;
ASSERT_TRUE(report.ParseFromString(serialized_report));
EXPECT_EQ(report.type(),
ClientSafeBrowsingReportRequest_ReportType_URL_PHISHING);
EXPECT_EQ(
report.client_properties().url_api_type(),
ClientSafeBrowsingReportRequest_SafeBrowsingUrlApiType_PVER5_NATIVE_REAL_TIME);
bool is_using_async_checks = GetParam();
EXPECT_EQ(report.client_properties().is_async_check(), is_using_async_checks);
}
class SafeBrowsingPrerenderBrowserTest
: public SafeBrowsingBlockingPageBrowserTest {
public:
SafeBrowsingPrerenderBrowserTest()
: prerender_helper_(base::BindRepeating(
&SafeBrowsingPrerenderBrowserTest::GetWebContents,
base::Unretained(this))) {}
~SafeBrowsingPrerenderBrowserTest() override = default;
SafeBrowsingPrerenderBrowserTest(const SafeBrowsingPrerenderBrowserTest&) =
delete;
SafeBrowsingPrerenderBrowserTest& operator=(
const SafeBrowsingPrerenderBrowserTest&) = delete;
void SetUp() override {
prerender_helper_.RegisterServerRequestMonitor(embedded_test_server());
SafeBrowsingBlockingPageBrowserTest::SetUp();
}
content::test::PrerenderTestHelper& prerender_helper() {
return prerender_helper_;
}
content::WebContents* GetWebContents() {
return browser()->tab_strip_model()->GetActiveWebContents();
}
// Prerenders |prerender_url|, which triggers SafeBrowsing, then verifies that
// the prerender is cancelled and that the security state of the primary page
// is not affected.
void PrerenderAndExpectCancellation(const GURL& prerender_url) {
content::test::PrerenderHostObserver observer(*GetWebContents(),
prerender_url);
prerender_helper().AddPrerenderAsync(prerender_url);
observer.WaitForDestroyed();
EXPECT_FALSE(IsShowingInterstitial(GetWebContents()));
ExpectNoSecurityIndicatorDowngrade(GetWebContents());
}
private:
content::test::PrerenderTestHelper prerender_helper_;
};
INSTANTIATE_TEST_SUITE_P(
All,
SafeBrowsingPrerenderBrowserTest,
testing::Combine(
testing::Values(
SBThreatType::SB_THREAT_TYPE_URL_MALWARE, // Threat types
SBThreatType::SB_THREAT_TYPE_URL_PHISHING,
SBThreatType::SB_THREAT_TYPE_URL_UNWANTED),
testing::Bool())); // If isolate all sites for testing.
// Attempt to prerender an unsafe page. The prerender navigation should be
// cancelled and should not affect the security state of the primary page.
IN_PROC_BROWSER_TEST_P(SafeBrowsingPrerenderBrowserTest, UnsafePrerender) {
base::HistogramTester histograms;
const GURL initial_url = embedded_test_server()->GetURL("/title1.html");
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url));
const GURL prerender_url = embedded_test_server()->GetURL(kEmptyPage);
SetURLThreatType(prerender_url, GetThreatType());
PrerenderAndExpectCancellation(prerender_url);
histograms.ExpectUniqueSample(
"Prerender.Experimental.PrerenderHostFinalStatus.SpeculationRule",
/*PrerenderFinalStatus::kBlockedByClient=*/28, 1);
}
class SafeBrowsingThreatDetailsPrerenderBrowserTest
: public SafeBrowsingPrerenderBrowserTest {};
INSTANTIATE_TEST_SUITE_P(
All,
SafeBrowsingThreatDetailsPrerenderBrowserTest,
// We simulate a SBThreatType::SB_THREAT_TYPE_URL_CLIENT_SIDE_PHISHING to
// trigger DOM detail collection.
testing::Combine(
testing::Values(SBThreatType::SB_THREAT_TYPE_URL_CLIENT_SIDE_PHISHING),
testing::Bool())); // If isolate all sites for testing.
// Test that the prerendering doesn't affect on the primary's threat report.
IN_PROC_BROWSER_TEST_P(SafeBrowsingThreatDetailsPrerenderBrowserTest,
DontContainPrerenderingInfoInThreatReport) {
SetExtendedReportingPrefForTests(browser()->profile()->GetPrefs(), true);
const bool expect_threat_details =
SafeBrowsingBlockingPage::ShouldReportThreatDetails(GetThreatType());
auto threat_report_sent_runner = std::make_unique<base::RunLoop>();
if (expect_threat_details) {
SetReportSentCallback(threat_report_sent_runner->QuitClosure());
}
// Navigate to a safe page which contains multiple potential DOM details
// on the primary page. (Despite the name, kMaliciousPage is not the page
// flagged as bad in this test.)
GURL primary_url = embedded_test_server()->GetURL(kMaliciousPage);
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), primary_url));
EXPECT_EQ(nullptr, details_factory_.get_details());
// Navigate to a different page on the prerendering page.
GURL prerender_url = embedded_test_server()->GetURL("/title1.html");
int host_id = prerender_helper().AddPrerender(prerender_url);
content::RenderFrameHost* prerender_render_frame_host =
prerender_helper().GetPrerenderedMainFrameHost(host_id);
EXPECT_NE(prerender_render_frame_host, nullptr);
EXPECT_EQ(prerender_url, prerender_render_frame_host->GetLastCommittedURL());
// Start navigation to bad page (kEmptyPage), which will be blocked before it
// is committed.
SetupWarningAndNavigate(browser());
ThreatDetails* threat_details = details_factory_.get_details();
EXPECT_EQ(expect_threat_details, threat_details != nullptr);
// Proceed through the warning.
EXPECT_TRUE(ClickAndWaitForDetach("proceed-link"));
AssertNoInterstitial(); // Assert the interstitial is gone
EXPECT_TRUE(IsExtendedReportingEnabled(*browser()->profile()->GetPrefs()));
EXPECT_EQ(primary_url, browser()
->tab_strip_model()
->GetActiveWebContents()
->GetLastCommittedURL());
if (expect_threat_details) {
threat_report_sent_runner->Run();
std::string serialized = GetReportSent();
ClientSafeBrowsingReportRequest report;
ASSERT_TRUE(report.ParseFromString(serialized));
// Verify the report is complete.
EXPECT_TRUE(report.complete());
// The threat report should not contain the prerender information.
EXPECT_NE(prerender_url.spec(), report.page_url());
EXPECT_NE(prerender_url.spec(), report.url());
ASSERT_EQ(3, report.resources_size());
for (const auto& resource : report.resources()) {
EXPECT_NE(prerender_url.spec(), resource.url());
}
}
}
class SafeBrowsingBlockingPageDelayedWarningPrerenderingBrowserTest
: public SafeBrowsingBlockingPageDelayedWarningBrowserTest {
public:
SafeBrowsingBlockingPageDelayedWarningPrerenderingBrowserTest() = default;
~SafeBrowsingBlockingPageDelayedWarningPrerenderingBrowserTest() override =
default;
void SetUpCommandLine(base::CommandLine* command_line) override {
SafeBrowsingBlockingPageDelayedWarningBrowserTest::SetUpCommandLine(
command_line);
// |prerender_helper_| has a ScopedFeatureList so we needed to delay its
// creation until now because
// SafeBrowsingBlockingPageDelayedWarningBrowserTest also uses a
// ScopedFeatureList and initialization order matters.
prerender_helper_ = std::make_unique<content::test::PrerenderTestHelper>(
base::BindRepeating(
&SafeBrowsingBlockingPageDelayedWarningPrerenderingBrowserTest::
GetWebContents,
base::Unretained(this)));
}
void SetUpOnMainThread() override {
prerender_helper_->RegisterServerRequestMonitor(embedded_test_server());
SafeBrowsingBlockingPageDelayedWarningBrowserTest::SetUpOnMainThread();
}
content::test::PrerenderTestHelper& prerender_helper() {
return *prerender_helper_;
}
content::WebContents* GetWebContents() {
return browser()->tab_strip_model()->GetActiveWebContents();
}
private:
std::unique_ptr<content::test::PrerenderTestHelper> prerender_helper_;
};
INSTANTIATE_TEST_SUITE_P(
All,
SafeBrowsingBlockingPageDelayedWarningPrerenderingBrowserTest,
testing::Combine(testing::Bool(), /* IsolateAllSitesForTesting */
testing::Bool() /* Show warning on mouse click */));
// This test loads a page in the prerender to ensure that the prerendering
// navigation is skipped at DidFinishNavigation() from
// SafeBrowsingUserInteractionObserver.
IN_PROC_BROWSER_TEST_P(
SafeBrowsingBlockingPageDelayedWarningPrerenderingBrowserTest,
DoNotRecordMetricsInPrerendering) {
base::HistogramTester histograms;
NavigateAndAssertNoInterstitial();
// Load a page in the prerender.
GURL prerender_url = embedded_test_server()->GetURL("/simple.html");
prerender_helper().AddPrerender(prerender_url);
// Activating the prerendered page causes "flush" metrics.
prerender_helper().NavigatePrimaryPage(prerender_url);
}
class WarningShownTimestampCSBRRDisabledBrowserTest
: public SafeBrowsingBlockingPageBrowserTest {
public:
WarningShownTimestampCSBRRDisabledBrowserTest() {
scoped_feature_list_.InitAndDisableFeature(
safe_browsing::kAddWarningShownTSToClientSafeBrowsingReport);
}
~WarningShownTimestampCSBRRDisabledBrowserTest() override = default;
void SetUp() override { SafeBrowsingBlockingPageBrowserTest::SetUp(); }
content::WebContents* GetWebContents() {
return browser()->tab_strip_model()->GetActiveWebContents();
}
void RunThreatReportSentLoop() {
base::RunLoop threat_report_sent_loop;
SetReportSentCallback(threat_report_sent_loop.QuitClosure());
threat_report_sent_loop.Run();
}
void CheckCSBRRForTimestamp() {
std::string serialized = GetReportSent();
ClientSafeBrowsingReportRequest report;
ASSERT_TRUE(report.ParseFromString(serialized));
// The timestamp of the warning shown should not be in the report.
EXPECT_FALSE(report.has_warning_shown_timestamp_msec());
}
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
INSTANTIATE_TEST_SUITE_P(
WarningShownTimestampCSBRRDisabledBrowserTestWithThreatTypeAndIsolationSetting,
WarningShownTimestampCSBRRDisabledBrowserTest,
testing::Combine(
testing::Values(
SBThreatType::SB_THREAT_TYPE_URL_PHISHING, // Threat types
SBThreatType::SB_THREAT_TYPE_URL_CLIENT_SIDE_PHISHING),
testing::Bool())); // If isolate all sites for testing.
IN_PROC_BROWSER_TEST_P(WarningShownTimestampCSBRRDisabledBrowserTest,
TimestampNotInCSBRRClickedThroughBlockingPage) {
SetExtendedReportingPrefForTests(browser()->profile()->GetPrefs(), true);
content::TestNavigationObserver observer(
browser()->tab_strip_model()->GetActiveWebContents());
SetupWarningAndNavigate(browser());
// Proceed to unsafe site, sending CSBRR.
EXPECT_TRUE(ClickAndWaitForDetach("proceed-link"));
observer.WaitForNavigationFinished();
RunThreatReportSentLoop();
CheckCSBRRForTimestamp();
}
IN_PROC_BROWSER_TEST_P(WarningShownTimestampCSBRRDisabledBrowserTest,
TimestampNotInFallbackCSBRRSent) {
SetExtendedReportingPrefForTests(browser()->profile()->GetPrefs(), true);
content::TestNavigationObserver observer(
browser()->tab_strip_model()->GetActiveWebContents());
SetupWarningAndNavigate(browser());
// Send CSBRR without interactions.
chrome::CloseTab(browser());
observer.WaitForNavigationFinished();
RunThreatReportSentLoop();
CheckCSBRRForTimestamp();
}
} // namespace safe_browsing