Overview

Low
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInappropriate implementation in UI
DescriptionInappropriate implementation in UI
ComponentUI
Bug ClassLogic Error
Tracker519710361
Fix commitf0e142a719fd (chromium/src) +20/-2
CISA KEVNot listed
CreditedGoogle
Disclosed2026-07-29

Changed Functions

FunctionChangeNotes
if
content/browser/android/selection/selection_popup_controller.cc
modified

Files Changed

  • content/browser/android/selection/selection_popup_controller.cc
  • content/public/android/java/src/org/chromium/content/browser/input/ImeAdapterImpl.java
From f0e142a719fd48c201eb5bd976a13f0944591f62 Mon Sep 17 00:00:00 2001
From: Yaron Friedman <yfriedman@google.com>
Date: Thu, 04 Jun 2026 11:36:30 -0700
Subject: [PATCH] Android: Fix selection menu password exposure (b/519710361)

When a password field is toggled to text, it becomes type="text" but
still contains sensitive data. We should not show ACTION_PROCESS_TEXT
handlers for it.

This CL checks the HAS_BEEN_PASSWORD_FIELD and
HAS_BEEN_CUSTOM_PASSWORD flags in both IME (Java) and Selection (C++)
layers to force the password state to true if the field has ever been
a password.

Bug: 519710361
Change-Id: I86e58ac46a7fb26d90be65aa745e877f58b85793
Fixed: 519710361
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7903878
Reviewed-by: Ted Choc <tedchoc@chromium.org>
Commit-Queue: Yaron Friedman <yfriedman@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1641829}
---

diff --git a/content/browser/android/selection/selection_popup_controller.cc b/content/browser/android/selection/selection_popup_controller.cc
index fefd6ea..b846c7e1 100644
--- a/content/browser/android/selection/selection_popup_controller.cc
+++ b/content/browser/android/selection/selection_popup_controller.cc
@@ -12,6 +12,7 @@
 #include "base/feature_list.h"
 #include "content/browser/android/selection/composited_touch_handle_drawable.h"
 #include "content/browser/renderer_host/render_widget_host_view_android.h"
+#include "content/browser/renderer_host/text_input_manager.h"
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/browser/web_contents/web_contents_view_android.h"
 #include "content/common/features.h"
@@ -21,6 +22,7 @@
 #include "third_party/blink/public/common/context_menu_data/edit_flags.h"
 #include "third_party/blink/public/mojom/context_menu/context_menu.mojom.h"
 #include "third_party/blink/public/mojom/input/input_handler.mojom.h"
+#include "ui/base/ime/text_input_flags.h"
 #include "ui/base/models/menu_model.h"
 #include "ui/base/mojom/menu_source_type.mojom.h"
 #include "ui/gfx/android/android_surface_control_compat.h"
@@ -300,8 +302,20 @@
       !!(params.edit_flags & blink::ContextMenuDataEditFlags::kCanSelectAll);
   const bool can_edit_richly =
       !!(params.edit_flags & blink::ContextMenuDataEditFlags::kCanEditRichly);
-  const bool is_password_type =
+  bool is_password_type =
       params.form_control_type == blink::mojom::FormControlType::kInputPassword;
+  if (rwhva_) {
+    TextInputManager* text_input_manager = rwhva_->GetTextInputManager();
+    if (text_input_manager) {
+      const ui::mojom::TextInputState* state =
+          text_input_manager->GetTextInputState();
+      if (state &&
+          (state->flags & (ui::TEXT_INPUT_FLAG_HAS_BEEN_PASSWORD |
+                           ui::TEXT_INPUT_FLAG_HAS_BEEN_CUSTOM_PASSWORD))) {
+        is_password_type = true;
+      }
+    }
+  }
   const ScopedJavaLocalRef<jstring> jselected_text =
       ConvertUTF16ToJavaString(env, params.selection_text);
   const bool should_suggest =
diff --git a/content/public/android/java/src/org/chromium/content/browser/input/ImeAdapterImpl.java b/content/public/android/java/src/org/chromium/content/browser/input/ImeAdapterImpl.java
index 0fb1507..c0ba05bc3 100644
--- a/content/public/android/java/src/org/chromium/content/browser/input/ImeAdapterImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/input/ImeAdapterImpl.java
@@ -64,6 +64,7 @@
 import org.chromium.blink.mojom.InputCursorAnchorInfo;
 import org.chromium.blink.mojom.StylusWritingGestureData;
 import org.chromium.blink_public.web.WebInputEventModifier;
+import org.chromium.blink_public.web.WebTextInputFlags;
 import org.chromium.blink_public.web.WebTextInputMode;
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.build.annotations.Nullable;
@@ -796,7 +797,10 @@
             }
 
             boolean editable = focusedNodeEditable();
-            boolean password = textInputType == TextInputType.PASSWORD;
+            boolean password =
+                    textInputType == TextInputType.PASSWORD
+                            || (textInputFlags & WebTextInputFlags.HAS_BEEN_PASSWORD_FIELD) != 0
+                            || (textInputFlags & WebTextInputFlags.HAS_BEEN_CUSTOM_PASSWORD) != 0;
             updateNodeAttributes(editable, password);
             if (mCursorAnchorInfoController != null
                     && (!TextUtils.equals(mLastText, text)
Loading diff…

Original Bug Report

reported by vm...@google.com

Potential Android selection menu password exposure to third-party ACTION_PROCESS_TEXT handlers

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: The Android selection popup controller fails to check the sticky HAS_BEEN_PASSWORD flag when evaluating if a text selection is in a password field. If a user toggles a password field’s visibility to plaintext and selects it, the plaintext credential is exposed to third-party ACTION_PROCESS_TEXT intent handlers. This bypasses Chrome’s security gate designed to keep sensitive credential selections isolated.

Affected files:

  • content/browser/android/selection/selection_popup_controller.cc
  • content/public/android/java/src/org/chromium/content/browser/selection/SelectionPopupControllerImpl.java
  • content/public/android/java/src/org/chromium/content/browser/input/ImeAdapterImpl.java
  • content/public/android/java/src/org/chromium/content/browser/selection/SelectActionMenuHelper.java

Estimated timestamp from git blame: 2015-09-25

Potential Vulnerability: Plaintext Password Exposure to Third-Party Android Handlers

Root Cause

The Android selection menu controller derives its password classification state solely from the active/current input element type, ignoring the sticky has-been-password flag (WebTextInputFlags.HAS_BEEN_PASSWORD_FIELD) propagated by Blink. Because of this, when a password field is revealed to plaintext (a common UX design pattern on login pages), both the native and Java layers of Chrome treat the field selection as ordinary text.

There are two main propagation sites where this occurs:

  1. Native (selection_popup_controller.cc):

    const bool is_password_type =
        params.form_control_type == blink::mojom::FormControlType::kInputPassword;
    

    is_password_type evaluates to false when the input type changes to text.

  2. Java (ImeAdapterImpl.java):

    boolean password = textInputType == TextInputType.PASSWORD; // mTextInputFlags is ignored here
    updateNodeAttributes(editable, password);
    

    password evaluates to false when the input type becomes TextInputType.TEXT.

Because the popup controller determines isSelectionPassword is false, it allows SelectActionMenuHelper.getTextProcessingItems() to query and show registered third-party ACTION_PROCESS_TEXT handlers in the selection popup menu. When the user taps one of these items, Chrome sends the raw selected plaintext password in the EXTRA_PROCESS_TEXT intent extra to the target third-party application.

Note: These are suggested/potential steps and analysis; our current tooling agent does not have the ability to run code or compile a working proof-of-concept.

Potential Trigger Steps

  1. An Android device has a third-party application installed that declares an <intent-filter> for android.intent.action.PROCESS_TEXT with mimeType text/plain.
  2. The user navigates to a login page containing a password field with a show/hide toggle in Chrome for Android.
  3. The user types their credential, then taps the visibility toggle to show the plaintext password.
  4. The user long-presses to select the revealed password text.
  5. The custom selection menu appears, exposing third-party application options (which would otherwise be suppressed if the field were recognized as having been a password field).
  6. The user taps the third-party application’s action. The external application receives the plaintext password via the ACTION_PROCESS_TEXT intent.

Suggested Fix

Update both the native and Java state-propagation logic to consult the sticky has-been-password bit:

  • In ImeAdapterImpl.java:
    boolean password = textInputType == TextInputType.PASSWORD 
        || (mTextInputFlags & WebTextInputFlags.HAS_BEEN_PASSWORD_FIELD) != 0;
    
  • In selection_popup_controller.cc, retrieve and consult the state of the active input’s has-been-password flag (which is already tracked in Blink) to correctly mark is_password_type as true if the field has ever been a password field.

Evaluated with Chrome root at commit: 9ebf4302210513a012c901d87a2668b3aadf8cc1


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.

View on issue tracker