CVE-2026-17731
Overview
69e11c8af383), which an audit found was a preparatory/helper change, not the fix. This page now points to the commit whose diff removes the bug. No root-cause analysis is published for this CVE.Changed Functions
| Function | Change | Notes |
|---|---|---|
ifchrome/browser/touch_to_fill/autofill/android/touch_to_fill_payment_method_controller_impl.cc |
modified | |
TEST_Fchrome/browser/touch_to_fill/autofill/android/touch_to_fill_payment_method_controller_impl_unittest.cc |
modified |
Files Changed
chrome/browser/touch_to_fill/autofill/android/BUILD.gnchrome/browser/touch_to_fill/autofill/android/touch_to_fill_payment_method_controller_impl.ccchrome/browser/touch_to_fill/autofill/android/touch_to_fill_payment_method_controller_impl.hchrome/browser/touch_to_fill/autofill/android/touch_to_fill_payment_method_controller_impl_unittest.cc
Patch
diff --git a/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_payment_method_controller_impl.cc b/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_payment_method_controller_impl.cc
index 4ea1d75..fd6c9f3 100644
--- a/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_payment_method_controller_impl.cc
+++ b/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_payment_method_controller_impl.cc
@@ -17,6 +17,8 @@
#include "chrome/browser/android/resource_mapper.h"
#include "chrome/browser/touch_to_fill/autofill/android/touch_to_fill_delegate_android_impl.h"
#include "chrome/browser/touch_to_fill/autofill/android/touch_to_fill_payment_method_view.h"
+#include "chrome/browser/ui/android/tab_model/tab_model.h"
+#include "chrome/browser/ui/android/tab_model/tab_model_list.h"
#include "chrome/browser/ui/autofill/autofill_suggestion_controller_utils.h"
#include "chrome/browser/ui/autofill/payments/android_bnpl_ui_delegate.h"
#include "components/autofill/content/browser/content_autofill_client.h"
@@ -31,6 +33,7 @@
#include "components/autofill/core/browser/payments/payments_util.h"
#include "components/autofill/core/browser/suggestions/suggestion.h"
#include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/web_contents.h"
#include "ui/android/window_android.h"
@@ -97,12 +100,22 @@
return false;
}
+ // The WebContents may have lost the focus because, for example, a new tab has
+ // just been opened.
+ // Beware that this check only works as intended before TTF is shown because
+ // the bottom sheet steals the focus from the RenderWidgetHostView.
+ if (auto* rwhv = web_contents()->GetRenderWidgetHostView();
+ !rwhv || !rwhv->HasFocus()) {
+ return false;
+ }
+
if (IsPointerLocked(web_contents())) {
return false;
}
// The bottom sheet steals the focus from the WebContents, so we cannot rely
- // on AutofillPopupHideHelper's focus handling.
+ // on AutofillPopupHideHelper's focus handling. Instead, we check if
+ // `IsActiveWebContents()` in the event handlers below.
AutofillPopupHideHelper::HidingParams params = {
.hide_on_web_contents_lost_focus = false};
@@ -121,6 +134,11 @@
return true;
}
+bool TouchToFillPaymentMethodControllerImpl::IsActiveWebContents() {
+ TabModel* tab_model = TabModelList::GetTabModelForWebContents(web_contents());
+ return tab_model && tab_model->GetActiveWebContents() == web_contents();
+}
+
bool TouchToFillPaymentMethodControllerImpl::ShowPaymentMethods(
std::unique_ptr<TouchToFillPaymentMethodView> view,
base::WeakPtr<TouchToFillDelegate> delegate,
@@ -395,55 +413,69 @@
JNIEnv* env,
const std::string& unique_id,
bool is_virtual) {
- if (delegate_) {
- delegate_->CreditCardSuggestionSelected(unique_id, is_virtual);
+ if (!IsActiveWebContents() || !delegate_) {
+ Hide();
+ return;
}
+ delegate_->CreditCardSuggestionSelected(unique_id, is_virtual);
}
void TouchToFillPaymentMethodControllerImpl::BnplSuggestionSelected(
JNIEnv* env,
std::optional<int64_t> extracted_amount) {
- if (delegate_) {
- delegate_->BnplSuggestionSelected(extracted_amount);
+ if (!IsActiveWebContents() || !delegate_) {
+ Hide();
+ return;
}
+ delegate_->BnplSuggestionSelected(extracted_amount);
}
void TouchToFillPaymentMethodControllerImpl::LocalIbanSuggestionSelected(
JNIEnv* env,
const std::string& guid) {
- if (delegate_) {
- delegate_->IbanSuggestionSelected(Iban::Guid(guid));
+ if (!IsActiveWebContents() || !delegate_) {
+ Hide();
+ return;
}
+ delegate_->IbanSuggestionSelected(Iban::Guid(guid));
}
void TouchToFillPaymentMethodControllerImpl::ServerIbanSuggestionSelected(
JNIEnv* env,
long instrument_id) {
- if (delegate_) {
- delegate_->IbanSuggestionSelected(Iban::InstrumentId(instrument_id));
+ if (!IsActiveWebContents() || !delegate_) {
+ Hide();
+ return;
}
+ delegate_->IbanSuggestionSelected(Iban::InstrumentId(instrument_id));
}
void TouchToFillPaymentMethodControllerImpl::LoyaltyCardSuggestionSelected(
JNIEnv* env,
const LoyaltyCard& loyalty_card) {
- if (delegate_) {
- delegate_->LoyaltyCardSuggestionSelected(loyalty_card);
+ if (!IsActiveWebContents() || !delegate_) {
+ Hide();
+ return;
}
+ delegate_->LoyaltyCardSuggestionSelected(loyalty_card);
}
void TouchToFillPaymentMethodControllerImpl::OnBnplIssuerSuggestionSelected(
JNIEnv* env,
const std::string& issuer_id) {
- if (delegate_) {
- delegate_->OnBnplIssuerSuggestionSelected(issuer_id);
+ if (!IsActiveWebContents() || !delegate_) {
+ Hide();
+ return;
}
+ delegate_->OnBnplIssuerSuggestionSelected(issuer_id);
}
void TouchToFillPaymentMethodControllerImpl::OnBnplTosAccepted(JNIEnv* env) {
- if (delegate_) {
- delegate_->OnBnplTosAccepted();
+ if (!IsActiveWebContents() || !delegate_) {
+ Hide();
+ return;
}
+ delegate_->OnBnplTosAccepted();
}
int TouchToFillPaymentMethodControllerImpl::GetJavaResourceId(
diff --git a/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_payment_method_controller_impl.h b/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_payment_method_controller_impl.h
index d13f469..3dd9892d 100644
--- a/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_payment_method_controller_impl.h
+++ b/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_payment_method_controller_impl.h
@@ -129,6 +129,12 @@
private:
bool InitHideHelper(TouchToFillDelegate& delegate);
+
+ // The controller must ignore user actions if the associated WebContents is
+ // not active anymore. This is to handle race conditions between, for example,
+ // a new tab being opened and TTF being shown.
+ bool IsActiveWebContents();
+
content::WebContents* web_contents();
// Observes creation of ContentAutofillDrivers to inject a
diff --git a/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_payment_method_controller_impl_unittest.cc b/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_payment_method_controller_impl_unittest.cc
index 100fb31..00355f5 100644
--- a/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_payment_method_controller_impl_unittest.cc
+++ b/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_payment_method_controller_impl_unittest.cc
@@ -15,6 +15,8 @@
#include "chrome/browser/touch_to_fill/autofill/android/touch_to_fill_delegate_android_impl.h"
#include "chrome/browser/touch_to_fill/autofill/android/touch_to_fill_payment_method_view.h"
#include "chrome/browser/touch_to_fill/autofill/android/touch_to_fill_payment_method_view_controller.h"
+#include "chrome/browser/ui/android/tab_model/tab_model_list.h"
+#include "chrome/browser/ui/android/tab_model/tab_model_test_helper.h"
#include "chrome/browser/ui/autofill/payments/android_bnpl_ui_delegate.h"
#include "chrome/test/base/chrome_render_view_host_test_harness.h"
#include "components/autofill/content/browser/test_autofill_client_injector.h"
@@ -147,6 +149,10 @@
(std::optional<int64_t> extracted_amount),
(override));
MOCK_METHOD(void,
+ IbanSuggestionSelected,
+ ((std::variant<Iban::Guid, Iban::InstrumentId> backend_id)),
+ (override));
+ MOCK_METHOD(void,
OnDismissed,
(bool dismissed_by_user, bool should_reshow),
(override));
@@ -191,6 +197,12 @@
void SetUp() override {
ChromeRenderViewHostTestHarness::SetUp();
+
+ tab_model_.emplace(profile());
+ TabModelList::AddTabModel(&*tab_model_);
+ tab_model_->SetWebContentsList({web_contents()});
+ tab_model_->SetIsActiveModel(true);
+
NavigateAndCommit(GURL("about:blank"));
FocusWebContentsOnMainFrame();
ASSERT_TRUE(web_contents()->GetFocusedFrame());
@@ -200,6 +212,14 @@
mock_view_ = std::make_unique<MockTouchToFillPaymentMethodViewImpl>();
}
+ void AddSecondWebContents() {
+ CHECK(!second_web_contents_);
+ second_web_contents_ = content::WebContents::Create(
+ content::WebContents::CreateParams(profile()));
+ tab_model_->SetWebContentsList(
+ {second_web_contents_.get(), web_contents()});
+ }
+
void SetUpIbanFormField() {
some_form_data_ = autofill::test::CreateTestIbanFormData();
some_form_ = some_form_data_.global_id();
@@ -214,6 +234,8 @@
void TearDown() override {
mock_view_.reset();
+ second_web_contents_.reset();
+ TabModelList::RemoveTabModel(&*tab_model_);
ChromeRenderViewHostTestHarness::TearDown();
}
@@ -301,6 +323,10 @@
autofill_client_injector_;
TestAutofillManagerInjector<TestBrowserAutofillManager>
autofill_manager_injector_;
+
+ std::optional<TestTabModel> tab_model_;
+ std::unique_ptr<content::WebContents> second_web_contents_;
+
FormData some_form_data_ =
autofill::test::CreateTestCreditCardFormData(/*is_https=*/true,
/*use_month_type=*/false);
@@ -608,6 +634,30 @@
extracted_amount);
}
+// Tests that accepting a (credit card) suggestion on TTF from an inactive tab
+// is a no-op.
+TEST_F(TouchToFillPaymentMethodControllerImplTest,
+ CreditCardSuggestionSelected_AbortsIfInactiveWebContents) {
+ MockTouchToFillPaymentMethodViewImpl* raw_mock_view = mock_view_.get();
+
+ OnBeforeAskForValuesToFill();
+ payment_method_controller().ShowPaymentMethods(
+ std::move(mock_view_), ttf_delegate().GetWeakPointer(), suggestions_);
+ OnAfterAskForValuesToFill();
+
+ AddSecondWebContents();
+
+ // Expect that Hide() is called on the view, and the delegate is NOT called.
+ EXPECT_CALL(*raw_mock_view, Hide());
+ EXPECT_CALL(ttf_delegate(), CreditCardSuggestionSelected).Times(0);
+
+ payment_method_controller().CreditCardSuggestionSelected(
+ /*env=*/nullptr, "some_guid", /*is_virtual=*/false);
+
+ testing::Mock::VerifyAndClearExpectations(raw_mock_view);
+ testing::Mock::VerifyAndClearExpectations(&ttf_delegate());
+}
+
TEST_F(TouchToFillPaymentMethodControllerImplTest, ShowErrorScreenOnNewView) {
const std::u16string title = u"Error Title";
const std::u16string description = u"Error Description";
diff --git a/chrome/browser/touch_to_fill/autofill/android/BUILD.gn b/chrome/browser/touch_to_fill/autofill/android/BUILD.gn
index 38850beb..9252b1b 100644
--- a/chrome/browser/touch_to_fill/autofill/android/BUILD.gn
+++ b/chrome/browser/touch_to_fill/autofill/android/BUILD.gn
@@ -56,6 +56,7 @@
"//base",
"//chrome/android:chrome_jni_headers",
"//chrome/browser/android:resource_mapper",
+ "//chrome/browser/ui/android/tab_model",
"//chrome/browser/ui/autofill",
"//chrome/browser/ui/autofill/payments",
"//components/affiliations/core/browser:affiliations",
@@ -82,6 +83,7 @@
":public",
"//base",
"//base/test:test_support",
+ "//chrome/browser/ui/android/tab_model:test_support",
"//chrome/browser/ui/autofill/payments",
"//chrome/test:test_support",
"//components/autofill/content/browser",
Regression Test / PoC
diff --git a/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_payment_method_controller_impl_unittest.cc b/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_payment_method_controller_impl_unittest.cc
index 100fb31..00355f5 100644
--- a/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_payment_method_controller_impl_unittest.cc
+++ b/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_payment_method_controller_impl_unittest.cc
@@ -15,6 +15,8 @@
#include "chrome/browser/touch_to_fill/autofill/android/touch_to_fill_delegate_android_impl.h"
#include "chrome/browser/touch_to_fill/autofill/android/touch_to_fill_payment_method_view.h"
#include "chrome/browser/touch_to_fill/autofill/android/touch_to_fill_payment_method_view_controller.h"
+#include "chrome/browser/ui/android/tab_model/tab_model_list.h"
+#include "chrome/browser/ui/android/tab_model/tab_model_test_helper.h"
#include "chrome/browser/ui/autofill/payments/android_bnpl_ui_delegate.h"
#include "chrome/test/base/chrome_render_view_host_test_harness.h"
#include "components/autofill/content/browser/test_autofill_client_injector.h"
@@ -147,6 +149,10 @@
(std::optional<int64_t> extracted_amount),
(override));
MOCK_METHOD(void,
+ IbanSuggestionSelected,
+ ((std::variant<Iban::Guid, Iban::InstrumentId> backend_id)),
+ (override));
+ MOCK_METHOD(void,
OnDismissed,
(bool dismissed_by_user, bool should_reshow),
(override));
@@ -191,6 +197,12 @@
void SetUp() override {
ChromeRenderViewHostTestHarness::SetUp();
+
+ tab_model_.emplace(profile());
+ TabModelList::AddTabModel(&*tab_model_);
+ tab_model_->SetWebContentsList({web_contents()});
+ tab_model_->SetIsActiveModel(true);
+
NavigateAndCommit(GURL("about:blank"));
FocusWebContentsOnMainFrame();
ASSERT_TRUE(web_contents()->GetFocusedFrame());
@@ -200,6 +212,14 @@
mock_view_ = std::make_unique<MockTouchToFillPaymentMethodViewImpl>();
}
+ void AddSecondWebContents() {
+ CHECK(!second_web_contents_);
+ second_web_contents_ = content::WebContents::Create(
+ content::WebContents::CreateParams(profile()));
+ tab_model_->SetWebContentsList(
+ {second_web_contents_.get(), web_contents()});
+ }
+
void SetUpIbanFormField() {
some_form_data_ = autofill::test::CreateTestIbanFormData();
some_form_ = some_form_data_.global_id();
@@ -214,6 +234,8 @@
void TearDown() override {
mock_view_.reset();
+ second_web_contents_.reset();
+ TabModelList::RemoveTabModel(&*tab_model_);
ChromeRenderViewHostTestHarness::TearDown();
}
@@ -301,6 +323,10 @@
autofill_client_injector_;
TestAutofillManagerInjector<TestBrowserAutofillManager>
autofill_manager_injector_;
+
+ std::optional<TestTabModel> tab_model_;
+ std::unique_ptr<content::WebContents> second_web_contents_;
+
FormData some_form_data_ =
autofill::test::CreateTestCreditCardFormData(/*is_https=*/true,
/*use_month_type=*/false);
@@ -608,6 +634,30 @@
extracted_amount);
}
+// Tests that accepting a (credit card) suggestion on TTF from an inactive tab
+// is a no-op.
+TEST_F(TouchToFillPaymentMethodControllerImplTest,
+ CreditCardSuggestionSelected_AbortsIfInactiveWebContents) {
+ MockTouchToFillPaymentMethodViewImpl* raw_mock_view = mock_view_.get();
+
+ OnBeforeAskForValuesToFill();
+ payment_method_controller().ShowPaymentMethods(
+ std::move(mock_view_), ttf_delegate().GetWeakPointer(), suggestions_);
+ OnAfterAskForValuesToFill();
+
+ AddSecondWebContents();
+
+ // Expect that Hide() is called on the view, and the delegate is NOT called.
+ EXPECT_CALL(*raw_mock_view, Hide());
+ EXPECT_CALL(ttf_delegate(), CreditCardSuggestionSelected).Times(0);
+
+ payment_method_controller().CreditCardSuggestionSelected(
+ /*env=*/nullptr, "some_guid", /*is_virtual=*/false);
+
+ testing::Mock::VerifyAndClearExpectations(raw_mock_view);
+ testing::Mock::VerifyAndClearExpectations(&ttf_delegate());
+}
+
TEST_F(TouchToFillPaymentMethodControllerImplTest, ShowErrorScreenOnNewView) {
const std::u16string title = u"Error Title";
const std::u16string description = u"Error Description";
Original Bug Report
Debit/Credit Card Autofill Leakage via Cross-Origin Rendering in Chrome 142.0.7444.171 for Android
Steps to reproduce the problem
- Visit https://attacker.hackerbro.net/exploit.html in Chrome for Android.
- Tap the Credit Card option and then tap Verify Info.
- The page redirects to https://splendorous-truffle-da6efb.netlify.app/card.html (a different origin).
- After the redirect, Chrome shows the stored card prompt on this cross-origin page.
- When the user clicks OK to populate the stored card, the card details are not filled into the currently visible page.
- Instead, Chrome autofills the stored card information into the attacker-controlled origin, resulting in cross-origin autofill leakage.
Problem Description
- In Chrome version 142.0.7444.171, stored debit and credit cards are being rendered across origins. This cross-origin rendering exposes sensitive autofill data, allowing leakage when the card prompt is triggered on an external domain.
Impact:
- Potential exposure of users’ payment card information to malicious websites via autofill.
Recommendation:
- Restrict cross-origin access to stored payment methods and enforce same-origin policies for autofill prompts.
Summary
Debit/Credit Card Autofill Leakage via Cross-Origin Rendering in Chrome 142.0.7444.171 for Android
Custom Questions
Reporter credit:
Manojkumar Jaganathan (https://www.linkedin.com/in/manojkumar-j-7ba35b202/) Aka TheWhiteEvil (https://hackerone.com/the-white-evil) with HackerBro Technologies
Additional Data
Category: Security
Chrome Channel: Stable
Regression: No \