Overview

Low
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInappropriate implementation in Passwords
DescriptionInappropriate implementation in Passwords
ComponentPasswords
Bug ClassLogic Error
Tracker522419819
Fix commit60478d1d9fd8 (chromium/src) +3/-1
CISA KEVNot listed
CreditedGoogle
Disclosed2026-07-29

Files Changed

  • chrome/browser/ui/webui/password_manager/promo_cards/move_passwords_promo.cc
From 60478d1d9fd8d78ac0551173581523087a909164 Mon Sep 17 00:00:00 2001
From: Andrii Natiahlyi <[email protected]>
Date: Thu, 11 Jun 2026 04:54:19 -0700
Subject: [PATCH] Escape primary account email in Move Passwords promo description.

Fixed: 522419819
Change-Id: I50b47bb9c4b565ebc16344a5dafa08477660b3de
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7921943
Reviewed-by: Adem Derinel <[email protected]>
Auto-Submit: Andrii Natiahlyi <[email protected]>
Commit-Queue: Adem Derinel <[email protected]>
Commit-Queue: Andrii Natiahlyi <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1645279}
---

diff --git a/chrome/browser/ui/webui/password_manager/promo_cards/move_passwords_promo.cc b/chrome/browser/ui/webui/password_manager/promo_cards/move_passwords_promo.cc
index bbe2f60..e91b098d 100644
--- a/chrome/browser/ui/webui/password_manager/promo_cards/move_passwords_promo.cc
+++ b/chrome/browser/ui/webui/password_manager/promo_cards/move_passwords_promo.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ui/webui/password_manager/promo_cards/move_passwords_promo.h"
 
+#include "base/strings/escape.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/sync/sync_service_factory.h"
@@ -129,7 +130,8 @@
                    GetLocalPasswordsCount(delegate_.get()))
              : l10n_util::GetStringFUTF16(
                    IDS_PASSWORD_MANAGER_UI_MOVE_PASSWORDS_PROMO_CARD_DESCRIPTION,
-                   GetPrimaryAccountEmailFromProfile(profile_));
+                   base::EscapeForHTML(
+                       GetPrimaryAccountEmailFromProfile(profile_)));
 }
 
 std::u16string MovePasswordsPromo::GetActionButtonText() const {
Loading diff…

Original Bug Report

reported by [email protected]

Potential HTML link injection in chrome://password-manager Move-Passwords promo card

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: A potential sanitizer-bounded HTML injection vulnerability exists in the chrome://password-manager WebUI’s Move-Passwords promo card. By manipulating the primary account email via a compromised network process, a remote attacker can inject arbitrary links or basic formatting elements into the promo card description. This allows UI spoofing and link injection inside trusted, first-party Chrome WebUI pages.

Affected files:

  • chrome/browser/ui/webui/password_manager/promo_cards/move_passwords_promo.cc
  • chrome/browser/resources/password_manager/promo_cards/promo_card.ts
  • chrome/browser/resources/password_manager/promo_cards/promo_card.html

Estimated timestamp from git blame: 2024-01-09

Summary

A potential sanitizer-bounded HTML injection vulnerability exists in the chrome://password-manager WebUI’s Move-Passwords promo card. By manipulating the primary account’s email via a compromised network process, a remote attacker can potentially inject arbitrary links or basic HTML elements into the promo card description, enabling UI spoofing or phishing inside trusted browser UI.

Root Cause Analysis

In chrome/browser/ui/webui/password_manager/promo_cards/move_passwords_promo.cc at lines 124-133, MovePasswordsPromo::GetDescription() constructs the promo card’s description by substituting the signed-in account’s email into a localized string:

std::u16string MovePasswordsPromo::GetDescription() const {
  CHECK(profile_);
  return base::FeatureList::IsEnabled(switches::kPasswordUploadUiUpdate)
             ? l10n_util::GetPluralStringFUTF16(...)
             : l10n_util::GetStringFUTF16(
                   IDS_PASSWORD_MANAGER_UI_MOVE_PASSWORDS_PROMO_CARD_DESCRIPTION,
                   GetPrimaryAccountEmailFromProfile(profile_));
}

The helper function GetPrimaryAccountEmailFromProfile() returns the primary account’s email directly:

std::u16string GetPrimaryAccountEmailFromProfile(Profile* profile) {
  ...
  return base::UTF8ToUTF16(
      identity_manager->GetPrimaryAccountInfo(signin::ConsentLevel::kSignin)
          .email);
}

Because the email is substituted into the localized template via raw placeholder replacement (l10n_util::GetStringFUTF16), any HTML tags present in the email string are preserved without escaping.

When the user visits chrome://password-manager, PromoCardsHandler retrieves the available promo cards and returns the formatted description to the WebUI frontend via WebUI message passing (chrome/browser/ui/webui/password_manager/promo_cards_handler.cc:46-55).

On the frontend, promo_card.html renders the description via inner-h-t-m-l:

<div id="description" class="cr-primary-text label"
    inner-h-t-m-l="[[getDescription_(promoCard)]]"></div>

And promo_card.ts filters the description using sanitizeInnerHtml() (which calls parseHtmlSubset() in ui/webui/resources/js/parse_html_subset.ts):

private getDescription_(): TrustedHTML {
  return sanitizeInnerHtml(this.promoCard.description);
}

Because parseHtmlSubset permits standard formatting tags (e.g., <a>, <b>, <i>), and allows href attributes on <a> tags starting with chrome:// or https://, any anchor tags injected into the email string will survive sanitization and be rendered as clickable, live links within trusted first-party UI.

Suggested/Potential Exploit Scenario

Note: These are suggested/potential steps; our tooling doesn’t yet have the ability to run code.

  1. An attacker compromises the network process (representing the standard threat model boundary for network-to-browser exploits).
  2. When the user’s browser requests profile info from the Gaia /userinfo endpoint, the attacker intercepts the response and returns a crafted JSON payload containing a malicious "email" field containing HTML, for example: "email": "<a href='https://attacker.example/'>Verify your account</a>"
  3. The browser process ingests the payload via AccountInfoFromUserInfo() (components/signin/internal/identity_manager/account_info_util.cc), accepts it without format validation, and updates the primary account email.
  4. When the user opens chrome://password-manager (assuming they have at least one device-only password, making MovePasswordsPromo::ShouldShowPromo() true), the page fetches and displays the card.
  5. The description renders with a live, functional, first-party-styled link pointing to https://attacker.example/ or a sensitive internal page like chrome://settings/resetProfileSettings.

Suggested Fix

The email address must be escaped before being substituted into the localized string template. In chrome/browser/ui/webui/password_manager/promo_cards/move_passwords_promo.cc, include base/strings/escape.h and wrap the email in base::EscapeForHTML() before formatting:

#include "base/strings/escape.h"
...
l10n_util::GetStringFUTF16(
    IDS_PASSWORD_MANAGER_UI_MOVE_PASSWORDS_PROMO_CARD_DESCRIPTION,
    base::EscapeForHTML(GetPrimaryAccountEmailFromProfile(profile_)))

Alternatively, formatting can be refactored to perform the substitution on the WebUI/TypeScript side where htmlEscape or other WebUI sanitization routines can be applied.

Evaluated with Chrome root at commit: b2fea2e31df308d0f04e4ae47def4c4f939ee141


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