Medium chrome Logic Error 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInappropriate implementation in Passwords
DescriptionInappropriate implementation in Passwords
ComponentPasswords
Bug ClassLogic Error
Tracker517779123
Fix commit7992d4e6d9bc (chromium/src) +6/-6
CISA KEVNot listed
CreditedGoogle
Disclosed2026-07-29

Changed Functions

FunctionChangeNotes
if
components/password_manager/core/browser/password_manager.cc
modified

Files Changed

  • chrome/browser/touch_to_fill/password_manager/password_generation/android/touch_to_fill_password_generation_controller.cc
  • chrome/browser/ui/passwords/password_generation_popup_controller_impl.cc
  • components/password_manager/core/browser/password_manager.cc
  • components/password_manager/ios/ios_password_manager_driver.mm
  • components/password_manager/ios/shared_password_controller.mm
From 7992d4e6d9bc97266f88f30ed1d8a8dca98a96a6 Mon Sep 17 00:00:00 2001
From: Maria Kazinova <[email protected]>
Date: Tue, 09 Jun 2026 09:00:02 -0700
Subject: [PATCH] [Passwords] Use subframe origin for password requirements lookup.

During password generation Chromium was retrieving password
requirements from PasswordRequirementsService using the tab's
top-level primary main-frame origin instead of the subframe's origin.

This CL resolves the discrepancy by querying the requirements service
with the last committed origin of the frame initiating the password
generation.
Specifically:
1. Updates PasswordGenerationPopupControllerImpl::GeneratePasswordValue (desktop)
2. Updates TouchToFillPasswordGenerationController::ShowTouchToFill (Android)
3. Updates SharedPasswordController (iOS)
4. Updates PasswordManager::OnPasswordFormsParsed (prefetching)

Also implements IOSPasswordManagerDriver::GetLastCommittedOrigin() to
support retrieving the frame's origin on iOS.

Fixed: 517779123
Change-Id: I1a07648f692655687d0d8843b188ad42a0acddae
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7897805
Reviewed-by: Mohamed Amir Yosef <[email protected]>
Commit-Queue: Maria Kazinova <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1644005}
---

diff --git a/chrome/browser/touch_to_fill/password_manager/password_generation/android/touch_to_fill_password_generation_controller.cc b/chrome/browser/touch_to_fill/password_manager/password_generation/android/touch_to_fill_password_generation_controller.cc
index d8c722f..0bc8860 100644
--- a/chrome/browser/touch_to_fill/password_manager/password_generation/android/touch_to_fill_password_generation_controller.cc
+++ b/chrome/browser/touch_to_fill/password_manager/password_generation/android/touch_to_fill_password_generation_controller.cc
@@ -61,7 +61,7 @@
 
   std::u16string generated_password =
       frame_driver_->GetPasswordGenerationHelper()->GeneratePassword(
-          web_contents_->GetLastCommittedURL().DeprecatedGetOriginAsURL(), type,
+          frame_driver_->GetLastCommittedOrigin().GetURL(), type,
           generation_element_data_.form_signature,
           generation_element_data_.field_signature,
           generation_element_data_.max_password_length);
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 01a1441..353202a 100644
--- a/chrome/browser/ui/passwords/password_generation_popup_controller_impl.cc
+++ b/chrome/browser/ui/passwords/password_generation_popup_controller_impl.cc
@@ -251,8 +251,8 @@
   if (current_generated_password_.empty() || state_ != kOfferGeneration) {
     current_generated_password_ =
         driver_->GetPasswordGenerationHelper()->GeneratePassword(
-            web_contents()->GetLastCommittedURL().DeprecatedGetOriginAsURL(),
-            generation_type, form_signature_, field_signature_, max_length_);
+            driver_->GetLastCommittedOrigin().GetURL(), generation_type,
+            form_signature_, field_signature_, max_length_);
   }
 }
 
diff --git a/components/password_manager/core/browser/password_manager.cc b/components/password_manager/core/browser/password_manager.cc
index 029b4d1..860638d 100644
--- a/components/password_manager/core/browser/password_manager.cc
+++ b/components/password_manager/core/browser/password_manager.cc
@@ -1112,7 +1112,7 @@
       driver ? driver->GetPasswordGenerationHelper() : nullptr;
   if (password_generation_manager) {
     password_generation_manager->PrefetchSpec(
-        client_->GetLastCommittedOrigin().GetURL());
+        driver->GetLastCommittedOrigin().GetURL());
   }
 }
 
diff --git a/components/password_manager/ios/ios_password_manager_driver.mm b/components/password_manager/ios/ios_password_manager_driver.mm
index 9d257fd..a544874 100644
--- a/components/password_manager/ios/ios_password_manager_driver.mm
+++ b/components/password_manager/ios/ios_password_manager_driver.mm
@@ -203,7 +203,7 @@
 }
 
 const url::Origin& IOSPasswordManagerDriver::GetLastCommittedOrigin() const {
-  NOTREACHED();
+  return security_origin_;
 }
 
 bool IOSPasswordManagerDriver::HasCrossOriginAncestor() const {
diff --git a/components/password_manager/ios/shared_password_controller.mm b/components/password_manager/ios/shared_password_controller.mm
index c4453bc..0637c89 100644
--- a/components/password_manager/ios/shared_password_controller.mm
+++ b/components/password_manager/ios/shared_password_controller.mm
@@ -997,7 +997,7 @@
 
   std::u16string generatedPassword =
       [_driverHelper PasswordGenerationHelper:frame]->GeneratePassword(
-          [self lastCommittedURL],
+          frame->GetSecurityOrigin().GetURL(),
           isManuallyTriggered ? PasswordGenerationType::kManual
                               : PasswordGenerationType::kAutomatic,
           formSignature, fieldSignature, maxLength);
Loading diff…

Original Bug Report

reported by [email protected]

Potential cross-origin password requirements leakage via main frame origin lookup

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 vulnerability in Chrome’s password generation logic allows cross-origin password requirements to leak and be influenced by an embedding main frame. When generating a password in a subframe, Chrome erroneously retrieves password requirements using the top-level main frame’s origin instead of the subframe’s origin. An attacker could potentially abuse this to force weak or predictable password generation requirements on embedded victim subframes.

Affected files:

  • chrome/browser/ui/passwords/password_generation_popup_controller_impl.cc
  • chrome/browser/touch_to_fill/password_manager/password_generation/android/touch_to_fill_password_generation_controller.cc

Estimated timestamp from git blame: 2018-06-12

Detailed Writeup

Root Cause Analysis

During password generation in cross-origin frames, Chrome queries the PasswordRequirementsService to retrieve requirements specifications (PasswordRequirementsSpec) used to constrain the generated password length and alphabet. However, both the desktop popup controller and the Android Touch-to-Fill controller erroneously pass the tab’s top-level primary main-frame origin to the generation helper rather than the origin of the subframe initiating the request.

Specifically, in chrome/browser/ui/passwords/password_generation_popup_controller_impl.cc:

void PasswordGenerationPopupControllerImpl::GeneratePasswordValue(
    PasswordGenerationType generation_type) {
  if (current_generated_password_.empty() || state_ != kOfferGeneration) {
    current_generated_password_ =
        driver_->GetPasswordGenerationHelper()->GeneratePassword(
            web_contents()->GetLastCommittedURL().DeprecatedGetOriginAsURL(),  // <-- Main frame origin is used
            generation_type, form_signature_, field_signature_, max_length_);
  }
}

Similarly, in chrome/browser/touch_to_fill/password_manager/password_generation/android/touch_to_fill_password_generation_controller.cc:

std::u16string generated_password =
    frame_driver_->GetPasswordGenerationHelper()->GeneratePassword(
        web_contents_->GetLastCommittedURL().DeprecatedGetOriginAsURL(), type,  // <-- Main frame origin is used
        generation_element_data_.form_signature, ...);

On iOS, in components/password_manager/ios/shared_password_controller.mm:

  std::u16string generatedPassword =
      [_driverHelper PasswordGenerationHelper:frame]->GeneratePassword(
          [self lastCommittedURL], // <-- Main frame URL is used
          isManuallyTriggered ? PasswordGenerationType::kManual
                              : PasswordGenerationType::kAutomatic,
          formSignature, fieldSignature, maxLength);

When PasswordRequirementsService::GetSpec is called, it checks its domain-keyed cache for the main-frame origin instead of the subframe’s origin:

PasswordRequirementsSpec PasswordRequirementsService::GetSpec(
    const GURL& main_frame_domain,
    autofill::FormSignature form_signature,
    autofill::FieldSignature field_signature) {
  ...
  auto iter_by_domain = specs_for_domains_.Get(main_frame_domain);
  if (iter_by_domain != specs_for_domains_.end()) {
    ...
  }
}

Potential Exploitation Scenario

An attacker embedding a victim registration page (https://victim.com/signup.html) inside an iframe on https://attacker.com could potentially leverage this behavior to enforce predictable password requirements:

  1. Database Seeding: The attacker seeds the Autofill crowdsourcing database with a weak/degraded requirement specification for attacker.com (e.g., restricting character classes or forcing specific symbol character sets).
  2. Subframe Rendering: The attacker embeds https://victim.com/signup.html in an iframe.
  3. Triggering Generation: When the user interacts with the password field in the victim’s iframe, the browser invokes password generation.
  4. Requirements Leakage: Instead of querying victim.com, Chrome retrieves the pre-cached degraded specification for the top-level domain attacker.com.
  5. Deterministic Password Generation: The password generator utilizes the attacker-controlled specification, generating a weak or deterministic password (e.g., consisting entirely of !) which is subsequently saved under victim.com in the user’s password manager, potentially allowing the attacker to compromise the user’s account.

Note: These are potential steps based on static code analysis; we do not have a running environment to construct or execute a live proof of concept.

Suggested Fix

The password generation controllers should utilize the initiating subframe’s actual origin rather than the main frame’s URL. The frame’s origin can be retrieved securely from the corresponding frame driver via driver_->GetLastCommittedOrigin().

For example, update PasswordGenerationPopupControllerImpl::GeneratePasswordValue to:

current_generated_password_ =
    driver_->GetPasswordGenerationHelper()->GeneratePassword(
        driver_->GetLastCommittedOrigin().GetURL(),
        generation_type, form_signature_, field_signature_, max_length_);

A similar change should be applied to the Android Touch-to-Fill and iOS controllers to ensure consistent use of the subframe’s origin.

Evaluated with Chrome root at commit: 5133b93d189b383c37805b1cf3a9d2dbfe8d7379


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