Medium chrome Logic Error 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInsufficient validation of untrusted input in Autofill
DescriptionInsufficient validation of untrusted input in Autofill
ComponentAutofill
Bug ClassLogic Error
Tracker506127858
Fix commit95d627b12d7a (chromium/src) +13/-0
CISA KEVNot listed
CreditedGoogle
Disclosed2026-06-02

Changed Functions

FunctionChangeNotes
TEST_F
components/autofill/core/browser/payments/payments_requests/get_bnpl_payment_instrument_for_fetching_url_request_unittest.cc
modified

Files Changed

  • components/autofill/core/browser/payments/payments_requests/get_bnpl_payment_instrument_for_fetching_url_request.cc
  • components/autofill/core/browser/payments/payments_requests/get_bnpl_payment_instrument_for_fetching_url_request_unittest.cc
From 95d627b12d7a51f8f10ed3992463332fb808359c Mon Sep 17 00:00:00 2001
From: Vinny Persky <[email protected]>
Date: Fri, 01 May 2026 10:12:26 -0700
Subject: [PATCH] Update BNPL redirects to enforce HTTP/HTTPS scheme

This CL ensures BNPL redirects enforce HTTP/HTTPS schemes, to prevent
potential malicious usage of the popup flow.

Fixed: 506127858
Change-Id: If7c275dd5cfd4debdbb6c4d1e9aec99abd1751f3
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7804653
Reviewed-by: Olivia Saul <[email protected]>
Commit-Queue: Vinny Persky <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1623891}
---

diff --git a/components/autofill/core/browser/payments/payments_requests/get_bnpl_payment_instrument_for_fetching_url_request.cc b/components/autofill/core/browser/payments/payments_requests/get_bnpl_payment_instrument_for_fetching_url_request.cc
index 14092a48..69a457b 100644
--- a/components/autofill/core/browser/payments/payments_requests/get_bnpl_payment_instrument_for_fetching_url_request.cc
+++ b/components/autofill/core/browser/payments/payments_requests/get_bnpl_payment_instrument_for_fetching_url_request.cc
@@ -104,6 +104,7 @@
 
 bool GetBnplPaymentInstrumentForFetchingUrlRequest::IsResponseComplete() {
   return response_details_.redirect_url.is_valid() &&
+         response_details_.redirect_url.SchemeIsHTTPOrHTTPS() &&
          response_details_.success_url_prefix.is_valid() &&
          response_details_.failure_url_prefix.is_valid() &&
          !response_details_.context_token.empty();
diff --git a/components/autofill/core/browser/payments/payments_requests/get_bnpl_payment_instrument_for_fetching_url_request_unittest.cc b/components/autofill/core/browser/payments/payments_requests/get_bnpl_payment_instrument_for_fetching_url_request_unittest.cc
index 48854fe9..dc5f22a94 100644
--- a/components/autofill/core/browser/payments/payments_requests/get_bnpl_payment_instrument_for_fetching_url_request_unittest.cc
+++ b/components/autofill/core/browser/payments/payments_requests/get_bnpl_payment_instrument_for_fetching_url_request_unittest.cc
@@ -202,6 +202,18 @@
   EXPECT_FALSE(request_->IsResponseComplete());
 }
 
+TEST_F(GetBnplPaymentInstrumentForFetchingUrlRequestTest,
+       IsResponseComplete_ParseResponseCalled_NonHttpRedirectUrl) {
+  Dict response = GetFullResponse();
+  response.SetByDottedPath(
+      "buy_now_pay_later_info.get_redirect_url_response_"
+      "info.redirect_url",
+      "chrome://version/");
+  request_->ParseResponse(response);
+
+  EXPECT_FALSE(request_->IsResponseComplete());
+}
+
 TEST_F(GetBnplPaymentInstrumentForFetchingUrlRequestTest, RespondToDelegate) {
   Dict response_dict = Dict().Set(
       "buy_now_pay_later_info",
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/components/autofill/core/browser/payments/payments_requests/get_bnpl_payment_instrument_for_fetching_url_request_unittest.cc b/components/autofill/core/browser/payments/payments_requests/get_bnpl_payment_instrument_for_fetching_url_request_unittest.cc
index 48854fe9..dc5f22a94 100644
--- a/components/autofill/core/browser/payments/payments_requests/get_bnpl_payment_instrument_for_fetching_url_request_unittest.cc
+++ b/components/autofill/core/browser/payments/payments_requests/get_bnpl_payment_instrument_for_fetching_url_request_unittest.cc
@@ -202,6 +202,18 @@
   EXPECT_FALSE(request_->IsResponseComplete());
 }
 
+TEST_F(GetBnplPaymentInstrumentForFetchingUrlRequestTest,
+       IsResponseComplete_ParseResponseCalled_NonHttpRedirectUrl) {
+  Dict response = GetFullResponse();
+  response.SetByDottedPath(
+      "buy_now_pay_later_info.get_redirect_url_response_"
+      "info.redirect_url",
+      "chrome://version/");
+  request_->ParseResponse(response);
+
+  EXPECT_FALSE(request_->IsResponseComplete());
+}
+
 TEST_F(GetBnplPaymentInstrumentForFetchingUrlRequestTest, RespondToDelegate) {
   Dict response_dict = Dict().Set(
       "buy_now_pay_later_info",
Loading diff…

Original Bug Report

reported by [email protected]

Lack of scheme validation for BNPL redirect_url allows privileged URL navigation

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 without the Chrome Security team. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.

Overview: The Buy Now Pay Later (BNPL) flow does not validate the scheme of the redirect_url returned from the Google payments server. A compromised network process can supply a privileged URL (e.g., devtools:// or chrome://), which the browser will open as a browser-initiated navigation. This leads to a potential network-to-browser sandbox escape.

Affected files:

  • components/autofill/core/browser/payments/payments_requests/get_bnpl_payment_instrument_for_fetching_url_request.cc
  • chrome/browser/ui/autofill/payments/desktop_payments_window_manager.cc
  • chrome/browser/ui/autofill/payments/android_payments_window_manager.cc
  • components/autofill/core/browser/payments/bnpl_manager.cc
  • chrome/browser/ui/android/autofill/payments/payments_window_bridge.cc
  • chrome/browser/ui/android/ephemeraltab/java/src/org/chromium/chrome/browser/ephemeraltab/EphemeralTabMediator.java

Estimated timestamp from git blame: 2025-02-07

Summary

A vulnerability exists in the Buy Now Pay Later (BNPL) payment flow where the redirect_url provided by the payments server is not validated against a scheme allowlist. Because the subsequent navigation is treated as browser-initiated, an attacker who can modify the network response (e.g., from a compromised sandboxed network process) can force the browser to navigate to highly privileged URL schemes like devtools://, chrome://, or file://. This provides a potential network-to-browser sandbox escape primitive.

Technical Details

  1. Parsing: When the user initiates a BNPL checkout, the browser sends a GetBnplPaymentInstrumentForFetchingUrlRequest. The response is parsed in GetBnplPaymentInstrumentForFetchingUrlRequest::ParseResponse() (in components/autofill/core/browser/payments/payments_requests/get_bnpl_payment_instrument_for_fetching_url_request.cc). The redirect_url string is extracted and converted to a GURL.
  2. Lack of Validation: The only validation occurs in IsResponseComplete(), which checks response_details_.redirect_url.is_valid(). GURL::is_valid() returns true for privileged schemes like devtools:// and chrome://. There is no check to restrict the scheme to http or https.
  3. Propagation: The URL is passed back to BnplManager::OnRedirectUrlFetched(), which places it into a PaymentsWindowManager::BnplContext and calls GetPaymentsWindowManager()->InitBnplFlow(context).
  4. Navigation (Desktop): In DesktopPaymentsWindowManager::CreatePopup(), a NavigateParams object is constructed with the malicious URL. The NavigateParams constructor defaults to is_renderer_initiated = false and provides no initiator_origin. The navigation is executed via Navigate(&params).
  5. Navigation (Android): In AndroidPaymentsWindowManager::CreateTab(), the flow uses PaymentsWindowBridge::OpenEphemeralTab(), which calls through JNI to PaymentsWindowCoordinator.openEphemeralTab(), explicitly passing a null initiatorOrigin to the ephemeral tab mediator, which then calls mWebContents.getNavigationController().loadUrl(params).
  6. Sandbox Escape: Because the navigation is browser-initiated, standard security restrictions (like ChildProcessSecurityPolicy checks for renderer-initiated navigations) are bypassed. The browser successfully opens the privileged URL in a new popup or ephemeral tab.

Potential Attacker Steps

(Note: These are suggested steps based on static analysis.)

  1. The attacker compromises the Chromium network process (which is sandboxed) or establishes an enterprise MITM position.
  2. The user initiates a BNPL payment flow via Autofill on a merchant website.
  3. The attacker intercepts the response from https://payments.google.com/payments/apis-secure/chromepaymentsservice/getpaymentinstrument.
  4. The attacker modifies the JSON response to inject a malicious scheme into the get_redirect_url_response_info: "redirect_url": "devtools://..." or "redirect_url": "chrome://...".
  5. The browser parses the response and opens a new popup or tab navigating to the privileged URL, allowing the attacker to interact with powerful internal browser surfaces.

Suggested Fix

Modify GetBnplPaymentInstrumentForFetchingUrlRequest::IsResponseComplete() or ParseResponse() to explicitly require the redirect_url (as well as success_url_prefix and failure_url_prefix) to have an http or https scheme. For example:

bool IsValidBnplUrl(const GURL& url) {
  return url.is_valid() && url.SchemeIsHTTPOrHTTPS();
}

Evaluated with Chrome root at commit: 3acbde3302da0cb19488c22c0eb007c791207b4b


Results so far have been promising, but there can be wrong deductions. If this proves to be a false positive, please close as WAI; 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