Chrome · Passwords
CVE-2026-113046
Input Validation in Passwords
Overview
Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifchrome/browser/ui/passwords/password_generation_popup_controller_impl.cc |
modified | |
DropFullscreenDelegatechrome/browser/ui/passwords/password_generation_popup_controller_impl_unittest.cc |
modified | |
ifchrome/browser/ui/passwords/password_generation_popup_controller_impl_unittest.cc |
modified | |
MockPasswordManagerDriverchrome/browser/ui/passwords/password_generation_popup_controller_impl_unittest.cc |
modified | |
TEST_Fchrome/browser/ui/passwords/password_generation_popup_controller_impl_unittest.cc |
modified | |
BindLambdaForTestingchrome/browser/ui/passwords/password_generation_popup_controller_impl_unittest.cc |
modified |
Files Changed
components/password_manager/core/browser/features/password_features.cccomponents/password_manager/core/browser/features/password_features.hchrome/browser/ui/passwords/password_generation_popup_controller_impl_unittest.ccchrome/browser/ui/passwords/password_generation_popup_controller_impl.hchrome/browser/ui/passwords/password_generation_popup_controller_impl.cc
Patch
From 27fa86b82960ac4c3d4573a6871f476ed28981c1 Mon Sep 17 00:00:00 2001
From: Rafal Godlewski <rgod@google.com>
Date: Mon, 28 Sep 2026 07:26:12 -0700
Subject: [PATCH] Drop fullscreen and check pointer lock in password generation popup
Fixed: 562999635
Change-Id: I6b197d7f44ec1fb6efeea5a68bd83972acab27ec
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8445419
Reviewed-by: Jan Keitel <jkeitel@google.com>
Reviewed-by: Maria Kazinova <kazinova@google.com>
Commit-Queue: Rafał Godlewski <rgod@google.com>
Cr-Commit-Position: refs/heads/main@{#1706091}
---
diff --git a/chrome/browser/ui/passwords/password_generation_popup_controller_impl.cc b/chrome/browser/ui/passwords/password_generation_popup_controller_impl.cc
index 3e4463c..ca1075a 100644
--- a/chrome/browser/ui/passwords/password_generation_popup_controller_impl.cc
+++ b/chrome/browser/ui/passwords/password_generation_popup_controller_impl.cc
@@ -8,6 +8,7 @@
#include <stddef.h>
#include <algorithm>
+#include <optional>
#include <utility>
#include <vector>
@@ -22,6 +23,7 @@
#include "build/build_config.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/signin/identity_manager_factory.h"
+#include "chrome/browser/ui/autofill/autofill_suggestion_controller_utils.h"
#include "chrome/browser/ui/passwords/password_generation_popup_controller.h"
#include "chrome/browser/ui/passwords/password_generation_popup_observer.h"
#include "chrome/browser/ui/passwords/password_generation_popup_view.h"
@@ -32,6 +34,7 @@
#include "components/autofill/core/common/password_generation_util.h"
#include "components/autofill/core/common/unique_ids.h"
#include "components/input/native_web_keyboard_event.h"
+#include "components/password_manager/core/browser/features/password_features.h"
#include "components/password_manager/core/browser/password_bubble_experiment.h"
#include "components/password_manager/core/browser/password_generation_frame_helper.h"
#include "components/password_manager/core/browser/password_manager.h"
@@ -46,6 +49,7 @@
#include "content/public/browser/web_contents.h"
#include "ui/accessibility/platform/ax_platform.h"
#include "ui/base/l10n/l10n_util.h"
+#include "ui/display/types/display_constants.h"
#include "ui/events/keycodes/keyboard_codes.h"
#include "ui/gfx/geometry/rect_conversions.h"
#include "ui/gfx/text_utils.h"
@@ -227,6 +231,15 @@
return;
}
+ // Hide the popup if the pointer is locked to prevent untrusted interactions.
+ if (base::FeatureList::IsEnabled(
+ password_manager::features::
+ kPasswordGenerationDropFullscreenAndCheckPointerLock) &&
+ autofill::IsPointerLocked(GetWebContents())) {
+ HideImpl();
+ return;
+ }
+
base::WeakPtr<PasswordGenerationPopupControllerImpl> weak_this = GetWeakPtr();
if (driver_) {
// See https://crbug.com/40053471 for when `driver_` might be null due to a
@@ -266,6 +279,33 @@
CHECK(!current_generated_password_.empty());
state_ = state;
+ if (base::FeatureList::IsEnabled(
+ password_manager::features::
+ kPasswordGenerationDropFullscreenAndCheckPointerLock)) {
+ content::WebContents* web_contents = GetWebContents();
+ if (!web_contents || autofill::IsPointerLocked(web_contents)) {
+ HideImpl();
+ return;
+ }
+
+ // Block tab fullscreen to prevent UI spoofing. The block will be released
+ // when `this` is destroyed.
+ if (!fullscreen_blocker_) {
+ base::WeakPtr<PasswordGenerationPopupControllerImpl> weak_this =
+ GetWeakPtr();
+ std::optional<base::ScopedClosureRunner> fullscreen_blocker =
+ web_contents->ForSecurityDropFullscreen(display::kInvalidDisplayId);
+ if (!weak_this) {
+ return;
+ }
+ if (!fullscreen_blocker) {
+ HideImpl();
+ return;
+ }
+ fullscreen_blocker_ = std::move(*fullscreen_blocker);
+ }
+ }
+
if (!view_) {
view_ = PasswordGenerationPopupView::Create(GetWeakPtr());
diff --git a/chrome/browser/ui/passwords/password_generation_popup_controller_impl.h b/chrome/browser/ui/passwords/password_generation_popup_controller_impl.h
index 6331192..bf8e7d5 100644
--- a/chrome/browser/ui/passwords/password_generation_popup_controller_impl.h
+++ b/chrome/browser/ui/passwords/password_generation_popup_controller_impl.h
@@ -11,6 +11,7 @@
#include <string>
#include <vector>
+#include "base/functional/callback_helpers.h"
#include "base/i18n/rtl.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
@@ -228,6 +229,9 @@
std::unique_ptr<KeyPressRegistrator> key_press_handler_manager_;
+ // Fullscreen block held while the popup is showing.
+ base::ScopedClosureRunner fullscreen_blocker_;
+
base::ScopedObservation<zoom::ZoomController, zoom::ZoomObserver>
zoom_observation_{this};
diff --git a/components/password_manager/core/browser/features/password_features.cc b/components/password_manager/core/browser/features/password_features.cc
index 9970daa9..05023dc1 100644
--- a/components/password_manager/core/browser/features/password_features.cc
+++ b/components/password_manager/core/browser/features/password_features.cc
@@ -166,6 +166,9 @@
BASE_FEATURE(kPasswordFormGroupedAffiliations,
base::FEATURE_ENABLED_BY_DEFAULT);
+BASE_FEATURE(kPasswordGenerationDropFullscreenAndCheckPointerLock,
+ base::FEATURE_ENABLED_BY_DEFAULT);
+
BASE_FEATURE(kPasswordManagerLogToTerminal, base::FEATURE_DISABLED_BY_DEFAULT);
BASE_FEATURE(kPasswordManagerOnDeviceEncryptionMetricsReporter,
diff --git a/components/password_manager/core/browser/features/password_features.h b/components/password_manager/core/browser/features/password_features.h
index 54604243..51bf3fb 100644
--- a/components/password_manager/core/browser/features/password_features.h
+++ b/components/password_manager/core/browser/features/password_features.h
@@ -175,6 +175,10 @@
// Enables offering credentials for filling across grouped domains.
BASE_DECLARE_FEATURE(kPasswordFormGroupedAffiliations);
+// Kill switch for dropping fullscreen and checking pointer lock before
+// showing or accepting password generation popups.
+BASE_DECLARE_FEATURE(kPasswordGenerationDropFullscreenAndCheckPointerLock);
+
// Enables logging the content of chrome://password-manager-internals to the
// terminal.
BASE_DECLARE_FEATURE(kPasswordManagerLogToTerminal);
diff --git a/chrome/browser/ui/passwords/password_generation_popup_controller_impl_unittest.cc b/chrome/browser/ui/passwords/password_generation_popup_controller_impl_unittest.cc
index a33bd66..9e9375d 100644
--- a/chrome/browser/ui/passwords/password_generation_popup_controller_impl_unittest.cc
+++ b/chrome/browser/ui/passwords/password_generation_popup_controller_impl_unittest.cc
@@ -9,6 +9,7 @@
#include "base/i18n/rtl.h"
#include "base/strings/utf_string_conversions.h"
+#include "base/test/bind.h"
#include "chrome/browser/ui/passwords/password_generation_popup_controller.h"
#include "chrome/browser/ui/passwords/password_generation_popup_view.h"
#include "chrome/grit/generated_resources.h"
@@ -21,6 +22,7 @@
#include "components/password_manager/core/browser/stub_password_manager_client.h"
#include "components/password_manager/core/browser/stub_password_manager_driver.h"
#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_delegate.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/geometry/rect_f.h"
@@ -41,6 +43,28 @@
autofill::FormData(), /*input_field_empty=*/true);
}
+class DropFullscreenDelegate : public content::WebContentsDelegate {
+ public:
+ explicit DropFullscreenDelegate(base::RepeatingClosure on_exit)
+ : on_exit_(std::move(on_exit)) {}
+
+ content::FullscreenState GetFullscreenState(
+ const content::WebContents* web_contents) const override {
+ content::FullscreenState state;
+ state.target_mode = content::FullscreenMode::kContent;
+ return state;
+ }
+
+ void ExitFullscreenModeForTab(content::WebContents* web_contents) override {
+ if (on_exit_) {
+ on_exit_.Run();
+ }
+ }
+
+ private:
+ base::RepeatingClosure on_exit_;
+};
+
class MockPasswordManagerDriver
: public password_manager::StubPasswordManagerDriver {
public:
@@ -328,4 +352,52 @@
PasswordGenerationPopupController::GenerationUIState::kOfferGeneration);
}
+TEST_F(PasswordGenerationPopupControllerImplTest, ShowDropsFullscreen) {
+ base::WeakPtr<PasswordGenerationPopupControllerImpl> controller =
+ PasswordGenerationPopupControllerImpl::GetOrCreate(
+ /*previous=*/nullptr, ui_data().bounds, ui_data(), weak_driver(),
+ /*observer=*/nullptr, web_contents(), main_rfh());
+
+ controller->SetViewForTesting(popup_view());
+ ON_CALL(*popup_view(), UpdateBoundsAndRedrawPopup)
+ .WillByDefault(Return(true));
+
+ bool exit_fullscreen_called = false;
+ DropFullscreenDelegate delegate(
+ base::BindLambdaForTesting([&]() { exit_fullscreen_called = true; }));
+ web_contents()->SetDelegate(&delegate);
+
+ controller->GeneratePasswordValue(PasswordGenerationType::kAutomatic);
+ controller->Show(
+ PasswordGenerationPopupController::GenerationUIState::kOfferGeneration);
+
+ EXPECT_TRUE(exit_fullscreen_called);
+ web_contents()->SetDelegate(nullptr);
+}
+
+TEST_F(PasswordGenerationPopupControllerImplTest,
+ DestroyedWhileExitingFullscreenDoesNotCrash) {
+ base::WeakPtr<PasswordGenerationPopupControllerImpl> controller =
+ PasswordGenerationPopupControllerImpl::GetOrCreate(
+ /*previous=*/nullptr, ui_data().bounds, ui_data(), weak_driver(),
+ /*observer=*/nullptr, web_contents(), main_rfh());
+
+ controller->SetViewForTesting(popup_view());
+
+ DropFullscreenDelegate delegate(base::BindLambdaForTesting([&]() {
+ if (controller) {
+ static_cast<PasswordGenerationPopupController*>(controller.get())
+ ->Hide(autofill::SuggestionHidingReason::kViewDestroyed);
+ }
+ }));
+ web_contents()->SetDelegate(&delegate);
+
+ controller->GeneratePasswordValue(PasswordGenerationType::kAutomatic);
+ controller->Show(
+ PasswordGenerationPopupController::GenerationUIState::kOfferGeneration);
+
+ EXPECT_FALSE(controller);
+ web_contents()->SetDelegate(nullptr);
+}
+
} // namespace password_manager
Loading diff…
Regression Test / PoC
shipped with the fix
diff --git a/chrome/browser/ui/passwords/password_generation_popup_controller_impl_unittest.cc b/chrome/browser/ui/passwords/password_generation_popup_controller_impl_unittest.cc
index a33bd66..9e9375d 100644
--- a/chrome/browser/ui/passwords/password_generation_popup_controller_impl_unittest.cc
+++ b/chrome/browser/ui/passwords/password_generation_popup_controller_impl_unittest.cc
@@ -9,6 +9,7 @@
#include "base/i18n/rtl.h"
#include "base/strings/utf_string_conversions.h"
+#include "base/test/bind.h"
#include "chrome/browser/ui/passwords/password_generation_popup_controller.h"
#include "chrome/browser/ui/passwords/password_generation_popup_view.h"
#include "chrome/grit/generated_resources.h"
@@ -21,6 +22,7 @@
#include "components/password_manager/core/browser/stub_password_manager_client.h"
#include "components/password_manager/core/browser/stub_password_manager_driver.h"
#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_delegate.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/geometry/rect_f.h"
@@ -41,6 +43,28 @@
autofill::FormData(), /*input_field_empty=*/true);
}
+class DropFullscreenDelegate : public content::WebContentsDelegate {
+ public:
+ explicit DropFullscreenDelegate(base::RepeatingClosure on_exit)
+ : on_exit_(std::move(on_exit)) {}
+
+ content::FullscreenState GetFullscreenState(
+ const content::WebContents* web_contents) const override {
+ content::FullscreenState state;
+ state.target_mode = content::FullscreenMode::kContent;
+ return state;
+ }
+
+ void ExitFullscreenModeForTab(content::WebContents* web_contents) override {
+ if (on_exit_) {
+ on_exit_.Run();
+ }
+ }
+
+ private:
+ base::RepeatingClosure on_exit_;
+};
+
class MockPasswordManagerDriver
: public password_manager::StubPasswordManagerDriver {
public:
@@ -328,4 +352,52 @@
PasswordGenerationPopupController::GenerationUIState::kOfferGeneration);
}
+TEST_F(PasswordGenerationPopupControllerImplTest, ShowDropsFullscreen) {
+ base::WeakPtr<PasswordGenerationPopupControllerImpl> controller =
+ PasswordGenerationPopupControllerImpl::GetOrCreate(
+ /*previous=*/nullptr, ui_data().bounds, ui_data(), weak_driver(),
+ /*observer=*/nullptr, web_contents(), main_rfh());
+
+ controller->SetViewForTesting(popup_view());
+ ON_CALL(*popup_view(), UpdateBoundsAndRedrawPopup)
+ .WillByDefault(Return(true));
+
+ bool exit_fullscreen_called = false;
+ DropFullscreenDelegate delegate(
+ base::BindLambdaForTesting([&]() { exit_fullscreen_called = true; }));
+ web_contents()->SetDelegate(&delegate);
+
+ controller->GeneratePasswordValue(PasswordGenerationType::kAutomatic);
+ controller->Show(
+ PasswordGenerationPopupController::GenerationUIState::kOfferGeneration);
+
+ EXPECT_TRUE(exit_fullscreen_called);
+ web_contents()->SetDelegate(nullptr);
+}
+
+TEST_F(PasswordGenerationPopupControllerImplTest,
+ DestroyedWhileExitingFullscreenDoesNotCrash) {
+ base::WeakPtr<PasswordGenerationPopupControllerImpl> controller =
+ PasswordGenerationPopupControllerImpl::GetOrCreate(
+ /*previous=*/nullptr, ui_data().bounds, ui_data(), weak_driver(),
+ /*observer=*/nullptr, web_contents(), main_rfh());
+
+ controller->SetViewForTesting(popup_view());
+
+ DropFullscreenDelegate delegate(base::BindLambdaForTesting([&]() {
+ if (controller) {
+ static_cast<PasswordGenerationPopupController*>(controller.get())
+ ->Hide(autofill::SuggestionHidingReason::kViewDestroyed);
+ }
+ }));
+ web_contents()->SetDelegate(&delegate);
+
+ controller->GeneratePasswordValue(PasswordGenerationType::kAutomatic);
+ controller->Show(
+ PasswordGenerationPopupController::GenerationUIState::kOfferGeneration);
+
+ EXPECT_FALSE(controller);
+ web_contents()->SetDelegate(nullptr);
+}
+
} // namespace password_manager
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.
References
On This Page