CVE-2026-11001
Overview
Files Changed
chrome/browser/ui/views/payments/payment_sheet_view_controller.ccchrome/browser/ui/views/payments/payment_sheet_view_controller_browsertest.cc
Patch
From baccdff3d46464d7c9e63483d28632d3db19e107 Mon Sep 17 00:00:00 2001 From: Slobodan Pejic <[email protected]> Date: Tue, 14 Apr 2026 07:13:19 -0700 Subject: [PATCH] Protect against enterjacking on the Payment Sheet This CL removes the keyboard event exception (`allow_key_events=false`) in `PaymentSheetViewController::PossiblyIgnorePrimaryButtonPress`. This ensures that both mouse clicks and key presses are subject to the same double-click safety interval upon the dialog being shown. Bug: 493691489 Change-Id: I4a8b242e703b25ae6774a47d15c65a39812fd56f Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7756830 Reviewed-by: Stephen McGruer <[email protected]> Commit-Queue: Slobodan Pejic <[email protected]> Cr-Commit-Position: refs/heads/main@{#1614426} --- diff --git a/chrome/browser/ui/views/payments/payment_sheet_view_controller.cc b/chrome/browser/ui/views/payments/payment_sheet_view_controller.cc index 0476908..6f1c437 100644 --- a/chrome/browser/ui/views/payments/payment_sheet_view_controller.cc +++ b/chrome/browser/ui/views/payments/payment_sheet_view_controller.cc @@ -935,7 +935,7 @@ PaymentRequestSheetController::ButtonCallback callback, const ui::Event& event) { if (input_protector_->IsPossiblyUnintendedInteraction( - event, /*allow_key_events=*/true)) { + event, /*allow_key_events=*/false)) { return; } callback.Run(event); diff --git a/chrome/browser/ui/views/payments/payment_sheet_view_controller_browsertest.cc b/chrome/browser/ui/views/payments/payment_sheet_view_controller_browsertest.cc index 1290ad7..b4a4318e 100644 --- a/chrome/browser/ui/views/payments/payment_sheet_view_controller_browsertest.cc +++ b/chrome/browser/ui/views/payments/payment_sheet_view_controller_browsertest.cc @@ -5,6 +5,7 @@ #include "chrome/browser/ui/views/payments/payment_sheet_view_controller.h" #include "base/strings/utf_string_conversions.h" +#include "base/test/scoped_command_line.h" #include "chrome/browser/ui/views/payments/payment_request_browsertest_base.h" #include "chrome/browser/ui/views/payments/payment_request_dialog_view_ids.h" #include "chrome/test/base/ui_test_utils.h" @@ -20,8 +21,11 @@ #include "net/dns/mock_host_resolver.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/base/l10n/l10n_util.h" +#include "ui/events/base_event_utils.h" #include "ui/views/controls/scroll_view.h" +#include "ui/views/metrics.h" #include "ui/views/test/mock_input_event_activation_protector.h" +#include "ui/views/views_switches.h" namespace payments { @@ -61,7 +65,10 @@ // accepts all subsequent inputs. auto input_protector = std::make_unique<views::MockInputEventActivationProtector>(); - EXPECT_CALL(*input_protector, IsPossiblyUnintendedInteraction) + // Expect that `allow_key_events` is set to false to protect against + // enter-jacking. + EXPECT_CALL(*input_protector, IsPossiblyUnintendedInteraction( + testing::_, /*allow_key_events=*/false)) .WillOnce(testing::Return(true)) .WillRepeatedly(testing::Return(false));
Regression Test / PoC
diff --git a/chrome/browser/ui/views/payments/payment_sheet_view_controller_browsertest.cc b/chrome/browser/ui/views/payments/payment_sheet_view_controller_browsertest.cc
index 1290ad7..b4a4318e 100644
--- a/chrome/browser/ui/views/payments/payment_sheet_view_controller_browsertest.cc
+++ b/chrome/browser/ui/views/payments/payment_sheet_view_controller_browsertest.cc
@@ -5,6 +5,7 @@
#include "chrome/browser/ui/views/payments/payment_sheet_view_controller.h"
#include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_command_line.h"
#include "chrome/browser/ui/views/payments/payment_request_browsertest_base.h"
#include "chrome/browser/ui/views/payments/payment_request_dialog_view_ids.h"
#include "chrome/test/base/ui_test_utils.h"
@@ -20,8 +21,11 @@
#include "net/dns/mock_host_resolver.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/l10n/l10n_util.h"
+#include "ui/events/base_event_utils.h"
#include "ui/views/controls/scroll_view.h"
+#include "ui/views/metrics.h"
#include "ui/views/test/mock_input_event_activation_protector.h"
+#include "ui/views/views_switches.h"
namespace payments {
@@ -61,7 +65,10 @@
// accepts all subsequent inputs.
auto input_protector =
std::make_unique<views::MockInputEventActivationProtector>();
- EXPECT_CALL(*input_protector, IsPossiblyUnintendedInteraction)
+ // Expect that `allow_key_events` is set to false to protect against
+ // enter-jacking.
+ EXPECT_CALL(*input_protector, IsPossiblyUnintendedInteraction(
+ testing::_, /*allow_key_events=*/false))
.WillOnce(testing::Return(true))
.WillRepeatedly(testing::Return(false));
Original Bug Report
Potential Enterjacking in PaymentSheetViewController due to input protection bypass
Flapjack (go/flapjack), an LLM-powered static analysis tool, has identified the following potential security issue.
Overview: The PaymentSheetViewController potentially bypasses the 500ms input protection window for keyboard events on its primary button. This might allow an attacker to trick users into unintentionally authorizing payments or sharing personal information through a timed sequence of keystrokes. This is similar to known ’enterjacking’ issues found in other UI components.
Affected files:
chrome/browser/ui/views/payments/payment_sheet_view_controller.cc
Estimated timestamp from git blame: 2025-06-10
Overview
The PaymentSheetViewController is potentially vulnerable to a form of “enterjacking” because it explicitly allows keyboard events to bypass the InputEventActivationProtector safety window.
In chrome/browser/ui/views/payments/payment_sheet_view_controller.cc, the method PossiblyIgnorePrimaryButtonPress is responsible for handling interactions with the primary (“Pay”) button. When invoking the input protector, it passes true for allow_key_events:
void PaymentSheetViewController::PossiblyIgnorePrimaryButtonPress(
PaymentRequestSheetController::ButtonCallback callback,
const ui::Event& event) {
if (input_protector_->IsPossiblyUnintendedInteraction(
event, /*allow_key_events=*/true)) {
return;
}
callback.Run(event);
}
By passing /*allow_key_events=*/true, the implementation of the protector (ui/views/input_event_activation_protector.cc) explicitly returns false for any KeyEvent, effectively disabling the 500ms protection interval that is meant to prevent accidental activations immediately after a dialog appears or changes state.
Potential Exploitation Scenario
Because we do not currently have the capability to run a live proof-of-concept, the following steps outline a theoretical attack path based on code analysis:
- Attacker Setup: An attacker creates a malicious webpage designed to induce rapid typing from the user. For instance, a fake game or CAPTCHA that requires the user to repeatedly press the
Tab(orShift+Tab) andEnterkeys in quick succession. - Triggering the UI: While the user is actively typing, the attacker’s JavaScript silently initiates a payment request or a request for user information (e.g., via
PaymentRequest.show()). - UI Construction: The browser builds the Payment Request dialog. By design, to prevent immediate accidental
Enterpresses,PaymentRequestSheetController::GetFirstFocusedView()initially focuses the secondary (“Cancel”) button, not the primary button. - Focus Shift: The user, following the attacker’s rhythmic typing task, presses
Tab(on macOS) orShift+Tab(on non-macOS platforms, due to differences in button creation order inPaymentRequestSheetController::CreateFooterView). This keystroke shifts focus from the secondary button to the primary (“Pay”) button. - The Keypress: Continuing the rapid sequence, the user presses
Enterwhile the primary button is focused. This occurs immediately after the dialog has appeared, well within the intended 500ms safety window. - Protection Bypass: The
Enterkey event triggersPaymentSheetViewController::PossiblyIgnorePrimaryButtonPress. Becauseallow_key_eventsis hardcoded totrue,InputEventActivationProtector::IsPossiblyUnintendedInteractionevaluatesif (allow_key_events || !event.IsKeyEvent())and immediately returnsfalse. - Impact: The protection check is bypassed, and the original callback (
PaymentRequestDialogView::Pay()) executes. The payment is immediately authorized, or sensitive Autofill data (PII) is disclosed to the attacker’s site before the user can react to the UI change.
This vulnerability appears to be a variant of a known class of input protection bypasses recently fixed in other Chrome components, such as the download bubble.
Evaluated with Chrome root at commit: 3fe82e59ce7e04ce7a87a3df42521b560271cbc0
Results from Flapjack so far have been promising, but it can be wrong in its deductions. At this time, it does not produce proof of concepts or fuzzer tests. If this proves to be a false positive, please close as WAI; data from false positives will be used to improve Flapjack’s accuracy over time. And please feel free to reach out to me directly if you have concerns or feedback on the project.