Low chrome Logic Error 🔧 Commit mapped

Overview

Low
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactIncorrect authorization in FileHandling
DescriptionIncorrect authorization in FileHandling
ComponentFileHandling
Bug ClassLogic Error
Tracker537101736
Fix commita245abe0732f (chromium/src) +51/-1
CISA KEVNot listed
CreditedGoogle
Disclosed2026-09-08

Changed Functions

FunctionChangeNotes
IN_PROC_BROWSER_TEST_F
chrome/browser/ui/views/web_apps/file_handler_launch_dialog_browsertest.cc
modified

Files Changed

  • chrome/browser/ui/views/web_apps/file_handler_launch_dialog_browsertest.cc
  • chrome/browser/ui/views/web_apps/launch_app_user_choice_dialog_view.cc
  • chrome/browser/ui/views/web_apps/launch_app_user_choice_dialog_view.h
  • chrome/browser/ui/views/web_apps/protocol_handler_launch_dialog_browsertest.cc
  • chrome/browser/ui/views/web_apps/protocol_handler_launch_dialog_view.cc
From a245abe0732f3d9f180163b2de0ab52f54ec59c1 Mon Sep 17 00:00:00 2001
From: Kunjan Vinay Patel <[email protected]>
Date: Mon, 10 Aug 2026 14:31:48 -0700
Subject: [PATCH] [PWA] Prevent keyjacking on web app launch consent dialogs.

This change enhances security for web app launch dialogs (covering both
File and Protocol handlers) to prevent accidental or malicious bypasses
via rapid key presses:

- Sets the default focused button to 'Cancel' in the base class
   LaunchAppUserChoiceDialogView.
- Enforces the 500ms input activation delay for keyboard events by
   overriding ShouldAllowKeyEventsDuringInputProtection() to return
   false.

Previously, these dialogs could be immediately accepted via the 'Enter'
key because the default button was 'OK' (specifically for File Handlers)
and key events were not blocked during the initial activation delay.

Bug: 537101736
Change-Id: Ie7396ee149ca498e0df19441c548c8eaa22e9d42
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8226085
Reviewed-by: Dibyajyoti Pal <[email protected]>
Commit-Queue: Kunjan Patel <[email protected]>
Reviewed-by: Luke Deng <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1676726}
---

diff --git a/chrome/browser/ui/views/web_apps/file_handler_launch_dialog_browsertest.cc b/chrome/browser/ui/views/web_apps/file_handler_launch_dialog_browsertest.cc
index 6aa7bdb2..0af938e 100644
--- a/chrome/browser/ui/views/web_apps/file_handler_launch_dialog_browsertest.cc
+++ b/chrome/browser/ui/views/web_apps/file_handler_launch_dialog_browsertest.cc
@@ -226,6 +226,24 @@
   EXPECT_EQ(1U, GlobalBrowserCollection::GetInstance()->GetSize());
 }
 
+IN_PROC_BROWSER_TEST_F(FileHandlerLaunchDialogTest,
+                       DefaultButtonAndInputProtection) {
+  views::NamedWidgetShownWaiter waiter(views::test::AnyWidgetTestPasskey{},
+                                       "FileHandlerLaunchDialogView");
+  LaunchAppWithFiles({{base::FilePath::FromASCII("foo.txt")}});
+  views::Widget* widget = waiter.WaitIfNeededAndGet();
+  ASSERT_NE(widget, nullptr);
+  views::DialogDelegate* dialog_delegate =
+      widget->widget_delegate()->AsDialogDelegate();
+  ASSERT_NE(dialog_delegate, nullptr);
+
+  EXPECT_EQ(dialog_delegate->GetDefaultDialogButton(),
+            static_cast<int>(ui::mojom::DialogButton::kCancel));
+  EXPECT_FALSE(dialog_delegate->ShouldAllowKeyEventsDuringInputProtection());
+
+  views::test::CancelDialog(widget);
+}
+
 IN_PROC_BROWSER_TEST_F(FileHandlerLaunchDialogTest, DisallowAndRemember) {
   // One normal browser window exists.
   EXPECT_EQ(1U, GlobalBrowserCollection::GetInstance()->GetSize());
diff --git a/chrome/browser/ui/views/web_apps/launch_app_user_choice_dialog_view.cc b/chrome/browser/ui/views/web_apps/launch_app_user_choice_dialog_view.cc
index 4141892..003b136 100644
--- a/chrome/browser/ui/views/web_apps/launch_app_user_choice_dialog_view.cc
+++ b/chrome/browser/ui/views/web_apps/launch_app_user_choice_dialog_view.cc
@@ -83,6 +83,8 @@
   SetCanResize(false);
   set_draggable(true);
 
+  SetDefaultButton(static_cast<int>(ui::mojom::DialogButton::kCancel));
+
   SetAcceptCallback(base::BindOnce(&LaunchAppUserChoiceDialogView::OnAccepted,
                                    base::Unretained(this)));
 
@@ -233,6 +235,11 @@
       gfx::ImageSkia::CreateFrom1xBitmap(std::move(masked_bitmap))));
 }
 
+bool LaunchAppUserChoiceDialogView::ShouldAllowKeyEventsDuringInputProtection()
+    const {
+  return false;
+}
+
 BEGIN_METADATA(LaunchAppUserChoiceDialogView)
 END_METADATA
 
diff --git a/chrome/browser/ui/views/web_apps/launch_app_user_choice_dialog_view.h b/chrome/browser/ui/views/web_apps/launch_app_user_choice_dialog_view.h
index 406708d3..7c164a5b 100644
--- a/chrome/browser/ui/views/web_apps/launch_app_user_choice_dialog_view.h
+++ b/chrome/browser/ui/views/web_apps/launch_app_user_choice_dialog_view.h
@@ -46,6 +46,9 @@
 
   void Init();
 
+  // views::DialogDelegate:
+  bool ShouldAllowKeyEventsDuringInputProtection() const override;
+
   static void SetDefaultRememberSelectionForTesting(bool remember_selection);
 
  protected:
diff --git a/chrome/browser/ui/views/web_apps/protocol_handler_launch_dialog_browsertest.cc b/chrome/browser/ui/views/web_apps/protocol_handler_launch_dialog_browsertest.cc
index 868700c..00c5c32 100644
--- a/chrome/browser/ui/views/web_apps/protocol_handler_launch_dialog_browsertest.cc
+++ b/chrome/browser/ui/views/web_apps/protocol_handler_launch_dialog_browsertest.cc
@@ -108,6 +108,29 @@
 }
 
 IN_PROC_BROWSER_TEST_F(ProtocolHandlerLaunchDialogBrowserTest,
+                       DefaultButtonAndInputProtection) {
+  views::NamedWidgetShownWaiter waiter(views::test::AnyWidgetTestPasskey{},
+                                       "ProtocolHandlerLaunchDialogView");
+  GURL protocol_url("web+test://test");
+  webapps::AppId test_app_id = InstallTestWebApp(browser()->GetProfile());
+
+  ShowWebAppProtocolLaunchDialog(protocol_url, browser()->GetProfile(),
+                                 test_app_id, base::DoNothing());
+
+  views::Widget* widget = waiter.WaitIfNeededAndGet();
+  ASSERT_NE(widget, nullptr);
+  views::DialogDelegate* dialog_delegate =
+      widget->widget_delegate()->AsDialogDelegate();
+  ASSERT_NE(dialog_delegate, nullptr);
+
+  EXPECT_EQ(dialog_delegate->GetDefaultDialogButton(),
+            static_cast<int>(ui::mojom::DialogButton::kCancel));
+  EXPECT_FALSE(dialog_delegate->ShouldAllowKeyEventsDuringInputProtection());
+
+  views::test::CancelDialog(widget);
+}
+
+IN_PROC_BROWSER_TEST_F(ProtocolHandlerLaunchDialogBrowserTest,
                        ProtocolHandlerIntentPickerDialog_DisallowAndRemember) {
   ProtocolHandlerLaunchDialogView::SetDefaultRememberSelectionForTesting(true);
   ShowDialogAndCloseWithReason(
diff --git a/chrome/browser/ui/views/web_apps/protocol_handler_launch_dialog_view.cc b/chrome/browser/ui/views/web_apps/protocol_handler_launch_dialog_view.cc
index 94f1812..32b758e 100644
--- a/chrome/browser/ui/views/web_apps/protocol_handler_launch_dialog_view.cc
+++ b/chrome/browser/ui/views/web_apps/protocol_handler_launch_dialog_view.cc
@@ -39,7 +39,6 @@
   SetButtonLabel(
       ui::mojom::DialogButton::kCancel,
       l10n_util::GetStringUTF16(IDS_WEB_APP_PERMISSION_NEGATIVE_BUTTON));
-  SetDefaultButton(static_cast<int>(ui::mojom::DialogButton::kCancel));
 }
 
 ProtocolHandlerLaunchDialogView::~ProtocolHandlerLaunchDialogView() = default;
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/chrome/browser/ui/views/web_apps/file_handler_launch_dialog_browsertest.cc b/chrome/browser/ui/views/web_apps/file_handler_launch_dialog_browsertest.cc
index 6aa7bdb2..0af938e 100644
--- a/chrome/browser/ui/views/web_apps/file_handler_launch_dialog_browsertest.cc
+++ b/chrome/browser/ui/views/web_apps/file_handler_launch_dialog_browsertest.cc
@@ -226,6 +226,24 @@
   EXPECT_EQ(1U, GlobalBrowserCollection::GetInstance()->GetSize());
 }
 
+IN_PROC_BROWSER_TEST_F(FileHandlerLaunchDialogTest,
+                       DefaultButtonAndInputProtection) {
+  views::NamedWidgetShownWaiter waiter(views::test::AnyWidgetTestPasskey{},
+                                       "FileHandlerLaunchDialogView");
+  LaunchAppWithFiles({{base::FilePath::FromASCII("foo.txt")}});
+  views::Widget* widget = waiter.WaitIfNeededAndGet();
+  ASSERT_NE(widget, nullptr);
+  views::DialogDelegate* dialog_delegate =
+      widget->widget_delegate()->AsDialogDelegate();
+  ASSERT_NE(dialog_delegate, nullptr);
+
+  EXPECT_EQ(dialog_delegate->GetDefaultDialogButton(),
+            static_cast<int>(ui::mojom::DialogButton::kCancel));
+  EXPECT_FALSE(dialog_delegate->ShouldAllowKeyEventsDuringInputProtection());
+
+  views::test::CancelDialog(widget);
+}
+
 IN_PROC_BROWSER_TEST_F(FileHandlerLaunchDialogTest, DisallowAndRemember) {
   // One normal browser window exists.
   EXPECT_EQ(1U, GlobalBrowserCollection::GetInstance()->GetSize());
diff --git a/chrome/browser/ui/views/web_apps/protocol_handler_launch_dialog_browsertest.cc b/chrome/browser/ui/views/web_apps/protocol_handler_launch_dialog_browsertest.cc
index 868700c..00c5c32 100644
--- a/chrome/browser/ui/views/web_apps/protocol_handler_launch_dialog_browsertest.cc
+++ b/chrome/browser/ui/views/web_apps/protocol_handler_launch_dialog_browsertest.cc
@@ -108,6 +108,29 @@
 }
 
 IN_PROC_BROWSER_TEST_F(ProtocolHandlerLaunchDialogBrowserTest,
+                       DefaultButtonAndInputProtection) {
+  views::NamedWidgetShownWaiter waiter(views::test::AnyWidgetTestPasskey{},
+                                       "ProtocolHandlerLaunchDialogView");
+  GURL protocol_url("web+test://test");
+  webapps::AppId test_app_id = InstallTestWebApp(browser()->GetProfile());
+
+  ShowWebAppProtocolLaunchDialog(protocol_url, browser()->GetProfile(),
+                                 test_app_id, base::DoNothing());
+
+  views::Widget* widget = waiter.WaitIfNeededAndGet();
+  ASSERT_NE(widget, nullptr);
+  views::DialogDelegate* dialog_delegate =
+      widget->widget_delegate()->AsDialogDelegate();
+  ASSERT_NE(dialog_delegate, nullptr);
+
+  EXPECT_EQ(dialog_delegate->GetDefaultDialogButton(),
+            static_cast<int>(ui::mojom::DialogButton::kCancel));
+  EXPECT_FALSE(dialog_delegate->ShouldAllowKeyEventsDuringInputProtection());
+
+  views::test::CancelDialog(widget);
+}
+
+IN_PROC_BROWSER_TEST_F(ProtocolHandlerLaunchDialogBrowserTest,
                        ProtocolHandlerIntentPickerDialog_DisallowAndRemember) {
   ProtocolHandlerLaunchDialogView::SetDefaultRememberSelectionForTesting(true);
   ShowDialogAndCloseWithReason(
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.