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 <[email protected]> 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 <[email protected]> Reviewed-by: Maria Kazinova <[email protected]> Commit-Queue: Rafał Godlewski <[email protected]> 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