Low chrome Logic Error 🔧 Commit mapped

Overview

Low
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUI misrepresentation in Web Authentication (Passkeys & Security Keys)
DescriptionUI misrepresentation in Web Authentication (Passkeys & Security Keys)
ComponentChromium
Bug ClassLogic Error
Tracker522304549
Fix commit9d7d8afd1cb8 (chromium/src) +124/-2
CISA KEVNot listed
CreditedGoogle
Disclosed2026-08-25

Changed Functions

FunctionChangeNotes
if
chrome/browser/ui/views/webauthn/authenticator_dialog_view_browsertest.cc
modified
StepTransitionObserver
chrome/browser/ui/views/webauthn/authenticator_dialog_view_browsertest.cc
modified
IN_PROC_BROWSER_TEST_F
chrome/browser/ui/views/webauthn/authenticator_dialog_view_browsertest.cc
modified
BindLambdaForTesting
chrome/browser/ui/views/webauthn/authenticator_dialog_view_browsertest.cc
modified

Files Changed

  • chrome/browser/ui/views/webauthn/BUILD.gn
  • chrome/browser/ui/views/webauthn/authenticator_dialog_view_browsertest.cc
  • chrome/browser/ui/views/webauthn/hover_list_view.cc
From 9d7d8afd1cb8ef089b616691c7a579996ef1bd2b Mon Sep 17 00:00:00 2001
From: Adem Derinel <derinel@google.com>
Date: Mon, 06 Jul 2026 08:47:38 -0700
Subject: [PATCH] [WebAuthn] Prevent input protection bypass on HoverListView row clicks

The WebAuthn tab-modal dialog HoverListView row buttons were not
protected by DialogClientView InputEventActivationProtector. As a
result, row clicks occurring immediately upon dialog presentation during
the double-click/input protection interval (500 ms) were executed
instead of dropped.

This change intercepts row button selections across HoverListView
(OnListItemSelected), querying
DialogClientView::IsPossiblyUnintendedInteraction(event, false) before
dispatching item selection callbacks
(HoverListModel::OnListItemSelected) or accepting actions.

Added regression browser test
(AuthenticatorDialogViewTest.InputEventProtection) to verify row clicks
inside the protection window are dropped.

TAG=agy

Fixed: 522304549
Change-Id: Ia4d54fce53a04da71b0095b988c4588f046c9c7a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8039609
Reviewed-by: Nina Satragno <nsatragno@chromium.org>
Commit-Queue: Adem Derinel <derinel@google.com>
Cr-Commit-Position: refs/heads/main@{#1657224}
---

diff --git a/chrome/browser/ui/views/webauthn/BUILD.gn b/chrome/browser/ui/views/webauthn/BUILD.gn
index fcb6201..08b0655 100644
--- a/chrome/browser/ui/views/webauthn/BUILD.gn
+++ b/chrome/browser/ui/views/webauthn/BUILD.gn
@@ -201,5 +201,6 @@
     "//content/test:test_support",
     "//testing/gtest",
     "//ui/views",
+    "//ui/views:test_support",
   ]
 }
diff --git a/chrome/browser/ui/views/webauthn/authenticator_dialog_view_browsertest.cc b/chrome/browser/ui/views/webauthn/authenticator_dialog_view_browsertest.cc
index b8e0252..092bb168 100644
--- a/chrome/browser/ui/views/webauthn/authenticator_dialog_view_browsertest.cc
+++ b/chrome/browser/ui/views/webauthn/authenticator_dialog_view_browsertest.cc
@@ -8,7 +8,9 @@
 
 #include "base/functional/callback_helpers.h"
 #include "base/memory/scoped_refptr.h"
+#include "base/test/bind.h"
 #include "build/build_config.h"
+#include "chrome/app/vector_icons/vector_icons.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
@@ -17,11 +19,14 @@
 #include "chrome/browser/ui/views/webauthn/authenticator_request_dialog_view_controller_views.h"
 #include "chrome/browser/ui/views/webauthn/authenticator_request_dialog_view_test_api.h"
 #include "chrome/browser/ui/views/webauthn/authenticator_request_sheet_view.h"
+#include "chrome/browser/ui/views/webauthn/hover_list_view.h"
 #include "chrome/browser/ui/webauthn/authenticator_request_sheet_model.h"
 #include "chrome/browser/webauthn/authenticator_request_dialog_model.h"
 #include "content/public/test/browser_test.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/views/controls/label.h"
+#include "ui/views/test/button_test_api.h"
+#include "ui/views/window/dialog_client_view.h"
 
 namespace {
 
@@ -92,6 +97,36 @@
   }
 };
 
+HoverListView* FindHoverListView(views::View* root) {
+  if (!root) {
+    return nullptr;
+  }
+  if (auto* hover_list = views::AsViewClass<HoverListView>(root)) {
+    return hover_list;
+  }
+  for (views::View* child : root->children()) {
+    if (HoverListView* found = FindHoverListView(child)) {
+      return found;
+    }
+  }
+  return nullptr;
+}
+
+views::Button* FindTopListItemButton(views::View* root) {
+  if (!root) {
+    return nullptr;
+  }
+  if (auto* button = views::AsViewClass<views::Button>(root)) {
+    return button;
+  }
+  for (views::View* child : root->children()) {
+    if (views::Button* found = FindTopListItemButton(child)) {
+      return found;
+    }
+  }
+  return nullptr;
+}
+
 }  // namespace
 
 class StepTransitionObserver
@@ -163,3 +198,61 @@
 IN_PROC_BROWSER_TEST_F(AuthenticatorDialogViewTest, InvokeUi_ReplaceSheet) {
   ShowAndVerifyUi();
 }
+
+IN_PROC_BROWSER_TEST_F(AuthenticatorDialogViewTest,
+                       HoverListViewInputEventProtection) {
+  content::WebContents* const web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  CHECK(web_contents);
+
+  int mechanism_callback_count_ = 0;
+  auto dialog_model =
+      base::MakeRefCounted<AuthenticatorRequestDialogModel>(nullptr);
+  dialog_model->relying_party_id = "example.com";
+  dialog_model->mechanisms.emplace_back(
+      AuthenticatorRequestDialogModel::Mechanism::Transport(
+          AuthenticatorTransport::kUsbHumanInterfaceDevice),
+      u"Security key", kPasskeyUsbDarkCustomIcon,
+      base::BindRepeating(
+          base::BindLambdaForTesting([&]() { mechanism_callback_count_++; })));
+  dialog_model->SetStep(
+      AuthenticatorRequestDialogModel::Step::kMechanismSelection);
+
+  auto view_controller =
+      std::make_unique<AuthenticatorRequestDialogViewControllerViews>(
+          web_contents, dialog_model.get());
+
+  // Trigger OnStepTransition() so that view_controller creates the sheet for
+  // the current step and calls Show().
+  view_controller->OnStepTransition();
+
+  AuthenticatorRequestSheetView* sheet =
+      test::AuthenticatorRequestDialogViewTestApi::GetSheet(
+          view_controller.get());
+  ASSERT_TRUE(sheet);
+
+  HoverListView* hover_list_view = FindHoverListView(sheet);
+  ASSERT_TRUE(hover_list_view);
+
+  views::Button* top_row_button = FindTopListItemButton(hover_list_view);
+  ASSERT_TRUE(top_row_button);
+
+  ui::MouseEvent click_event(ui::EventType::kMousePressed, gfx::Point(),
+                             gfx::Point(), base::TimeTicks::Now(),
+                             ui::EF_LEFT_MOUSE_BUTTON,
+                             ui::EF_LEFT_MOUSE_BUTTON);
+
+  views::test::ButtonTestApi(top_row_button).NotifyClick(click_event);
+  EXPECT_EQ(mechanism_callback_count_, 0);
+
+  ASSERT_TRUE(sheet->GetWidget());
+  auto* dialog_delegate =
+      sheet->GetWidget()->widget_delegate()->AsDialogDelegate();
+  ASSERT_TRUE(dialog_delegate);
+  auto* dialog_client_view = dialog_delegate->GetDialogClientView();
+  ASSERT_TRUE(dialog_client_view);
+
+  dialog_client_view->ResetViewShownTimeStampForTesting();
+  views::test::ButtonTestApi(top_row_button).NotifyClick(click_event);
+  EXPECT_EQ(mechanism_callback_count_, 1);
+}
diff --git a/chrome/browser/ui/views/webauthn/hover_list_view.cc b/chrome/browser/ui/views/webauthn/hover_list_view.cc
index 3f4e07a..f9e224b 100644
--- a/chrome/browser/ui/views/webauthn/hover_list_view.cc
+++ b/chrome/browser/ui/views/webauthn/hover_list_view.cc
@@ -19,6 +19,7 @@
 #include "ui/base/models/image_model.h"
 #include "ui/base/ui_base_features.h"
 #include "ui/color/color_id.h"
+#include "ui/events/event.h"
 #include "ui/gfx/geometry/insets.h"
 #include "ui/views/border.h"
 #include "ui/views/controls/button/button.h"
@@ -26,6 +27,10 @@
 #include "ui/views/controls/separator.h"
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/layout/fill_layout.h"
+#include "ui/views/widget/widget.h"
+#include "ui/views/widget/widget_delegate.h"
+#include "ui/views/window/dialog_client_view.h"
+#include "ui/views/window/dialog_delegate.h"
 
 namespace {
 
@@ -89,10 +94,12 @@
                                        std::u16string description_text,
                                        bool enabled,
                                        int item_tag) {
+  // Safe because the hover button is added as a child view owned by `this`,
+  // guaranteeing `this` strictly outlives `hover_button` and its callback.
   auto hover_button = CreateHoverButtonForListItem(
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/chrome/browser/ui/views/webauthn/authenticator_dialog_view_browsertest.cc b/chrome/browser/ui/views/webauthn/authenticator_dialog_view_browsertest.cc
index b8e0252..092bb168 100644
--- a/chrome/browser/ui/views/webauthn/authenticator_dialog_view_browsertest.cc
+++ b/chrome/browser/ui/views/webauthn/authenticator_dialog_view_browsertest.cc
@@ -8,7 +8,9 @@
 
 #include "base/functional/callback_helpers.h"
 #include "base/memory/scoped_refptr.h"
+#include "base/test/bind.h"
 #include "build/build_config.h"
+#include "chrome/app/vector_icons/vector_icons.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
@@ -17,11 +19,14 @@
 #include "chrome/browser/ui/views/webauthn/authenticator_request_dialog_view_controller_views.h"
 #include "chrome/browser/ui/views/webauthn/authenticator_request_dialog_view_test_api.h"
 #include "chrome/browser/ui/views/webauthn/authenticator_request_sheet_view.h"
+#include "chrome/browser/ui/views/webauthn/hover_list_view.h"
 #include "chrome/browser/ui/webauthn/authenticator_request_sheet_model.h"
 #include "chrome/browser/webauthn/authenticator_request_dialog_model.h"
 #include "content/public/test/browser_test.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/views/controls/label.h"
+#include "ui/views/test/button_test_api.h"
+#include "ui/views/window/dialog_client_view.h"
 
 namespace {
 
@@ -92,6 +97,36 @@
   }
 };
 
+HoverListView* FindHoverListView(views::View* root) {
+  if (!root) {
+    return nullptr;
+  }
+  if (auto* hover_list = views::AsViewClass<HoverListView>(root)) {
+    return hover_list;
+  }
+  for (views::View* child : root->children()) {
+    if (HoverListView* found = FindHoverListView(child)) {
+      return found;
+    }
+  }
+  return nullptr;
+}
+
+views::Button* FindTopListItemButton(views::View* root) {
+  if (!root) {
+    return nullptr;
+  }
+  if (auto* button = views::AsViewClass<views::Button>(root)) {
+    return button;
+  }
+  for (views::View* child : root->children()) {
+    if (views::Button* found = FindTopListItemButton(child)) {
+      return found;
+    }
+  }
+  return nullptr;
+}
+
 }  // namespace
 
 class StepTransitionObserver
@@ -163,3 +198,61 @@
 IN_PROC_BROWSER_TEST_F(AuthenticatorDialogViewTest, InvokeUi_ReplaceSheet) {
   ShowAndVerifyUi();
 }
+
+IN_PROC_BROWSER_TEST_F(AuthenticatorDialogViewTest,
+                       HoverListViewInputEventProtection) {
+  content::WebContents* const web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  CHECK(web_contents);
+
+  int mechanism_callback_count_ = 0;
+  auto dialog_model =
+      base::MakeRefCounted<AuthenticatorRequestDialogModel>(nullptr);
+  dialog_model->relying_party_id = "example.com";
+  dialog_model->mechanisms.emplace_back(
+      AuthenticatorRequestDialogModel::Mechanism::Transport(
+          AuthenticatorTransport::kUsbHumanInterfaceDevice),
+      u"Security key", kPasskeyUsbDarkCustomIcon,
+      base::BindRepeating(
+          base::BindLambdaForTesting([&]() { mechanism_callback_count_++; })));
+  dialog_model->SetStep(
+      AuthenticatorRequestDialogModel::Step::kMechanismSelection);
+
+  auto view_controller =
+      std::make_unique<AuthenticatorRequestDialogViewControllerViews>(
+          web_contents, dialog_model.get());
+
+  // Trigger OnStepTransition() so that view_controller creates the sheet for
+  // the current step and calls Show().
+  view_controller->OnStepTransition();
+
+  AuthenticatorRequestSheetView* sheet =
+      test::AuthenticatorRequestDialogViewTestApi::GetSheet(
+          view_controller.get());
+  ASSERT_TRUE(sheet);
+
+  HoverListView* hover_list_view = FindHoverListView(sheet);
+  ASSERT_TRUE(hover_list_view);
+
+  views::Button* top_row_button = FindTopListItemButton(hover_list_view);
+  ASSERT_TRUE(top_row_button);
+
+  ui::MouseEvent click_event(ui::EventType::kMousePressed, gfx::Point(),
+                             gfx::Point(), base::TimeTicks::Now(),
+                             ui::EF_LEFT_MOUSE_BUTTON,
+                             ui::EF_LEFT_MOUSE_BUTTON);
+
+  views::test::ButtonTestApi(top_row_button).NotifyClick(click_event);
+  EXPECT_EQ(mechanism_callback_count_, 0);
+
+  ASSERT_TRUE(sheet->GetWidget());
+  auto* dialog_delegate =
+      sheet->GetWidget()->widget_delegate()->AsDialogDelegate();
+  ASSERT_TRUE(dialog_delegate);
+  auto* dialog_client_view = dialog_delegate->GetDialogClientView();
+  ASSERT_TRUE(dialog_client_view);
+
+  dialog_client_view->ResetViewShownTimeStampForTesting();
+  views::test::ButtonTestApi(top_row_button).NotifyClick(click_event);
+  EXPECT_EQ(mechanism_callback_count_, 1);
+}
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.