Chrome · Safebrowsing
CVE-2026-87519
Logic Error in Safebrowsing
Overview
Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
switchcomponents/security_interstitials/core/safe_browsing_loud_error_ui.cc |
modified | |
ifcomponents/security_interstitials/core/safe_browsing_loud_error_ui.cc |
modified | |
switchcomponents/security_interstitials/core/safe_browsing_quiet_error_ui.cc |
modified |
Files Changed
components/safe_browsing/content/browser/base_blocking_page.cccomponents/safe_browsing/content/browser/ui_manager_unittest.cccomponents/security_interstitials/core/safe_browsing_loud_error_ui.cccomponents/security_interstitials/core/safe_browsing_quiet_error_ui.cc
Patch
From d592f083e77d44162c672637d66334cba1851561 Mon Sep 17 00:00:00 2001
From: Piotr Krzeszewski <krzeszewski@google.com>
Date: Thu, 06 Aug 2026 08:03:17 -0700
Subject: [PATCH] [Safe Browsing] Disable keyboard override when proceeding is disabled
When SafeBrowsingProceedAnywayDisabled policy is active, typing "thisisunsafe"
prematurely allowlisted the host before policy checks forced navigation away.
This change:
- Sets `disableKeyboardOverride` in SafeBrowsingLoudErrorUI.
- Prevents allowlisting in BaseBlockingPage if proceeding is disabled.
- Adds unit test coverage for policy enforcement.
Tested with local reproduction steps presented in the bug
Bug: 501763003
Change-Id: Idea6720c9a8c07c6bfb261e9d29d4e50734f84cc
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8178244
Reviewed-by: thefrog <thefrog@chromium.org>
Reviewed-by: Michał Kaczmarczyk <mickaczmarczyk@google.com>
Commit-Queue: Piotr Krzeszewski <krzeszewski@google.com>
Reviewed-by: Dominique Fauteux-Chapleau <domfc@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1674980}
---
diff --git a/components/safe_browsing/content/browser/base_blocking_page.cc b/components/safe_browsing/content/browser/base_blocking_page.cc
index 4b07a2bc..d600077c 100644
--- a/components/safe_browsing/content/browser/base_blocking_page.cc
+++ b/components/safe_browsing/content/browser/base_blocking_page.cc
@@ -118,7 +118,8 @@
static_cast<security_interstitials::SecurityInterstitialCommand>(command);
if (interstitial_command ==
- security_interstitials::SecurityInterstitialCommand::CMD_PROCEED) {
+ security_interstitials::SecurityInterstitialCommand::CMD_PROCEED &&
+ !sb_error_ui_->is_proceed_anyway_disabled()) {
// With committed interstitials, OnProceed() doesn't get called, so handle
// adding to the allow list here.
set_proceeded(true);
diff --git a/components/safe_browsing/content/browser/ui_manager_unittest.cc b/components/safe_browsing/content/browser/ui_manager_unittest.cc
index 4ef14a1..7ed70e7 100644
--- a/components/safe_browsing/content/browser/ui_manager_unittest.cc
+++ b/components/safe_browsing/content/browser/ui_manager_unittest.cc
@@ -96,7 +96,8 @@
TestSafeBrowsingBlockingPage(BaseUIManager* manager,
content::WebContents* web_contents,
const GURL& main_frame_url,
- const UnsafeResourceList& unsafe_resources)
+ const UnsafeResourceList& unsafe_resources,
+ bool is_proceed_anyway_disabled = false)
: SafeBrowsingBlockingPage(
manager,
web_contents,
@@ -117,14 +118,14 @@
BaseSafeBrowsingErrorUI::SBErrorDisplayOptions(
BaseBlockingPage::IsMainPageResourceLoadPending(
unsafe_resources),
- false, // is_extended_reporting_opt_in_allowed
- false, // is_off_the_record
- false, // is_extended_reporting_enabled
- false, // is_extended_reporting_policy_managed
- false, // is_enhanced_protection_enabled
- false, // is_proceed_anyway_disabled
- false, // should_open_links_in_new_tab
- true, // always_show_back_to_safety
+ false, // is_extended_reporting_opt_in_allowed
+ false, // is_off_the_record
+ false, // is_extended_reporting_enabled
+ false, // is_extended_reporting_policy_managed
+ false, // is_enhanced_protection_enabled
+ is_proceed_anyway_disabled, // is_proceed_anyway_disabled
+ false, // should_open_links_in_new_tab
+ true, // always_show_back_to_safety
false, // is_enhanced_protection_message_enabled
false, // is_safe_browsing_managed
"cpn_safe_browsing"), // help_center_article_link
@@ -132,7 +133,7 @@
/*navigation_observer_manager=*/nullptr,
/*metrics_collector=*/nullptr,
/*trigger_manager=*/nullptr,
- /*is_proceed_anyway_disabled=*/false,
+ is_proceed_anyway_disabled,
/*is_safe_browsing_surveys_enabled=*/true,
/*trust_safety_sentiment_service_trigger=*/base::NullCallback(),
/*ignore_auto_revocation_notifications_trigger=*/
@@ -824,4 +825,27 @@
&threat_type));
}
+TEST_F(SafeBrowsingUIManagerTest,
+ ProceedAnywayDisabled_CommandReceivedDoesNotAllowlist) {
+ security_interstitials::UnsafeResource resource =
+ MakeUnsafeResourceAndStartNavigation(kBadURL);
+ resource.threat_source = ThreatSource::LOCAL_PVER4;
+ std::vector<security_interstitials::UnsafeResource> resources = {resource};
+ TestSafeBrowsingBlockingPage blocking_page(
+ ui_manager(), web_contents(), GURL(kBadURL), resources,
+ /*is_proceed_anyway_disabled=*/true);
+
+ // 1. Verify frontend string population: disableKeyboardOverride must be true.
+ base::DictValue load_time_data;
+ blocking_page.sb_error_ui()->PopulateStringsForHtml(load_time_data);
+ std::optional<bool> disable_keyboard =
+ load_time_data.FindBool("disableKeyboardOverride");
+ ASSERT_TRUE(disable_keyboard.has_value());
+ EXPECT_TRUE(disable_keyboard.value());
+
+ // 2. Verify backend command handling: CMD_PROCEED (1) must not allowlist URL.
+ blocking_page.CommandReceived("1");
+ EXPECT_FALSE(IsAllowlisted(resource));
+}
+
} // namespace safe_browsing
diff --git a/components/security_interstitials/core/safe_browsing_loud_error_ui.cc b/components/security_interstitials/core/safe_browsing_loud_error_ui.cc
index e12e759..a154c82 100644
--- a/components/security_interstitials/core/safe_browsing_loud_error_ui.cc
+++ b/components/security_interstitials/core/safe_browsing_loud_error_ui.cc
@@ -86,6 +86,7 @@
"primaryButtonText",
l10n_util::GetStringUTF16(IDS_SAFEBROWSING_OVERRIDABLE_SAFETY_BUTTON));
load_time_data.Set("overridable", !is_proceed_anyway_disabled());
+ load_time_data.Set("disableKeyboardOverride", is_proceed_anyway_disabled());
load_time_data.Set(
security_interstitials::kOptInLink,
l10n_util::GetStringUTF16(IDS_SAFE_BROWSING_SCOUT_REPORTING_AGREE));
@@ -131,8 +132,8 @@
switch (command) {
case CMD_PROCEED: {
// User pressed on the button to proceed.
- user_made_decision_ = true;
if (!is_proceed_anyway_disabled()) {
+ user_made_decision_ = true;
controller()->metrics_helper()->RecordUserDecision(
MetricsHelper::PROCEED);
controller()->Proceed();
@@ -143,7 +144,11 @@
}
case CMD_DONT_PROCEED: {
// User pressed on the button to return to safety.
- user_made_decision_ = true;
+ // Only record a user decision if the command was actually
+ // CMD_DONT_PROCEED (not a fallthrough from a policy-blocked CMD_PROCEED).
+ if (command == CMD_DONT_PROCEED) {
+ user_made_decision_ = true;
+ }
// Don't record the user action here because there are other ways of
// triggering DontProceed, like clicking the back button.
if (is_main_frame_load_pending()) {
diff --git a/components/security_interstitials/core/safe_browsing_quiet_error_ui.cc b/components/security_interstitials/core/safe_browsing_quiet_error_ui.cc
index 75baec1..7482022 100644
--- a/components/security_interstitials/core/safe_browsing_quiet_error_ui.cc
+++ b/components/security_interstitials/core/safe_browsing_quiet_error_ui.cc
@@ -83,8 +83,8 @@
switch (command) {
case CMD_PROCEED: {
// User pressed on the button to proceed.
- user_made_decision_ = true;
if (!is_proceed_anyway_disabled()) {
+ user_made_decision_ = true;
controller()->metrics_helper()->RecordUserDecision(
MetricsHelper::PROCEED);
controller()->Proceed();
Loading diff…
Regression Test / PoC
shipped with the fix
diff --git a/components/safe_browsing/content/browser/ui_manager_unittest.cc b/components/safe_browsing/content/browser/ui_manager_unittest.cc
index 4ef14a1..7ed70e7 100644
--- a/components/safe_browsing/content/browser/ui_manager_unittest.cc
+++ b/components/safe_browsing/content/browser/ui_manager_unittest.cc
@@ -96,7 +96,8 @@
TestSafeBrowsingBlockingPage(BaseUIManager* manager,
content::WebContents* web_contents,
const GURL& main_frame_url,
- const UnsafeResourceList& unsafe_resources)
+ const UnsafeResourceList& unsafe_resources,
+ bool is_proceed_anyway_disabled = false)
: SafeBrowsingBlockingPage(
manager,
web_contents,
@@ -117,14 +118,14 @@
BaseSafeBrowsingErrorUI::SBErrorDisplayOptions(
BaseBlockingPage::IsMainPageResourceLoadPending(
unsafe_resources),
- false, // is_extended_reporting_opt_in_allowed
- false, // is_off_the_record
- false, // is_extended_reporting_enabled
- false, // is_extended_reporting_policy_managed
- false, // is_enhanced_protection_enabled
- false, // is_proceed_anyway_disabled
- false, // should_open_links_in_new_tab
- true, // always_show_back_to_safety
+ false, // is_extended_reporting_opt_in_allowed
+ false, // is_off_the_record
+ false, // is_extended_reporting_enabled
+ false, // is_extended_reporting_policy_managed
+ false, // is_enhanced_protection_enabled
+ is_proceed_anyway_disabled, // is_proceed_anyway_disabled
+ false, // should_open_links_in_new_tab
+ true, // always_show_back_to_safety
false, // is_enhanced_protection_message_enabled
false, // is_safe_browsing_managed
"cpn_safe_browsing"), // help_center_article_link
@@ -132,7 +133,7 @@
/*navigation_observer_manager=*/nullptr,
/*metrics_collector=*/nullptr,
/*trigger_manager=*/nullptr,
- /*is_proceed_anyway_disabled=*/false,
+ is_proceed_anyway_disabled,
/*is_safe_browsing_surveys_enabled=*/true,
/*trust_safety_sentiment_service_trigger=*/base::NullCallback(),
/*ignore_auto_revocation_notifications_trigger=*/
@@ -824,4 +825,27 @@
&threat_type));
}
+TEST_F(SafeBrowsingUIManagerTest,
+ ProceedAnywayDisabled_CommandReceivedDoesNotAllowlist) {
+ security_interstitials::UnsafeResource resource =
+ MakeUnsafeResourceAndStartNavigation(kBadURL);
+ resource.threat_source = ThreatSource::LOCAL_PVER4;
+ std::vector<security_interstitials::UnsafeResource> resources = {resource};
+ TestSafeBrowsingBlockingPage blocking_page(
+ ui_manager(), web_contents(), GURL(kBadURL), resources,
+ /*is_proceed_anyway_disabled=*/true);
+
+ // 1. Verify frontend string population: disableKeyboardOverride must be true.
+ base::DictValue load_time_data;
+ blocking_page.sb_error_ui()->PopulateStringsForHtml(load_time_data);
+ std::optional<bool> disable_keyboard =
+ load_time_data.FindBool("disableKeyboardOverride");
+ ASSERT_TRUE(disable_keyboard.has_value());
+ EXPECT_TRUE(disable_keyboard.value());
+
+ // 2. Verify backend command handling: CMD_PROCEED (1) must not allowlist URL.
+ blocking_page.CommandReceived("1");
+ EXPECT_FALSE(IsAllowlisted(resource));
+}
+
} // namespace safe_browsing
Loading diff…
Original Bug Report
The reporter's bug is still restricted on the tracker. Chrome de-restricts security bugs ~30–90 days after the fix ships; a later run will backfill it here.
References
On This Page