Medium chrome Logic Error 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactIncorrect security UI in WebAppInstalls
DescriptionIncorrect security UI in WebAppInstalls
ComponentWebAppInstalls
Bug ClassLogic Error
Tracker479326680
Fix commit637553f660ab (chromium/src) +46/-0
CISA KEVNot listed
CreditedBarath Stalin K
Disclosed2026-03-10

Files Changed

  • chrome/browser/ui/web_applications/web_app_menu_model.cc
  • ui/base/models/menu_model.cc
  • ui/base/models/menu_model.h
  • ui/menus/simple_menu_model.cc
  • ui/menus/simple_menu_model.h
  • ui/views/controls/menu/menu_item_view.cc
  • ui/views/controls/menu/menu_item_view.h
  • ui/views/controls/menu/menu_model_adapter.cc
From 637553f660ab74b759bc6848935df2a133311fab Mon Sep 17 00:00:00 2001
From: Mustafa Emre Acer <[email protected]>
Date: Thu, 26 Feb 2026 16:49:26 -0800
Subject: [PATCH] Render RTL URLs properly in the PWA page info menu

The PWA page info menu renders the App's origin as a minor text next
to the "App Info" menu item. Presently, this doesn't handle RTL
hostnames correctly. This CL fixes that by forcing directionality to
LTR.

Bug: 479326680
Change-Id: I421e2886e21dbdb9a8e8b298e3cebaa8de49955f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7608472
Reviewed-by: Dibyajyoti Pal <[email protected]>
Reviewed-by: Dana Fried <[email protected]>
Commit-Queue: Mustafa Emre Acer <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1591194}
---

diff --git a/chrome/browser/ui/web_applications/web_app_menu_model.cc b/chrome/browser/ui/web_applications/web_app_menu_model.cc
index e1d05c40..6957c75eb 100644
--- a/chrome/browser/ui/web_applications/web_app_menu_model.cc
+++ b/chrome/browser/ui/web_applications/web_app_menu_model.cc
@@ -180,6 +180,8 @@
           browser()->app_controller()->GetAppShortName();
       // For Isolated Web Apps, |GetAppShortName()| must be non-empty.
       display_text = short_name;
+    } else {
+      SetMinorTextIsUrlAt(app_info_index, true);
     }
     SetMinorText(app_info_index, display_text);
   }
diff --git a/ui/base/models/menu_model.cc b/ui/base/models/menu_model.cc
index 0ec16f06..264435ec 100644
--- a/ui/base/models/menu_model.cc
+++ b/ui/base/models/menu_model.cc
@@ -75,6 +75,10 @@
   return std::u16string();
 }
 
+bool MenuModel::GetMinorTextIsUrlAt(size_t index) const {
+  return false;
+}
+
 std::u16string MenuModel::GetSecondaryLabelAt(size_t index) const {
   return std::u16string();
 }
diff --git a/ui/base/models/menu_model.h b/ui/base/models/menu_model.h
index 4d2f358..3f92c25 100644
--- a/ui/base/models/menu_model.h
+++ b/ui/base/models/menu_model.h
@@ -86,6 +86,10 @@
   // is rendered to the right of the label and using the font GetLabelFontAt().
   virtual std::u16string GetMinorTextAt(size_t index) const;
 
+  // Returns true if the minor text at the specified index should be treated as
+  // a URL when rendering the menu item.
+  virtual bool GetMinorTextIsUrlAt(size_t index) const;
+
   // Returns the minor icon of the item at the specified index. The minor icon
   // is rendered to the left of the minor text.
   virtual ImageModel GetMinorIconAt(size_t index) const;
diff --git a/ui/menus/simple_menu_model.cc b/ui/menus/simple_menu_model.cc
index 156e506..069f500 100644
--- a/ui/menus/simple_menu_model.cc
+++ b/ui/menus/simple_menu_model.cc
@@ -355,6 +355,10 @@
   items_[ValidateItemIndex(index)].minor_text = minor_text;
 }
 
+void SimpleMenuModel::SetMinorTextIsUrlAt(size_t index, bool is_url) {
+  items_[ValidateItemIndex(index)].minor_text_is_url = is_url;
+}
+
 void SimpleMenuModel::SetMinorIcon(size_t index,
                                    const ui::ImageModel& minor_icon) {
   items_[ValidateItemIndex(index)].minor_icon = minor_icon;
@@ -462,6 +466,10 @@
   return items_[ValidateItemIndex(index)].minor_text;
 }
 
+bool SimpleMenuModel::GetMinorTextIsUrlAt(size_t index) const {
+  return items_[ValidateItemIndex(index)].minor_text_is_url;
+}
+
 ImageModel SimpleMenuModel::GetMinorIconAt(size_t index) const {
   return items_[ValidateItemIndex(index)].minor_icon;
 }
diff --git a/ui/menus/simple_menu_model.h b/ui/menus/simple_menu_model.h
index 8ddab95..78e2be4 100644
--- a/ui/menus/simple_menu_model.h
+++ b/ui/menus/simple_menu_model.h
@@ -227,6 +227,9 @@
   // Sets the minor text for the item at |index|.
   void SetMinorText(size_t index, const std::u16string& minor_text);
 
+  // Sets whether the minor text at |index| should be rendered as a URL.
+  void SetMinorTextIsUrlAt(size_t index, bool is_url);
+
   // Sets the minor icon for the item at |index|.
   void SetMinorIcon(size_t index, const ui::ImageModel& minor_icon);
 
@@ -277,6 +280,8 @@
   int GetCommandIdAt(size_t index) const override;
   std::u16string GetLabelAt(size_t index) const override;
   std::u16string GetMinorTextAt(size_t index) const override;
+  bool GetMinorTextIsUrlAt(size_t index) const override;
+
   ImageModel GetMinorIconAt(size_t index) const override;
   bool IsItemDynamicAt(size_t index) const override;
   // First defers to the delegate's GetAcceleratorForCommandId() method to
@@ -325,6 +330,7 @@
     std::u16string label;
     ui::Accelerator accelerator;
     std::u16string minor_text;
+    bool minor_text_is_url = false;
     ImageModel minor_icon;
     ImageModel icon;
     int group_id = -1;
diff --git a/ui/views/controls/menu/menu_item_view.cc b/ui/views/controls/menu/menu_item_view.cc
index 25653e1..e22ac23 100644
--- a/ui/views/controls/menu/menu_item_view.cc
+++ b/ui/views/controls/menu/menu_item_view.cc
@@ -551,6 +551,11 @@
   invalidate_dimensions();  // Triggers preferred size recalculation.
 }
 
+void MenuItemView::SetMinorTextIsUrl(bool is_url) {
+  minor_text_is_url_ = is_url;
+  invalidate_dimensions();  // Triggers preferred size recalculation.
+}
+
 void MenuItemView::SetMinorIcon(const ui::ImageModel& minor_icon) {
   minor_icon_ = minor_icon;
   invalidate_dimensions();  // Triggers preferred size recalculation.
@@ -1255,6 +1260,9 @@
     render_text->SetDisplayRect(minor_text_bounds);
     render_text->SetHorizontalAlignment(base::i18n::IsRTL() ? gfx::ALIGN_LEFT
                                                             : gfx::ALIGN_RIGHT);
+    if (GetMinorTextIsUrl()) {
+      render_text->SetDirectionalityMode(gfx::DIRECTIONALITY_AS_URL);
+    }
     render_text->Draw(canvas);
   }
 
@@ -1526,6 +1534,10 @@
              : minor_text_;
 }
 
+bool MenuItemView::GetMinorTextIsUrl() const {
+  return minor_text_is_url_;
+}
+
 ui::ImageModel MenuItemView::GetMinorIcon() const {
   return minor_icon_;
 }
diff --git a/ui/views/controls/menu/menu_item_view.h b/ui/views/controls/menu/menu_item_view.h
index c1b9193d..f620243 100644
--- a/ui/views/controls/menu/menu_item_view.h
+++ b/ui/views/controls/menu/menu_item_view.h
@@ -271,6 +271,9 @@
   // Sets the minor text.
   void SetMinorText(const std::u16string& minor_text);
 
+  // Sets whether the minor text should be rendered as a URL.
+  void SetMinorTextIsUrl(bool is_url);
+
   // Sets the minor icon.
   void SetMinorIcon(const ui::ImageModel& minor_icon);
 
@@ -527,6 +530,9 @@
   // item. This will be the accelerator (if one exists).
   std::u16string GetMinorText() const;
 
+  // Returns true if the minor text should be rendered as a URL.
+  bool GetMinorTextIsUrl() const;
+
   // Returns the icon that should be displayed to the left of the minor text.
   ui::ImageModel GetMinorIcon() const;
 
@@ -663,6 +669,7 @@
   std::u16string title_;
   std::u16string secondary_title_;
   std::u16string minor_text_;
+  bool minor_text_is_url_ = false;
   ui::ImageModel minor_icon_;
 
   // Does the title have a mnemonic? Only useful on the root menu item.
diff --git a/ui/views/controls/menu/menu_model_adapter.cc b/ui/views/controls/menu/menu_model_adapter.cc
index 3b477ca..9f78999d 100644
--- a/ui/views/controls/menu/menu_model_adapter.cc
+++ b/ui/views/controls/menu/menu_model_adapter.cc
@@ -139,6 +139,9 @@
   if (model->IsAlertedAt(model_index)) {
     menu_item_view->SetAlerted();
   }
+  if (model->GetMinorTextIsUrlAt(model_index)) {
+    menu_item_view->SetMinorTextIsUrl(true);
+  }
   menu_item_view->set_new_badge_type(model->GetNewBadgeTypeAt(model_index));
Loading diff…

Original Bug Report

reported by [email protected]

Incorrect Origin Display in PWA Install Prompt When Using RTL Characters


Report description

Chrome Mac - Incorrect Origin Display in PWA Install Prompt When Using RTL Characters


Bug location

Where do you want to report your vulnerability?

Chrome VRP – Report security issues affecting the Chrome browser. See program rules


The problem

Please describe the technical details of the vulnerability

Summary

When a domain containing RTL (Right-to-Left) Unicode characters is used, the PWA install prompt displays an incorrect and misleading origin. Instead of showing the real origin “https://summa.sbs”, the install UI renders it as “https://xn--mgb.google.com”, causing a reversal and reordering of the hostname. This results in origin confusion during a high-trust installation flow.

Steps to Reproduce

  1. Open the POC URL in the Chrome MacOS - https://xn--mgb.google.com.xn--mgb.yen.summa.sbs/webapp.html
  2. Trigger the PWA install prompt.
  3. Observe the origin displayed in the install UI,App Info and Uninstall prompt

Expected Result

The install prompt should display the canonical, correctly ordered punycode origin: https://xn--mgb.google.com.xn--mgb.yen.summa.sbs

Actual Result

The install prompt displays a visually reordered and misleading origin: https://yen.summa.sbs.xn--mgb.google.com.xn–mgb This misrepresentation makes the origin appear related to a trusted domain.

Impact analysis

This issue enables origin spoofing during the PWA installation flow, where users rely heavily on the displayed origin to make trust decisions. An attacker can craft a malicious PWA using RTL characters to visually impersonate a trusted brand and trick users into installing it. Once installed, the PWA runs in a standalone, address-bar-less context, increasing the risk of credential phishing, persistent UI deception, and long-term user compromise. The impact is amplified because installation is a one-time trust action with lasting consequences.


The cause

What version of Chrome have you found the security issue in?

144.0.7559.110 (Official Build) (arm64)

No, it is not related to a crash.

Choose the type of vulnerability

Security UI Spoofing

How would you like to be publicly acknowledged for your report?

Barath Stalin K( https://in.linkedin.com/in/barathstalin)

View on issue tracker