Chrome · Autofill
CVE-2026-17731
Logic Error in Autofill
Overview
Medium
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
BindRepeatingchrome/browser/touch_to_fill/autofill/android/touch_to_fill_payment_method_controller_impl.cc |
modified |
Files Changed
chrome/browser/touch_to_fill/autofill/android/BUILD.gnchrome/browser/touch_to_fill/autofill/android/touch_to_fill_delegate_android_impl.ccchrome/browser/touch_to_fill/autofill/android/touch_to_fill_delegate_android_impl.hchrome/browser/touch_to_fill/autofill/android/touch_to_fill_payment_method_controller_impl.cc
Patch
From 69e11c8af383a4e30ff3af85fccc7b1a9a73caab Mon Sep 17 00:00:00 2001 From: Christoph Schwering <[email protected]> Date: Tue, 09 Jun 2026 05:23:31 -0700 Subject: [PATCH] [Autofill] Migrate TTFPMControllerImpl to AutofillPopupHideHelper This CL replaces TouchToFillPaymentMethodControllerImpl's custom code that closes TTF on navigations and RFH destructions. This brings along small behaviour changes that make it consistent with other controllers: check for pointer lock, ancestor-check between the focused frame and the form's frame, visibility tests. Bug: 463551850 Change-Id: I0fcd955e7bbd4579698adaf9645a79f3d9206c1c Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7909061 Reviewed-by: Jan Keitel <[email protected]> Commit-Queue: Christoph Schwering <[email protected]> Cr-Commit-Position: refs/heads/main@{#1643877} --- diff --git a/chrome/browser/touch_to_fill/autofill/android/BUILD.gn b/chrome/browser/touch_to_fill/autofill/android/BUILD.gn index c8a1102..38850beb 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/autofill", "//chrome/browser/ui/autofill/payments", "//components/affiliations/core/browser:affiliations", "//components/autofill/android:keyboard_suppressor", diff --git a/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_delegate_android_impl.cc b/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_delegate_android_impl.cc index 03b6be3..4102487 100644 --- a/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_delegate_android_impl.cc +++ b/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_delegate_android_impl.cc @@ -131,6 +131,10 @@ HideTouchToFill(); } +BrowserAutofillManager& TouchToFillDelegateAndroidImpl::GetAutofillManager() { + return *manager_; +} + TouchToFillDelegateAndroidImpl::DryRunResult TouchToFillDelegateAndroidImpl::DryRun(FormGlobalId form_id, FieldGlobalId field_id) { diff --git a/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_delegate_android_impl.h b/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_delegate_android_impl.h index 64502ef..f69927b 100644 --- a/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_delegate_android_impl.h +++ b/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_delegate_android_impl.h @@ -107,6 +107,8 @@ const TouchToFillDelegateAndroidImpl&) = delete; ~TouchToFillDelegateAndroidImpl() override; + BrowserAutofillManager& GetAutofillManager() override; + // Checks whether TTF is eligible for the given web form data. // Only if this is true, the controller will show the view. bool IntendsToShowTouchToFill(FormGlobalId form_id, 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 842aac7..4ea1d75 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 @@ -12,10 +12,12 @@ #include "base/android/jni_string.h" #include "base/containers/span.h" #include "base/functional/callback.h" +#include "base/functional/callback_helpers.h" #include "base/strings/strcat.h" #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/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" #include "components/autofill/content/browser/content_autofill_driver.h" @@ -29,6 +31,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/web_contents.h" #include "ui/android/window_android.h" // Must come after all headers that specialize FromJniType() / ToJniType(). @@ -46,8 +49,7 @@ TouchToFillPaymentMethodControllerImpl::TouchToFillPaymentMethodControllerImpl( ContentAutofillClient* autofill_client) - : content::WebContentsObserver(&autofill_client->GetWebContents()), - keyboard_suppressor_( + : keyboard_suppressor_( autofill_client, base::BindRepeating([](AutofillManager& manager) { return GetDelegate(manager) && @@ -70,6 +72,55 @@ ResetJavaObject(); } +content::WebContents* TouchToFillPaymentMethodControllerImpl::web_contents() { + return driver_factory_observation_.GetSource()->web_contents(); +} + +bool TouchToFillPaymentMethodControllerImpl::InitHideHelper( + TouchToFillDelegate& delegate) { + // The focused frame may be a different frame than the one the delegate is + // associated with. This happens in two scenarios: + // - With frame-transcending forms: the focused frame is subframe, whose + // form has been flattened into an ancestor form. + // - With race conditions: while Autofill parsed the form, the focus may + // have moved to another frame. + // We support the case where the focused frame is a descendant of the + // `delegate_`'s frame. We observe the focused frame's RenderFrameDeleted() + // event. + content::RenderFrameHost* rfh = web_contents()->GetFocusedFrame(); + content::RenderFrameHost* delegate_rfh = + static_cast<ContentAutofillDriver&>( + delegate.GetAutofillManager().driver()) + .render_frame_host(); + + if (!rfh || !IsAncestorOf(delegate_rfh, rfh)) { + 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. + AutofillPopupHideHelper::HidingParams params = { + .hide_on_web_contents_lost_focus = false}; + + AutofillPopupHideHelper::HidingCallback hide_callback = + base::IgnoreArgs<SuggestionHidingReason>( + base::BindRepeating(&TouchToFillPaymentMethodControllerImpl::Hide, + base::Unretained(this))); + + // TODO(crbug.com/521318493): Should we hide TTF in the face of a PiP? + AutofillPopupHideHelper::PictureInPictureDetectionCallback + pip_detection_callback = base::BindRepeating([]() { return false; }); + + hide_helper_.emplace(web_contents(), rfh->GetGlobalId(), std::move(params), + std::move(hide_callback), + std::move(pip_detection_callback)); + return true; +} + bool TouchToFillPaymentMethodControllerImpl::ShowPaymentMethods( std::unique_ptr<TouchToFillPaymentMethodView> view, base::WeakPtr<TouchToFillDelegate> delegate, @@ -83,6 +134,10 @@ return false; } + if (!InitHideHelper(*delegate)) { + return false; + } + if (!view->ShowPaymentMethods( this, suggestions, payments::TouchToFillDisplayOptions{ @@ -110,6 +165,10 @@ return false; } + if (!InitHideHelper(*delegate)) { + return false; + } + if (!view->ShowIbans(this, ibans_to_suggest)) { ResetJavaObject(); return false; @@ -126,7 +185,7 @@ base::span<const LoyaltyCard> affiliated_loyalty_cards, base::span<const LoyaltyCard> all_loyalty_cards, bool first_time_usage) { - // TODO(crbug.com/404437211): Unify `ShowX()` methods to avoid code + // TODO(crbug.com/521032396): Unify `ShowX()` methods to avoid code // duplication. if (!keyboard_suppressor_.is_suppressing()) { return false; @@ -137,6 +196,10 @@ return false; } + if (!InitHideHelper(*delegate)) { + return false; + } + if (!view->ShowAffiliatedLoyaltyCards(this, affiliated_loyalty_cards, all_loyalty_cards, first_time_usage)) { ResetJavaObject(); @@ -157,6 +220,10 @@ return false; } + if (!InitHideHelper(*delegate)) { + return false; + }
Loading diff…
Regression Test / PoC
shipped with the fix
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 bdc0245..100fb31 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
@@ -192,6 +192,8 @@
void SetUp() override {
ChromeRenderViewHostTestHarness::SetUp();
NavigateAndCommit(GURL("about:blank"));
+ FocusWebContentsOnMainFrame();
+ ASSERT_TRUE(web_contents()->GetFocusedFrame());
autofill_manager().set_touch_to_fill_delegate(
std::make_unique<MockTouchToFillDelegateAndroidImpl>(
&autofill_manager()));
diff --git a/components/autofill/core/browser/foundations/browser_autofill_manager_unittest.cc b/components/autofill/core/browser/foundations/browser_autofill_manager_unittest.cc
index bd7f1db..247f5067 100644
--- a/components/autofill/core/browser/foundations/browser_autofill_manager_unittest.cc
+++ b/components/autofill/core/browser/foundations/browser_autofill_manager_unittest.cc
@@ -204,6 +204,7 @@
using ::testing::Property;
using ::testing::Ref;
using ::testing::Return;
+using ::testing::ReturnRef;
using ::testing::SaveArg;
using ::testing::UnorderedElementsAre;
using ::testing::VariantWith;
@@ -632,6 +633,7 @@
static std::unique_ptr<MockTouchToFillDelegate> Create(
BrowserAutofillManager* manager) {
auto delegate = std::make_unique<NiceMock<MockTouchToFillDelegate>>();
+ ON_CALL(*delegate, GetAutofillManager()).WillByDefault(ReturnRef(*manager));
ON_CALL(*delegate, IsShowingTouchToFill()).WillByDefault(Return(false));
return delegate;
}
@@ -641,6 +643,7 @@
MockTouchToFillDelegate& operator=(const MockTouchToFillDelegate&) = delete;
~MockTouchToFillDelegate() override = default;
+ MOCK_METHOD(BrowserAutofillManager&, GetAutofillManager, (), (override));
MOCK_METHOD(bool,
IntendsToShowTouchToFill,
(FormGlobalId, FieldGlobalId),
Loading diff…
Original Bug Report
reported by [email protected]
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 \
References
On This Page