Chrome · Passwords
CVE-2026-87624
Logic Error in Passwords
Overview
Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifchrome/browser/password_manager/chrome_password_manager_client.cc |
modified |
Files Changed
chrome/browser/password_manager/chrome_password_manager_client.ccchrome/browser/password_manager/chrome_password_manager_client_unittest.cc
Patch
From 10c3ba83d3674d727b18f64e239bda152bb32e9e Mon Sep 17 00:00:00 2001 From: Friedrich Horschig <[email protected]> Date: Thu, 30 Jul 2026 07:59:30 -0700 Subject: [PATCH] [Autofill] Check driver CanShowAutofillUi before showing TTF This CL ensures that ChromePasswordManagerClient::ContinueShowKeyboardReplacingSurface checks weak_driver->CanShowAutofillUi() before proceeding to show Touch to Fill or CredMan UI and setting the keyboard replacing surface visibility. Fixed: 533044125 Change-Id: If69b90f8fbb48aadaefbdca4800ffd99344452ae Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8174004 Reviewed-by: Ioana Treib <[email protected]> Commit-Queue: Friedrich Hauser <[email protected]> Cr-Commit-Position: refs/heads/main@{#1671066} --- diff --git a/chrome/browser/password_manager/chrome_password_manager_client.cc b/chrome/browser/password_manager/chrome_password_manager_client.cc index 1df9f1f..7fd808b4 100644 --- a/chrome/browser/password_manager/chrome_password_manager_client.cc +++ b/chrome/browser/password_manager/chrome_password_manager_client.cc @@ -653,7 +653,7 @@ // without being called. auto split_delay_callback = base::SplitOnceCallback(std::move(delay_callback)); - if (!weak_driver) { + if (!weak_driver || !weak_driver->CanShowAutofillUi()) { return; } password_manager::ContentPasswordManagerDriver* driver = diff --git a/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc b/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc index 220675f..738ca2fb 100644 --- a/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc +++ b/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc @@ -92,6 +92,7 @@ #include "content/public/common/url_constants.h" #include "content/public/test/mock_navigation_handle.h" #include "content/public/test/navigation_simulator.h" +#include "content/public/test/prerender_test_util.h" #include "content/public/test/web_contents_tester.h" #include "mojo/public/cpp/bindings/associated_receiver.h" #include "mojo/public/cpp/bindings/pending_associated_receiver.h" @@ -2590,4 +2591,54 @@ base::RunLoop().RunUntilIdle(); } +TEST_F(ChromePasswordManagerClientAndroidTest, + DoNotShowKeyboardReplacingSurfaceForInactiveFrame) { + webauthn::WebAuthnCredManDelegate::override_cred_man_support_for_testing( + webauthn::CredManSupport::DISABLED); + CreateManualFillingController(web_contents()); + + auto* ttf_controller = MakeMockTouchToFillPasswordManagerController(); + EXPECT_CALL(*ttf_controller, InitData).Times(0); + EXPECT_CALL(*ttf_controller, Show).Times(0); + + constexpr char kUrl[] = "https://www.foo.com/login.html"; + NavigateAndCommit(GURL(kUrl)); + + content::RenderFrameHostWrapper old_rfh(main_rfh()); + ContentAutofillDriver* autofill_driver = + ContentAutofillDriver::GetForRenderFrameHost(old_rfh.get()); + ASSERT_TRUE(autofill_driver); + + std::vector<FormFieldData> fields = {CreateTestFormField( + "Username:", "username", "", FormControlType::kInputText, "webauthn")}; + FormData form = + CreateFormDataForRenderFrameHost(*old_rfh.get(), std::move(fields)); + { + autofill::TestAutofillManagerWaiter waiter( + autofill_driver->GetAutofillManager(), + {autofill::AutofillManagerEvent::kFormsSeen}); + autofill_driver->renderer_events().FormsSeen(/*updated_forms=*/{form}, + /*removed_forms=*/{}); + ASSERT_TRUE(waiter.Wait(/*num_expected_relevant_events=*/1)); + } + + auto* old_driver = + ContentPasswordManagerDriver::GetForRenderFrameHost(old_rfh.get()); + + GetClient()->ShowKeyboardReplacingSurface( + old_driver, GetFocusedFieldSuggestionRequest(form)); + + // Navigate to another site so old_rfh becomes inactive. + NavigateAndCommit(GURL("https://www.bar.com/page2.html")); + ASSERT_FALSE(old_rfh.get()->IsActive()); + + // Passkeys arrive for the old driver/frame after page navigation. + ChromeWebAuthnCredentialsDelegateFactory::GetFactory(web_contents()) + ->GetDelegateForFrame(old_rfh.get()) + ->OnCredentialsReceived( + std::vector<password_manager::PasskeyCredential>(), + ChromeWebAuthnCredentialsDelegate::SecurityKeyOrHybridFlowAvailable( + true)); +} + #endif // BUILDFLAG(IS_ANDROID)
Loading diff…
Regression Test / PoC
shipped with the fix
diff --git a/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc b/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc
index 220675f..738ca2fb 100644
--- a/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc
+++ b/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc
@@ -92,6 +92,7 @@
#include "content/public/common/url_constants.h"
#include "content/public/test/mock_navigation_handle.h"
#include "content/public/test/navigation_simulator.h"
+#include "content/public/test/prerender_test_util.h"
#include "content/public/test/web_contents_tester.h"
#include "mojo/public/cpp/bindings/associated_receiver.h"
#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
@@ -2590,4 +2591,54 @@
base::RunLoop().RunUntilIdle();
}
+TEST_F(ChromePasswordManagerClientAndroidTest,
+ DoNotShowKeyboardReplacingSurfaceForInactiveFrame) {
+ webauthn::WebAuthnCredManDelegate::override_cred_man_support_for_testing(
+ webauthn::CredManSupport::DISABLED);
+ CreateManualFillingController(web_contents());
+
+ auto* ttf_controller = MakeMockTouchToFillPasswordManagerController();
+ EXPECT_CALL(*ttf_controller, InitData).Times(0);
+ EXPECT_CALL(*ttf_controller, Show).Times(0);
+
+ constexpr char kUrl[] = "https://www.foo.com/login.html";
+ NavigateAndCommit(GURL(kUrl));
+
+ content::RenderFrameHostWrapper old_rfh(main_rfh());
+ ContentAutofillDriver* autofill_driver =
+ ContentAutofillDriver::GetForRenderFrameHost(old_rfh.get());
+ ASSERT_TRUE(autofill_driver);
+
+ std::vector<FormFieldData> fields = {CreateTestFormField(
+ "Username:", "username", "", FormControlType::kInputText, "webauthn")};
+ FormData form =
+ CreateFormDataForRenderFrameHost(*old_rfh.get(), std::move(fields));
+ {
+ autofill::TestAutofillManagerWaiter waiter(
+ autofill_driver->GetAutofillManager(),
+ {autofill::AutofillManagerEvent::kFormsSeen});
+ autofill_driver->renderer_events().FormsSeen(/*updated_forms=*/{form},
+ /*removed_forms=*/{});
+ ASSERT_TRUE(waiter.Wait(/*num_expected_relevant_events=*/1));
+ }
+
+ auto* old_driver =
+ ContentPasswordManagerDriver::GetForRenderFrameHost(old_rfh.get());
+
+ GetClient()->ShowKeyboardReplacingSurface(
+ old_driver, GetFocusedFieldSuggestionRequest(form));
+
+ // Navigate to another site so old_rfh becomes inactive.
+ NavigateAndCommit(GURL("https://www.bar.com/page2.html"));
+ ASSERT_FALSE(old_rfh.get()->IsActive());
+
+ // Passkeys arrive for the old driver/frame after page navigation.
+ ChromeWebAuthnCredentialsDelegateFactory::GetFactory(web_contents())
+ ->GetDelegateForFrame(old_rfh.get())
+ ->OnCredentialsReceived(
+ std::vector<password_manager::PasskeyCredential>(),
+ ChromeWebAuthnCredentialsDelegate::SecurityKeyOrHybridFlowAvailable(
+ true));
+}
+
#endif // BUILDFLAG(IS_ANDROID)
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