CVE-2026-17975
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifcontent/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm |
modified | |
switchcontent/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm |
modified | |
ifcontent/browser/renderer_host/render_widget_host_view_mac.mm |
modified |
Files Changed
content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mmcontent/browser/renderer_host/render_widget_host_view_mac.mmcontent/browser/renderer_host/render_widget_host_view_mac_unittest.mm
Patch
From f419acfcd74bbcab41700900117cb6a970587832 Mon Sep 17 00:00:00 2001 From: Avi Drissman <[email protected]> Date: Wed, 03 Jun 2026 13:15:44 -0700 Subject: [PATCH] Treat "kinda password" fields as password fields When a password field is revealed by the user, the field is tagged with the flag TEXT_INPUT_FLAG_HAS_BEEN_PASSWORD. When a normal text field is detected to be used by the web page as a password field, the field is tagged with the flag TEXT_INPUT_FLAG_HAS_BEEN_CUSTOM_PASSWORD. In both cases, treat such a field as a password field in various places in the code. Fixed: 519228697, 519230894, 519233776 Change-Id: I7809e4c77f6a5a90ff33e6e2671b7c466a6a6964 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7896033 Reviewed-by: Keren Zhu <[email protected]> Commit-Queue: Avi Drissman <[email protected]> Cr-Commit-Position: refs/heads/main@{#1641141} --- diff --git a/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm b/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm index a04aa49..24c98863 100644 --- a/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm +++ b/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm @@ -566,23 +566,30 @@ - (void)requestTextSuggestions { auto* touchBarItem = _candidateListTouchBarItem; - if (!touchBarItem) + if (!touchBarItem) { return; + } [touchBarItem updateWithInsertionPointVisibility:_textSelectionRange.is_empty()]; - if (_textInputType == ui::TEXT_INPUT_TYPE_PASSWORD) + if (_textInputType == ui::TEXT_INPUT_TYPE_PASSWORD || + _textInputFlags & ui::TEXT_INPUT_FLAG_HAS_BEEN_PASSWORD || + _textInputFlags & ui::TEXT_INPUT_FLAG_HAS_BEEN_CUSTOM_PASSWORD) { return; - if (!touchBarItem.candidateListVisible) + } + if (!touchBarItem.candidateListVisible) { return; + } if (!_textSelectionRange.IsValid() || - _availableTextOffset > _textSelectionRange.GetMin()) + _availableTextOffset > _textSelectionRange.GetMin()) { return; + } NSRange selectionRange = _textSelectionRange.ToNSRange(); NSString* selectionText = base::SysUTF16ToNSString(_availableText); selectionRange.location -= _availableTextOffset; - if (NSMaxRange(selectionRange) > selectionText.length) + if (NSMaxRange(selectionRange) > selectionText.length) { return; + } // TODO: Fetch the spell document tag from the renderer (or equivalent). _textSuggestionsSequenceNumber = [self.spellChecker @@ -625,12 +632,15 @@ - (NSTextCheckingType)enabledTextCheckingTypes { NSTextCheckingType checkingTypes = 0; - if (self.automaticQuoteSubstitutionEnabled) + if (self.automaticQuoteSubstitutionEnabled) { checkingTypes |= NSTextCheckingTypeQuote; - if (self.automaticDashSubstitutionEnabled) + } + if (self.automaticDashSubstitutionEnabled) { checkingTypes |= NSTextCheckingTypeDash; - if (self.automaticTextReplacementEnabled) + } + if (self.automaticTextReplacementEnabled) { checkingTypes |= NSTextCheckingTypeReplacement; + } return checkingTypes; } @@ -639,10 +649,14 @@ } - (bool)canTransformText { - if (_textInputType == ui::TEXT_INPUT_TYPE_NONE) + if (_textInputType == ui::TEXT_INPUT_TYPE_NONE) { return NO; - if (_textInputType == ui::TEXT_INPUT_TYPE_PASSWORD) + } + if (_textInputType == ui::TEXT_INPUT_TYPE_PASSWORD || + _textInputFlags & ui::TEXT_INPUT_FLAG_HAS_BEEN_PASSWORD || + _textInputFlags & ui::TEXT_INPUT_FLAG_HAS_BEEN_CUSTOM_PASSWORD) { return NO; + } return YES; } @@ -2383,19 +2397,24 @@ // Each RenderWidgetHostViewCocoa has its own input context, but we return // nil when the caret is in non-editable content or password box to avoid // making input methods do their work. -// We disable input method inside password field as it is normal for Mac OS X +// +// We disable input method inside password field as it is normal for macOS // password input fields to not allow dead keys or non ASCII input methods. // There is also a privacy risk if the composition candidate window shows your // password when the user is "composing" inside a password field. See -// crbug.com/1196101 for more info. +// https://crbug.com/40759416 for more info. - (NSTextInputContext*)inputContext { - switch (_textInputType) { - case ui::TEXT_INPUT_TYPE_NONE: - case ui::TEXT_INPUT_TYPE_PASSWORD: - return nil; - default: - return [super inputContext]; + if (_textInputType == ui::TEXT_INPUT_TYPE_NONE || + _textInputType == ui::TEXT_INPUT_TYPE_PASSWORD) { + return nil; } + + if (_textInputFlags & ui::TEXT_INPUT_FLAG_HAS_BEEN_PASSWORD || + _textInputFlags & ui::TEXT_INPUT_FLAG_HAS_BEEN_CUSTOM_PASSWORD) { + return nil; + } + + return [super inputContext]; } - (BOOL)hasMarkedText { diff --git a/content/browser/renderer_host/render_widget_host_view_mac.mm b/content/browser/renderer_host/render_widget_host_view_mac.mm index 319b54e2..3acdf53d 100644 --- a/content/browser/renderer_host/render_widget_host_view_mac.mm +++ b/content/browser/renderer_host/render_widget_host_view_mac.mm @@ -72,6 +72,8 @@ #import "ui/base/cocoa/secure_password_input.h" #include "ui/base/cursor/cursor.h" #include "ui/base/ime/mojom/text_input_state.mojom.h" +#include "ui/base/ime/text_input_flags.h" +#include "ui/base/ime/text_input_type.h" #include "ui/base/mojom/attributed_string.mojom.h" #include "ui/base/ui_base_features.h" #include "ui/display/display.h" @@ -1788,14 +1790,33 @@ } void RenderWidgetHostViewMac::SetTextInputActive(bool active) { - const bool should_enable_password_input = - active && GetTextInputType() == ui::TEXT_INPUT_TYPE_PASSWORD; + bool should_enable_password_input = [active, this] { + if (!active) { + return false; + } + + if (!this->GetActiveWidget()) { + return false; + } + + const ui::mojom::TextInputState* state = + this->text_input_manager_->GetTextInputState(); + if (state->type == ui::TEXT_INPUT_TYPE_PASSWORD || + state->flags & ui::TEXT_INPUT_FLAG_HAS_BEEN_PASSWORD || + state->flags & ui::TEXT_INPUT_FLAG_HAS_BEEN_CUSTOM_PASSWORD) { + return true; + } + + return false; + }(); + if (should_enable_password_input) { password_input_enabler_ = std::make_unique<ui::ScopedPasswordInputEnabler>(); } else { password_input_enabler_.reset(); } + update_windows_timer_.Stop(); } diff --git a/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm b/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm index 2185bfb8..e1f6dcf 100644 --- a/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm +++ b/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm @@ -60,6 +60,8 @@ #import "third_party/ocmock/ocmock_extensions.h" #include "ui/base/cocoa/secure_password_input.h" #include "ui/base/ime/mojom/text_input_state.mojom.h" +#include "ui/base/ime/text_input_flags.h" +#include "ui/base/ime/text_input_type.h" #import "ui/base/test/cocoa_helper.h" #import "ui/base/test/scoped_fake_nswindow_focus.h" #include "ui/base/ui_base_features.h" @@ -1824,9 +1826,11 @@ } void SetTextInputType(RenderWidgetHostViewBase* view, - ui::TextInputType type) { + ui::TextInputType type, + ui::TextInputFlags flags = ui::TEXT_INPUT_FLAG_NONE) { ui::mojom::TextInputState state;
Regression Test / PoC
diff --git a/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm b/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm
index 2185bfb8..e1f6dcf 100644
--- a/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm
+++ b/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm
@@ -60,6 +60,8 @@
#import "third_party/ocmock/ocmock_extensions.h"
#include "ui/base/cocoa/secure_password_input.h"
#include "ui/base/ime/mojom/text_input_state.mojom.h"
+#include "ui/base/ime/text_input_flags.h"
+#include "ui/base/ime/text_input_type.h"
#import "ui/base/test/cocoa_helper.h"
#import "ui/base/test/scoped_fake_nswindow_focus.h"
#include "ui/base/ui_base_features.h"
@@ -1824,9 +1826,11 @@
}
void SetTextInputType(RenderWidgetHostViewBase* view,
- ui::TextInputType type) {
+ ui::TextInputType type,
+ ui::TextInputFlags flags = ui::TEXT_INPUT_FLAG_NONE) {
ui::mojom::TextInputState state;
state.type = type;
+ state.flags = flags;
view->TextInputStateChanged(state);
}
@@ -2089,6 +2093,66 @@
EXPECT_FALSE(ui::ScopedPasswordInputEnabler::IsPasswordInputEnabled());
}
+TEST_F(InputMethodMacTest, SecureHasBeenAPasswordInput) {
+ ASSERT_FALSE(ui::ScopedPasswordInputEnabler::IsPasswordInputEnabled());
+ ASSERT_EQ(text_input_manager(), tab_view()->GetTextInputManager());
+
+ // RenderWidgetHostViewMacTest.LostFocusAndGotFocusOnSetActive checks the
+ // GotFocus()/LostFocus() rules, just silence the warnings here.
+ EXPECT_CALL(*host_, Focus()).Times(::testing::AnyNumber());
+ EXPECT_CALL(*host_, Blur()).Times(::testing::AnyNumber());
+
+ [window_ makeFirstResponder:tab_view()->GetInProcessNSView()];
+
+ // Shouldn't enable secure input if it's not a "has been a password"
+ // textfield.
+ tab_view()->SetActive(true);
+ EXPECT_FALSE(ui::ScopedPasswordInputEnabler::IsPasswordInputEnabled());
+
+ SetTextInputType(child_view_, ui::TEXT_INPUT_TYPE_TEXT,
+ ui::TEXT_INPUT_FLAG_HAS_BEEN_PASSWORD);
+ ASSERT_EQ(child_widget_.get(), text_input_manager()->GetActiveWidget());
+ ASSERT_EQ(text_input_manager(), tab_view()->GetTextInputManager());
+ ASSERT_EQ(ui::TEXT_INPUT_TYPE_TEXT, tab_view()->GetTextInputType());
+
+ // Single matched calls immediately update IsPasswordInputEnabled().
+ tab_view()->SetActive(true);
+ EXPECT_TRUE(ui::ScopedPasswordInputEnabler::IsPasswordInputEnabled());
+
+ tab_view()->SetActive(false);
+ EXPECT_FALSE(ui::ScopedPasswordInputEnabler::IsPasswordInputEnabled());
+}
+
+TEST_F(InputMethodMacTest, SecureHasBeenACustomPasswordInput) {
+ ASSERT_FALSE(ui::ScopedPasswordInputEnabler::IsPasswordInputEnabled());
+ ASSERT_EQ(text_input_manager(), tab_view()->GetTextInputManager());
+
+ // RenderWidgetHostViewMacTest.LostFocusAndGotFocusOnSetActive checks the
+ // GotFocus()/LostFocus() rules, just silence the warnings here.
+ EXPECT_CALL(*host_, Focus()).Times(::testing::AnyNumber());
+ EXPECT_CALL(*host_, Blur()).Times(::testing::AnyNumber());
+
+ [window_ makeFirstResponder:tab_view()->GetInProcessNSView()];
+
+ // Shouldn't enable secure input if it's not a "has been a custom password"
+ // textfield.
+ tab_view()->SetActive(true);
+ EXPECT_FALSE(ui::ScopedPasswordInputEnabler::IsPasswordInputEnabled());
+
+ SetTextInputType(child_view_, ui::TEXT_INPUT_TYPE_TEXT,
+ ui::TEXT_INPUT_FLAG_HAS_BEEN_CUSTOM_PASSWORD);
+ ASSERT_EQ(child_widget_.get(), text_input_manager()->GetActiveWidget());
+ ASSERT_EQ(text_input_manager(), tab_view()->GetTextInputManager());
+ ASSERT_EQ(ui::TEXT_INPUT_TYPE_TEXT, tab_view()->GetTextInputType());
+
+ // Single matched calls immediately update IsPasswordInputEnabled().
+ tab_view()->SetActive(true);
+ EXPECT_TRUE(ui::ScopedPasswordInputEnabler::IsPasswordInputEnabled());
+
+ tab_view()->SetActive(false);
+ EXPECT_FALSE(ui::ScopedPasswordInputEnabler::IsPasswordInputEnabled());
+}
+
// This test creates a test view to mimic a child frame's view and verifies that
// calling ImeCancelComposition on either the child view or the tab's view will
// always lead to a call to cancelComposition on the cocoa view.
diff --git a/ui/base/ime/ash/input_method_ash_unittest.cc b/ui/base/ime/ash/input_method_ash_unittest.cc
index de29d25..ec96a46 100644
--- a/ui/base/ime/ash/input_method_ash_unittest.cc
+++ b/ui/base/ime/ash/input_method_ash_unittest.cc
@@ -429,7 +429,7 @@
ime.SetFocusedTextInputClient(nullptr);
}
-TEST_F(InputMethodAshTest, HasBeenPasswordShouldTriggerPassowrd) {
+TEST_F(InputMethodAshTest, HasBeenPasswordShouldTriggerPassword) {
InputMethodAsh ime(this);
FakeTextInputClient fake_text_input_client(ui::TEXT_INPUT_TYPE_TEXT);
fake_text_input_client.SetFlags(ui::TEXT_INPUT_FLAG_HAS_BEEN_PASSWORD);
@@ -442,6 +442,19 @@
ime.SetFocusedTextInputClient(nullptr);
}
+TEST_F(InputMethodAshTest, HasBeenCustomPasswordShouldTriggerPassword) {
+ InputMethodAsh ime(this);
+ FakeTextInputClient fake_text_input_client(ui::TEXT_INPUT_TYPE_TEXT);
+ fake_text_input_client.SetFlags(ui::TEXT_INPUT_FLAG_HAS_BEEN_CUSTOM_PASSWORD);
+
+ ime.SetFocusedTextInputClient(&fake_text_input_client);
+
+ EXPECT_EQ(mock_ime_engine_handler_->last_text_input_context().type,
+ ui::TEXT_INPUT_TYPE_PASSWORD);
+
+ ime.SetFocusedTextInputClient(nullptr);
+}
+
TEST_F(InputMethodAshTest, GetTextInputClient) {
EXPECT_EQ(this, input_method_ash_->GetTextInputClient());
input_method_ash_->SetFocusedTextInputClient(nullptr);
diff --git a/ui/base/ime/input_method_base_unittest.cc b/ui/base/ime/input_method_base_unittest.cc
index 64a5364c..50b98f0 100644
--- a/ui/base/ime/input_method_base_unittest.cc
+++ b/ui/base/ime/input_method_base_unittest.cc
@@ -311,5 +311,19 @@
EXPECT_EQ(TEXT_INPUT_TYPE_PASSWORD, input_method.GetTextInputType());
}
+TEST_F(InputMethodBaseTest, SetsPasswordWhenHasBeenCustomPassword) {
+ FakeTextInputClient fake_text_input_client(ui::TEXT_INPUT_TYPE_TEXT);
+
+ ClientChangeVerifier verifier;
+ verifier.ExpectClientChange(nullptr, &fake_text_input_client);
+ MockInputMethodBase input_method(&verifier);
+
+ fake_text_input_client.SetFlags(ui::TEXT_INPUT_FLAG_HAS_BEEN_CUSTOM_PASSWORD);
+
+ input_method.SetFocusedTextInputClient(&fake_text_input_client);
+
+ EXPECT_EQ(TEXT_INPUT_TYPE_PASSWORD, input_method.GetTextInputType());
+}
+
} // namespace
} // namespace ui
diff --git a/ui/views/cocoa/bridged_native_widget_unittest.mm b/ui/views/cocoa/bridged_native_widget_unittest.mm
index 105a9c38..ed21905 100644
--- a/ui/views/cocoa/bridged_native_widget_unittest.mm
+++ b/ui/views/cocoa/bridged_native_widget_unittest.mm
@@ -1101,6 +1101,13 @@
text_field->SetTextInputFlags(ui::TEXT_INPUT_FLAG_HAS_BEEN_PASSWORD);
EXPECT_FALSE(ns_view_.inputContext);
+ text_field =
+ InstallTextField(test_string, ui::TEXT_INPUT_TYPE_TEXT);
+ EXPECT_TRUE(ns_view_.inputContext);
+
+ text_field->SetTextInputFlags(ui::TEXT_INPUT_FLAG_HAS_BEEN_CUSTOM_PASSWORD);
+ EXPECT_FALSE(ns_view_.inputContext);
+
GetNSWindowHost()->text_input_host()->SetTextInputClient(nullptr);
EXPECT_FALSE(ns_view_.inputContext);
diff --git a/ui/views/controls/textfield/textfield_unittest.cc b/ui/views/controls/textfield/textfield_unittest.cc
index 899f467..09a1268f 100644
--- a/ui/views/controls/textfield/textfield_unittest.cc
+++ b/ui/views/controls/textfield/textfield_unittest.cc
@@ -38,6 +38,7 @@
#include "ui/base/ime/input_method_base.h"
#include "ui/base/ime/text_edit_commands.h"
#include "ui/base/ime/text_input_client.h"
+#include "ui/base/ime/text_input_flags.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/metadata/metadata_header_macros.h"
#include "ui/base/metadata/metadata_impl_macros.h"
@@ -5261,6 +5262,7 @@
}
#if BUILDFLAG(IS_MAC)
+
// Tests to see if the BiDi submenu items are updated correctly when the
// textfield's text direction is changed.
TEST_F(TextfieldTest, TextServicesContextMenuTextDirectionTest) {
@@ -5327,6 +5329,45 @@
textfield_->OnBlur();
EXPECT_FALSE(ui::ScopedPasswordInputEnabler::IsPasswordInputEnabled());
}
+
+TEST_F(TextfieldTest, SecureHasBeenAPasswordInput) {
+ InitTextfield();
+ ASSERT_FALSE(ui::ScopedPasswordInputEnabler::IsPasswordInputEnabled());
+
+ // Shouldn't enable secure input if it's not a password textfield.
+ textfield_->OnFocus();
+ EXPECT_FALSE(ui::ScopedPasswordInputEnabler::IsPasswordInputEnabled());
+
+ textfield_->SetTextInputFlags(textfield_->GetTextInputFlags() |
+ ui::TEXT_INPUT_FLAG_HAS_BEEN_PASSWORD);
+
+ // Single matched calls immediately update IsPasswordInputEnabled().
+ textfield_->OnFocus();
+ EXPECT_TRUE(ui::ScopedPasswordInputEnabler::IsPasswordInputEnabled());
+
+ textfield_->OnBlur();
+ EXPECT_FALSE(ui::ScopedPasswordInputEnabler::IsPasswordInputEnabled());
+}
+
+TEST_F(TextfieldTest, SecureHasBeenACustomPasswordInput) {
+ InitTextfield();
+ ASSERT_FALSE(ui::ScopedPasswordInputEnabler::IsPasswordInputEnabled());
+
+ // Shouldn't enable secure input if it's not a password textfield.
+ textfield_->OnFocus();
+ EXPECT_FALSE(ui::ScopedPasswordInputEnabler::IsPasswordInputEnabled());
+
+ textfield_->SetTextInputFlags(textfield_->GetTextInputFlags() |
+ ui::TEXT_INPUT_FLAG_HAS_BEEN_CUSTOM_PASSWORD);
+
+ // Single matched calls immediately update IsPasswordInputEnabled().
+ textfield_->OnFocus();
+ EXPECT_TRUE(ui::ScopedPasswordInputEnabler::IsPasswordInputEnabled());
+
+ textfield_->OnBlur();
+ EXPECT_FALSE(ui::ScopedPasswordInputEnabler::IsPasswordInputEnabled());
+}
+
#endif // BUILDFLAG(IS_MAC)
TEST_F(TextfieldTest, AccessibilitySelectionEvents) {
Original Bug Report
Potential secure event input bypass in RenderWidgetHostViewMac on password type toggles
Project Fortify, an experimental security project, has identified the following potential security issue. If you’re a feature owner CC-ed on this bug, please do your best to review these reports. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.
Overview: RenderWidgetHostViewMac fails to check the TEXT_INPUT_FLAG_HAS_BEEN_PASSWORD flag when determining whether to keep secure event input enabled. When a password field is dynamically toggled to a text field (such as via a ‘show password’ button), secure input is deactivated. This potentially exposes sensitive password keystrokes to system-wide keyboard event taps and third-party input method editors (IMEs) on macOS.
Affected files:
content/browser/renderer_host/render_widget_host_view_mac.mm
Estimated timestamp from git blame: 2018-03-12
Root Cause
In content/browser/renderer_host/render_widget_host_view_mac.mm, SetTextInputActive manages the lifecycle of ui::ScopedPasswordInputEnabler, which wraps macOS’s system-wide EnableSecureEventInput() security control. However, the decision to hold this enabler is based purely on the un-coerced text input type:
// content/browser/renderer_host/render_widget_host_view_mac.mm:1790-1800
void RenderWidgetHostViewMac::SetTextInputActive(bool active) {
const bool should_enable_password_input =
active && GetTextInputType() == ui::TEXT_INPUT_TYPE_PASSWORD;
if (should_enable_password_input) {
password_input_enabler_ =
std::make_unique<ui::ScopedPasswordInputEnabler>();
} else {
password_input_enabler_.reset();
}
update_windows_timer_.Stop();
}
// content/browser/renderer_host/render_widget_host_view_mac.mm:405-409
ui::TextInputType RenderWidgetHostViewMac::GetTextInputType() {
if (!GetActiveWidget())
return ui::TEXT_INPUT_TYPE_NONE;
return text_input_manager_->GetTextInputState()->type;
}
When a password input field’s type attribute is dynamically toggled to 'text' (e.g., to reveal the password to the user), Blink correctly maintains the kWebTextInputFlagHasBeenPasswordField flag (ui::TEXT_INPUT_FLAG_HAS_BEEN_PASSWORD in the browser) to signal that the field contains sensitive credentials that should still be protected. This flag is set in third_party/blink/renderer/core/editing/ime/input_method_controller.cc:
if (auto* input = DynamicTo<HTMLInputElement>(element)) {
if (input->HasBeenPasswordField())
flags |= kWebTextInputFlagHasBeenPasswordField;
}
Unlike Chrome’s Views counterpart (ui/views/cocoa/text_input_host.mm:247-258) or ui::InputMethodBase::GetTextInputType(), RenderWidgetHostViewMac does not consult this flag. As a result, when a password field is toggled, GetTextInputType() returns ui::TEXT_INPUT_TYPE_TEXT, evaluating should_enable_password_input to false. This triggers password_input_enabler_.reset(), disabling secure keyboard entry while the user is actively typing their password.
Potential Trigger Path
Note: These are suggested steps to replicate the behavior based on a code-level review, as our tooling does not have the capability to run code.
- Focus a password field on a web page (e.g.
<input type="password">). This triggersEnableSecureEventInput()viaScopedPasswordInputEnablerin the browser process. - Execute a script (or click a page element) that dynamically changes the input type to text:
inputElement.type = 'text'. This preserves theTEXT_INPUT_FLAG_HAS_BEEN_PASSWORDflag in the renderer’sTextInputStatebut changestypetoTEXT_INPUT_TYPE_TEXT. - Observe that
RenderWidgetHostViewMac::SetTextInputActivere-evaluates, sees the new type, and resetspassword_input_enabler_, actively callingDisableSecureEventInput()at the OS level while the user is still focused and typing.
Impact
This is a potential macOS-specific defense-in-depth bypass. When the secure event input protection is disabled, any background keyloggers (such as those using Carbon/Cocoa CGEventTap with accessibility/input-monitoring privileges) and third-party IMEs can capture the sensitive password keystrokes as the user types them into the revealed field.
Suggested Fix
Update RenderWidgetHostViewMac::SetTextInputActive to check for ui::TEXT_INPUT_FLAG_HAS_BEEN_PASSWORD when evaluating whether to enable secure password input, matching the approach used elsewhere in Chromium:
void RenderWidgetHostViewMac::SetTextInputActive(bool active) {
bool has_been_password = false;
if (text_input_manager_ && text_input_manager_->GetTextInputState()) {
has_been_password = (text_input_manager_->GetTextInputState()->flags &
ui::TEXT_INPUT_FLAG_HAS_BEEN_PASSWORD) != 0;
}
const bool should_enable_password_input =
active && (GetTextInputType() == ui::TEXT_INPUT_TYPE_PASSWORD || has_been_password);
...
Evaluated with Chrome root at commit: 87214e6721f6c34afd9181b80769a24c0c601c50
Results so far have been promising, but there can be wrong deductions. Feel free to adjust as follows:
- If you are familiar with the severity guidelines, you may adjust the severity.
- If this is a false positive, and there’s no work to be done, please close as WAI.
- If there is work to do here but not a vulnerability, please change the issue type to Task/Bug/FR.
Data from false positives will be used to improve accuracy over time. And please feel free to reach out to me directly if you have concerns or feedback on the project.