Chrome · Passwords › Passwords
CVE-2026-106256
Info Disclosure in Passwords (Passwords)
Overview
Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifchrome/browser/password_manager/android/all_passwords_bottom_sheet_controller.cc |
modified | |
PasswordManagerClientchrome/browser/password_manager/android/all_passwords_bottom_sheet_controller.h |
modified | |
MockPasswordManagerDriverchrome/browser/password_manager/android/all_passwords_bottom_sheet_controller_unittest.cc |
modified | |
MockPasswordManagerDriverchrome/browser/password_manager/android/all_passwords_bottom_sheet_controller_unittest.cc |
modified | |
MockAllPasswordsBottomSheetViewchrome/browser/password_manager/android/all_passwords_bottom_sheet_controller_unittest.cc |
modified | |
ifchrome/browser/password_manager/android/all_passwords_bottom_sheet_controller_unittest.cc |
modified | |
TEST_Fchrome/browser/password_manager/android/all_passwords_bottom_sheet_controller_unittest.cc |
modified | |
TEST_Fcomponents/password_manager/content/browser/content_password_manager_driver_unittest.cc |
modified |
Files Changed
chrome/browser/password_manager/android/all_passwords_bottom_sheet_controller_unittest.ccchrome/browser/password_manager/android/all_passwords_bottom_sheet_controller.hchrome/browser/password_manager/android/all_passwords_bottom_sheet_controller.cccomponents/password_manager/content/browser/content_password_manager_driver_unittest.cccomponents/password_manager/content/browser/content_password_manager_driver.ccchrome/browser/ui/android/passwords/all_passwords_bottom_sheet_view_impl.cc
Patch
From 9e0fe1fbbe859883a93bc9911997bafb006c68bc Mon Sep 17 00:00:00 2001 From: Ioana Treib <[email protected]> Date: Tue, 08 Sep 2026 01:51:50 -0700 Subject: [PATCH] [Passwords] Check frame active and origin in AllPasswords sheet When the All Passwords Bottom Sheet finishes fetching credentials asynchronously, the originating frame may have navigated or entered the BackForwardCache. This CL: 1. Adds an active-frame check to ContentPasswordManagerDriver::FillIntoFocusedField to avoid dispatching IPC to inactive/BFCached renderers. 2. Captures the frame origin/URL at initialization time in AllPasswordsBottomSheetController. 3. Verifies driver_->CanShowAutofillUi() and IsSameOriginWith() before showing the sheet in OnResultFromAllStoresReceived. 4. Verifies driver validity and IsSameOriginWith() before filling credentials in OnCredentialSelected and FillPassword. Bug:553335319 Change-Id: Iad6dacd44525a23b2ab9f2da51fc40d036062b31 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8354143 Reviewed-by: Maria Kazinova <[email protected]> Commit-Queue: Ioana Treib <[email protected]> Cr-Commit-Position: refs/heads/main@{#1693629} --- diff --git a/chrome/browser/password_manager/android/all_passwords_bottom_sheet_controller.cc b/chrome/browser/password_manager/android/all_passwords_bottom_sheet_controller.cc index 8ad06fcd..9890981c 100644 --- a/chrome/browser/password_manager/android/all_passwords_bottom_sheet_controller.cc +++ b/chrome/browser/password_manager/android/all_passwords_bottom_sheet_controller.cc @@ -49,7 +49,10 @@ focused_field_type_(focused_field_type), client_(client), password_reuse_detection_manager_client_( - password_reuse_detection_manager_client) {} + password_reuse_detection_manager_client) { + CHECK(driver_); + frame_origin_ = driver_->GetLastCommittedOrigin(); +} AllPasswordsBottomSheetController::AllPasswordsBottomSheetController( content::WebContents* web_contents, @@ -72,6 +75,7 @@ password_manager::ContentPasswordManagerDriver::GetForRenderFrameHost( focused_frame); driver_ = driver->AsWeakPtr(); + frame_origin_ = driver_->GetLastCommittedOrigin(); client_ = ChromePasswordManagerClient::FromWebContents(web_contents_); password_reuse_detection_manager_client_ = ChromePasswordReuseDetectionManagerClient::FromWebContents(web_contents_); @@ -136,7 +140,8 @@ RequestsToFillPassword requests_to_fill_password) { const bool is_password_field = focused_field_type_ == FocusedFieldType::kFillablePasswordField; - if (!driver_) { + if (!driver_ || + !driver_->GetLastCommittedOrigin().IsSameOriginWith(frame_origin_)) { OnDismiss(); return; } @@ -169,11 +174,13 @@ } void AllPasswordsBottomSheetController::OnDismiss() { - std::move(dismissal_callback_).Run(); + if (dismissal_callback_) { + std::move(dismissal_callback_).Run(); + } } -const GURL& AllPasswordsBottomSheetController::GetFrameUrl() { - return driver_->GetLastCommittedURL(); +GURL AllPasswordsBottomSheetController::GetFrameOriginUrl() { + return frame_origin_.GetURL(); } void AllPasswordsBottomSheetController::OnReauthCompleted( @@ -192,7 +199,8 @@ void AllPasswordsBottomSheetController::FillPassword( const std::u16string& password) { - if (!driver_) { + if (!driver_ || + !driver_->GetLastCommittedOrigin().IsSameOriginWith(frame_origin_)) { return; } driver_->FillIntoFocusedField(true, password); @@ -205,6 +213,12 @@ CHECK(!results.empty()); on_password_forms_received_barrier_callback_.Reset(); + if (!driver_ || !driver_->CanShowAutofillUi() || + !driver_->GetLastCommittedOrigin().IsSameOriginWith(frame_origin_)) { + OnDismiss(); + return; + } + if (results.size() > 1) { std::move(results[1].begin(), results[1].end(), std::back_inserter(results[0])); diff --git a/chrome/browser/password_manager/android/all_passwords_bottom_sheet_controller.h b/chrome/browser/password_manager/android/all_passwords_bottom_sheet_controller.h index 3d314a60..807f3c1 100644 --- a/chrome/browser/password_manager/android/all_passwords_bottom_sheet_controller.h +++ b/chrome/browser/password_manager/android/all_passwords_bottom_sheet_controller.h @@ -22,6 +22,7 @@ #include "components/password_manager/core/browser/password_store/stored_credential.h" #include "ui/gfx/native_ui_types.h" #include "url/gurl.h" +#include "url/origin.h" namespace password_manager { class PasswordManagerClient; @@ -97,8 +98,8 @@ // consumes |dismissal_callback|. void OnDismiss(); - // Returns the last committed URL of the frame from |driver_|. - const GURL& GetFrameUrl(); + // Returns the URL of `frame_origin_`. + GURL GetFrameOriginUrl(); private: // Called when the biometric re-auth completes. |password| is the password @@ -150,6 +151,9 @@ raw_ptr<safe_browsing::PasswordReuseDetectionManagerClient> password_reuse_detection_manager_client_ = nullptr; + // The origin of the frame at the time this controller was created. + url::Origin frame_origin_; + base::WeakPtrFactory<AllPasswordsBottomSheetController> weak_ptr_factory_{ this}; }; diff --git a/chrome/browser/ui/android/passwords/all_passwords_bottom_sheet_view_impl.cc b/chrome/browser/ui/android/passwords/all_passwords_bottom_sheet_view_impl.cc index c332294..da6f1a59 100644 --- a/chrome/browser/ui/android/passwords/all_passwords_bottom_sheet_view_impl.cc +++ b/chrome/browser/ui/android/passwords/all_passwords_bottom_sheet_view_impl.cc @@ -104,7 +104,7 @@ AttachCurrentThread(), reinterpret_cast<intptr_t>(this), controller_->GetProfile()->GetJavaObject(), controller_->GetNativeView()->GetWindowAndroid()->GetJavaObject(), - controller_->GetFrameUrl().spec()); + controller_->GetFrameOriginUrl().spec()); } DEFINE_JNI(AllPasswordsBottomSheetBridge) diff --git a/components/password_manager/content/browser/content_password_manager_driver.cc b/components/password_manager/content/browser/content_password_manager_driver.cc index 4552010..9314296 100644 --- a/components/password_manager/content/browser/content_password_manager_driver.cc +++ b/components/password_manager/content/browser/content_password_manager_driver.cc @@ -330,6 +330,9 @@ void ContentPasswordManagerDriver::FillIntoFocusedField( bool is_password, const std::u16string& credential) { + if (!render_frame_host_->IsActive()) { + return; + } if (const auto& agent = GetPasswordAutofillAgent()) { agent->FillIntoFocusedField(is_password, credential); } diff --git a/chrome/browser/password_manager/android/all_passwords_bottom_sheet_controller_unittest.cc b/chrome/browser/password_manager/android/all_passwords_bottom_sheet_controller_unittest.cc index ed788ccd..917675e7 100644 --- a/chrome/browser/password_manager/android/all_passwords_bottom_sheet_controller_unittest.cc +++ b/chrome/browser/password_manager/android/all_passwords_bottom_sheet_controller_unittest.cc @@ -6,6 +6,7 @@ #include "base/android/device_info.h" #include "base/memory/raw_ptr.h" +#include "base/run_loop.h" #include "base/test/gmock_callback_support.h" #include "base/test/mock_callback.h" #include "base/test/scoped_feature_list.h" @@ -66,10 +67,30 @@ class MockPasswordManagerDriver : public password_manager::StubPasswordManagerDriver { public: + MockPasswordManagerDriver() { + ON_CALL(*this, CanShowAutofillUi).WillByDefault(testing::Return(true)); + ON_CALL(*this, GetLastCommittedOrigin) + .WillByDefault(testing::ReturnRef(origin_)); + ON_CALL(*this, GetLastCommittedURL).WillByDefault(testing::ReturnRef(url_)); + } + MOCK_METHOD(void, FillIntoFocusedField, (bool, const std::u16string&), (override)); + MOCK_METHOD(bool, CanShowAutofillUi, (), (const, override)); + MOCK_METHOD(const url::Origin&, + GetLastCommittedOrigin, + (), + (const, override)); + MOCK_METHOD(const GURL&, GetLastCommittedURL, (), (const, override)); + + void SetLastCommittedOrigin(const url::Origin& origin) { origin_ = origin; } + void SetLastCommittedURL(const GURL& url) { url_ = url; } + + private: + url::Origin origin_; + GURL url_; }; class MockAllPasswordsBottomSheetView : public AllPasswordsBottomSheetView { @@ -139,20 +160,26 @@ } void createAllPasswordsController( - autofill::mojom::FocusedFieldType focused_field_type) { + autofill::mojom::FocusedFieldType focused_field_type, + base::WeakPtr<password_manager::PasswordManagerDriver> driver) { std::unique_ptr<MockAllPasswordsBottomSheetView> mock_view_unique_ptr = std::make_unique<MockAllPasswordsBottomSheetView>(); mock_view_ = mock_view_unique_ptr.get(); all_passwords_controller_ = std::make_unique<AllPasswordsBottomSheetController>( base::PassKey<AllPasswordsBottomSheetControllerTest>(), - web_contents(), std::move(mock_view_unique_ptr), - driver_.AsWeakPtr(), profile_store_.get(), account_store_.get(), + web_contents(), std::move(mock_view_unique_ptr), driver, + profile_store_.get(), account_store_.get(), dissmissal_callback_.Get(), focused_field_type, mock_pwd_manager_client_.get(), mock_pwd_reuse_detection_manager_client_.get()); } + void createAllPasswordsController( + autofill::mojom::FocusedFieldType focused_field_type) { + createAllPasswordsController(focused_field_type, driver_.AsWeakPtr()); + } + void TearDown() override { profile_store_->ShutdownOnUIThread(); if (account_store_) { @@ -458,3 +485,67 @@ // Show method uses the store which has async work. RunUntilIdle(); } + +TEST_F(AllPasswordsBottomSheetControllerTest, DoesNotShowWhenDriverIsNull) { + base::RunLoop run_loop; + auto local_driver = std::make_unique<MockPasswordManagerDriver>(); + createAllPasswordsController(FocusedFieldType::kFillablePasswordField, + local_driver->AsWeakPtr()); + local_driver.reset(); + + EXPECT_CALL(view(), Show).Times(0); + EXPECT_CALL(dismissal_callback(), Run()) + .WillOnce(base::test::RunClosure(run_loop.QuitClosure())); + all_passwords_controller()->Show(); + run_loop.Run(); +} + +TEST_F(AllPasswordsBottomSheetControllerTest, + DoesNotShowWhenCannotShowAutofillUi) { + base::RunLoop run_loop; + EXPECT_CALL(driver(), CanShowAutofillUi).WillRepeatedly(Return(false)); + EXPECT_CALL(view(), Show).Times(0); + EXPECT_CALL(dismissal_callback(), Run()) + .WillOnce(base::test::RunClosure(run_loop.QuitClosure())); + + all_passwords_controller()->Show(); + run_loop.Run(); +} + +TEST_F(AllPasswordsBottomSheetControllerTest, DoesNotShowWhenOriginChanged) { + base::RunLoop run_loop; + url::Origin new_origin = url::Origin::Create(GURL("https://different.com")); + driver().SetLastCommittedOrigin(new_origin); + + EXPECT_CALL(view(), Show).Times(0); + EXPECT_CALL(dismissal_callback(), Run()) + .WillOnce(base::test::RunClosure(run_loop.QuitClosure())); + + all_passwords_controller()->Show(); + run_loop.Run(); +} + +TEST_F(AllPasswordsBottomSheetControllerTest, DoesNotFillWhenOriginChanged) { + url::Origin new_origin = url::Origin::Create(GURL("https://different.com")); + driver().SetLastCommittedOrigin(new_origin); + + EXPECT_CALL(driver(), FillIntoFocusedField).Times(0); + EXPECT_CALL(dismissal_callback(), Run()); + + all_passwords_controller()->OnCredentialSelected( + kUsername1, kPassword, RequestsToFillPassword(false)); +} + +TEST_F(AllPasswordsBottomSheetControllerTest, + GetFrameOriginUrlReturnsOriginUrlWhenDriverIsNull) { + url::Origin expected_origin = + url::Origin::Create(GURL("https://example.com/login")); + auto local_driver = std::make_unique<MockPasswordManagerDriver>(); + local_driver->SetLastCommittedOrigin(expected_origin); + createAllPasswordsController(FocusedFieldType::kFillablePasswordField, + local_driver->AsWeakPtr()); + + local_driver.reset(); + EXPECT_EQ(expected_origin.GetURL(), + all_passwords_controller()->GetFrameOriginUrl()); +} diff --git a/components/password_manager/content/browser/content_password_manager_driver_unittest.cc b/components/password_manager/content/browser/content_password_manager_driver_unittest.cc index 57c5646..0bf9e23 100644 --- a/components/password_manager/content/browser/content_password_manager_driver_unittest.cc +++ b/components/password_manager/content/browser/content_password_manager_driver_unittest.cc @@ -12,6 +12,7 @@ #include "base/memory/raw_ptr.h" #include "base/run_loop.h" #include "base/strings/utf_string_conversions.h" +#include "base/test/gmock_callback_support.h" #include "base/test/metrics/histogram_tester.h" #include "base/test/scoped_feature_list.h" #include "components/autofill/content/common/mojom/autofill_agent.mojom.h" @@ -562,4 +563,14 @@ EXPECT_TRUE(mid_driver.HasCrossOriginAncestor()); } +TEST_F(ContentPasswordManagerDriverTest, FillIntoFocusedField) { + base::RunLoop run_loop; + ContentPasswordManagerDriver driver(main_rfh(), &password_manager_client_); + EXPECT_CALL(fake_agent_, + FillIntoFocusedField(true, std::u16string(u"secret"))) + .WillOnce(base::test::RunClosure(run_loop.QuitClosure())); + driver.FillIntoFocusedField(/*is_password=*/true, u"secret"); + run_loop.Run(); +} + } // namespace password_manager
Loading diff…
Regression Test / PoC
shipped with the fix
diff --git a/chrome/browser/password_manager/android/all_passwords_bottom_sheet_controller_unittest.cc b/chrome/browser/password_manager/android/all_passwords_bottom_sheet_controller_unittest.cc
index ed788ccd..917675e7 100644
--- a/chrome/browser/password_manager/android/all_passwords_bottom_sheet_controller_unittest.cc
+++ b/chrome/browser/password_manager/android/all_passwords_bottom_sheet_controller_unittest.cc
@@ -6,6 +6,7 @@
#include "base/android/device_info.h"
#include "base/memory/raw_ptr.h"
+#include "base/run_loop.h"
#include "base/test/gmock_callback_support.h"
#include "base/test/mock_callback.h"
#include "base/test/scoped_feature_list.h"
@@ -66,10 +67,30 @@
class MockPasswordManagerDriver
: public password_manager::StubPasswordManagerDriver {
public:
+ MockPasswordManagerDriver() {
+ ON_CALL(*this, CanShowAutofillUi).WillByDefault(testing::Return(true));
+ ON_CALL(*this, GetLastCommittedOrigin)
+ .WillByDefault(testing::ReturnRef(origin_));
+ ON_CALL(*this, GetLastCommittedURL).WillByDefault(testing::ReturnRef(url_));
+ }
+
MOCK_METHOD(void,
FillIntoFocusedField,
(bool, const std::u16string&),
(override));
+ MOCK_METHOD(bool, CanShowAutofillUi, (), (const, override));
+ MOCK_METHOD(const url::Origin&,
+ GetLastCommittedOrigin,
+ (),
+ (const, override));
+ MOCK_METHOD(const GURL&, GetLastCommittedURL, (), (const, override));
+
+ void SetLastCommittedOrigin(const url::Origin& origin) { origin_ = origin; }
+ void SetLastCommittedURL(const GURL& url) { url_ = url; }
+
+ private:
+ url::Origin origin_;
+ GURL url_;
};
class MockAllPasswordsBottomSheetView : public AllPasswordsBottomSheetView {
@@ -139,20 +160,26 @@
}
void createAllPasswordsController(
- autofill::mojom::FocusedFieldType focused_field_type) {
+ autofill::mojom::FocusedFieldType focused_field_type,
+ base::WeakPtr<password_manager::PasswordManagerDriver> driver) {
std::unique_ptr<MockAllPasswordsBottomSheetView> mock_view_unique_ptr =
std::make_unique<MockAllPasswordsBottomSheetView>();
mock_view_ = mock_view_unique_ptr.get();
all_passwords_controller_ =
std::make_unique<AllPasswordsBottomSheetController>(
base::PassKey<AllPasswordsBottomSheetControllerTest>(),
- web_contents(), std::move(mock_view_unique_ptr),
- driver_.AsWeakPtr(), profile_store_.get(), account_store_.get(),
+ web_contents(), std::move(mock_view_unique_ptr), driver,
+ profile_store_.get(), account_store_.get(),
dissmissal_callback_.Get(), focused_field_type,
mock_pwd_manager_client_.get(),
mock_pwd_reuse_detection_manager_client_.get());
}
+ void createAllPasswordsController(
+ autofill::mojom::FocusedFieldType focused_field_type) {
+ createAllPasswordsController(focused_field_type, driver_.AsWeakPtr());
+ }
+
void TearDown() override {
profile_store_->ShutdownOnUIThread();
if (account_store_) {
@@ -458,3 +485,67 @@
// Show method uses the store which has async work.
RunUntilIdle();
}
+
+TEST_F(AllPasswordsBottomSheetControllerTest, DoesNotShowWhenDriverIsNull) {
+ base::RunLoop run_loop;
+ auto local_driver = std::make_unique<MockPasswordManagerDriver>();
+ createAllPasswordsController(FocusedFieldType::kFillablePasswordField,
+ local_driver->AsWeakPtr());
+ local_driver.reset();
+
+ EXPECT_CALL(view(), Show).Times(0);
+ EXPECT_CALL(dismissal_callback(), Run())
+ .WillOnce(base::test::RunClosure(run_loop.QuitClosure()));
+ all_passwords_controller()->Show();
+ run_loop.Run();
+}
+
+TEST_F(AllPasswordsBottomSheetControllerTest,
+ DoesNotShowWhenCannotShowAutofillUi) {
+ base::RunLoop run_loop;
+ EXPECT_CALL(driver(), CanShowAutofillUi).WillRepeatedly(Return(false));
+ EXPECT_CALL(view(), Show).Times(0);
+ EXPECT_CALL(dismissal_callback(), Run())
+ .WillOnce(base::test::RunClosure(run_loop.QuitClosure()));
+
+ all_passwords_controller()->Show();
+ run_loop.Run();
+}
+
+TEST_F(AllPasswordsBottomSheetControllerTest, DoesNotShowWhenOriginChanged) {
+ base::RunLoop run_loop;
+ url::Origin new_origin = url::Origin::Create(GURL("https://different.com"));
+ driver().SetLastCommittedOrigin(new_origin);
+
+ EXPECT_CALL(view(), Show).Times(0);
+ EXPECT_CALL(dismissal_callback(), Run())
+ .WillOnce(base::test::RunClosure(run_loop.QuitClosure()));
+
+ all_passwords_controller()->Show();
+ run_loop.Run();
+}
+
+TEST_F(AllPasswordsBottomSheetControllerTest, DoesNotFillWhenOriginChanged) {
+ url::Origin new_origin = url::Origin::Create(GURL("https://different.com"));
+ driver().SetLastCommittedOrigin(new_origin);
+
+ EXPECT_CALL(driver(), FillIntoFocusedField).Times(0);
+ EXPECT_CALL(dismissal_callback(), Run());
+
+ all_passwords_controller()->OnCredentialSelected(
+ kUsername1, kPassword, RequestsToFillPassword(false));
+}
+
+TEST_F(AllPasswordsBottomSheetControllerTest,
+ GetFrameOriginUrlReturnsOriginUrlWhenDriverIsNull) {
+ url::Origin expected_origin =
+ url::Origin::Create(GURL("https://example.com/login"));
+ auto local_driver = std::make_unique<MockPasswordManagerDriver>();
+ local_driver->SetLastCommittedOrigin(expected_origin);
+ createAllPasswordsController(FocusedFieldType::kFillablePasswordField,
+ local_driver->AsWeakPtr());
+
+ local_driver.reset();
+ EXPECT_EQ(expected_origin.GetURL(),
+ all_passwords_controller()->GetFrameOriginUrl());
+}
diff --git a/components/password_manager/content/browser/content_password_manager_driver_unittest.cc b/components/password_manager/content/browser/content_password_manager_driver_unittest.cc
index 57c5646..0bf9e23 100644
--- a/components/password_manager/content/browser/content_password_manager_driver_unittest.cc
+++ b/components/password_manager/content/browser/content_password_manager_driver_unittest.cc
@@ -12,6 +12,7 @@
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
+#include "base/test/gmock_callback_support.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "components/autofill/content/common/mojom/autofill_agent.mojom.h"
@@ -562,4 +563,14 @@
EXPECT_TRUE(mid_driver.HasCrossOriginAncestor());
}
+TEST_F(ContentPasswordManagerDriverTest, FillIntoFocusedField) {
+ base::RunLoop run_loop;
+ ContentPasswordManagerDriver driver(main_rfh(), &password_manager_client_);
+ EXPECT_CALL(fake_agent_,
+ FillIntoFocusedField(true, std::u16string(u"secret")))
+ .WillOnce(base::test::RunClosure(run_loop.QuitClosure()));
+ driver.FillIntoFocusedField(/*is_password=*/true, u"secret");
+ run_loop.Run();
+}
+
} // namespace password_manager
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