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 <ioanap@chromium.org>
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 <kazinova@google.com>
Commit-Queue: Ioana Treib <ioanap@chromium.org>
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