CVE-2026-8008
Overview
Background
- DevTools client trust
- the Chrome DevTools Protocol (CDP) marks front-ends as trusted (the real DevTools UI) or untrusted (e.g. extensions with the
debuggerpermission), read viachannel->GetClient()->IsTrusted(). - `PageHandler`
- the browser-side handler implementing the CDP
Pagedomain, instantiated per session when the domain is available to the client. - `Page.setSPCTransactionMode`
- an experimental command that puts the Secure Payment Confirmation dialog into an auto-accept/auto-reject mode, bypassing the UI where users review payee and amount.
- `Page.setRPHRegistrationMode`
- an experimental command that auto-accepts (or rejects) the custom protocol-handler (RPH) registration consent prompt.
Root Cause Analysis
This is the same commit referenced by CVE-2026-8006 (Fixed: 496426191, 496373088), framed here as an inappropriate-implementation/logic error: PageHandler implemented two consent-suppressing commands without conditioning their effect on the caller’s trust level. The implementation incorrectly treated SetSPCTransactionMode and SetRPHRegistrationMode as ordinary Page commands, so once the domain was exposed to an untrusted client the commands ran their dialog-mode logic unconditionally — the invariant that only a trusted client may override security-dialog behavior was never encoded in the methods.
The fix stores the connection trust bit in a new const bool is_trusted_ member (set from channel->GetClient()->IsTrusted() in ChromeDevToolsSession and passed through the PageHandler constructor) and makes each command early-return a permission-denied protocol::Response::ServerError(...) when !is_trusted_. Because the guard runs before the mode is applied, the corrected logic refuses untrusted callers outright.
is_trusted_ early-return in each method.Attack Path
- Attach an untrusted client
an extension with the
debuggerpermission opens a CDP session on a page wherePageHandleris constructed for untrusted clients. - Call `Page.setSPCTransactionMode` pre-patch the command applied the requested transaction mode, auto-accepting SPC without the trusted review UI for payee/amount.
- Call `Page.setRPHRegistrationMode` pre-patch the command auto-accepted protocol-handler registration, bypassing the consent prompt.
- Abuse the suppressed consent
proceed with an unreviewed payment authorization or an unconsented handler registration (the concrete tampered-payment outcome is advisory-stated as possible and marked
(inferred)).
Impact Assessment
(inferred) from the advisory’s “could potentially” language, not demonstrated by the diff. No in-the-wild exploitation is indicated.Changed Functions
| Function | Change | Notes |
|---|---|---|
is_trusted_chrome/browser/devtools/protocol/page_handler.cc |
modified | |
ifchrome/browser/devtools/protocol/page_handler.cc |
modified |
Files Changed
chrome/browser/devtools/chrome_devtools_session.ccchrome/browser/devtools/protocol/page_handler.ccchrome/browser/devtools/protocol/page_handler.h
Audit Directions
- Mode/override commands without trust logicflag any CDP command whose effect is to change how a security dialog behaves; its implementation must check
is_trusted_/IsTrusted()internally, not rely on domain exposure. - State carried but not enforcedaudit handlers where a trust/permission flag is stored but only some methods consult it, leaving sibling methods unguarded.
- Experimental-command reviewexperimental CDP commands often ship before their trust model is settled; review newly added
Page/other-domain commands specifically for missing untrusted-client gating.
Patch
From d283fa55777a691531f3eceb8020dd890548e680 Mon Sep 17 00:00:00 2001
From: Danil Somsikov <dsv@chromium.org>
Date: Wed, 01 Apr 2026 01:21:37 -0700
Subject: [PATCH] Restrict Page.setSPCTransactionMode and Page.setRPHRegistrationMode to trusted DevTools clients
The experimental DevTools commands Page.setSPCTransactionMode and
Page.setRPHRegistrationMode lacked trust restrictions, allowing
untrusted DevTools clients (such as Chrome extensions with the
`debugger` permission) to automatically accept dialogs without user
interaction.
For Page.setSPCTransactionMode, this bypassed the primary UI where
users review transaction details (payee, amount) for Secure Payment
Confirmation (SPC), leaving only a generic OS-level biometric prompt
that lacks context and could potentially be exploited to authorize
tampered payments.
Similarly, Page.setRPHRegistrationMode could be exploited to silently
accept custom protocol handler registration prompts, bypassing user
consent.
Fixed: 496426191, 496373088
Change-Id: I49cafe716e5c81c3715f8bde30e579d4cc9a01b8
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7707756
Commit-Queue: Danil Somsikov <dsv@chromium.org>
Auto-Submit: Danil Somsikov <dsv@chromium.org>
Reviewed-by: Andrey Kosyakov <caseq@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1608355}
---
diff --git a/chrome/browser/devtools/chrome_devtools_session.cc b/chrome/browser/devtools/chrome_devtools_session.cc
index e01701b..07560364 100644
--- a/chrome/browser/devtools/chrome_devtools_session.cc
+++ b/chrome/browser/devtools/chrome_devtools_session.cc
@@ -61,7 +61,8 @@
if (IsDomainAvailableToUntrustedClient<PageHandler>() ||
channel->GetClient()->IsTrusted()) {
page_handler_ = std::make_unique<PageHandler>(
- agent_host, agent_host->GetWebContents(), &dispatcher_);
+ agent_host, agent_host->GetWebContents(), &dispatcher_,
+ channel->GetClient()->IsTrusted());
}
if (IsDomainAvailableToUntrustedClient<SecurityHandler>() ||
channel->GetClient()->IsTrusted()) {
diff --git a/chrome/browser/devtools/protocol/page_handler.cc b/chrome/browser/devtools/protocol/page_handler.cc
index c5c79bf..bc3122f4 100644
--- a/chrome/browser/devtools/protocol/page_handler.cc
+++ b/chrome/browser/devtools/protocol/page_handler.cc
@@ -37,8 +37,11 @@
PageHandler::PageHandler(scoped_refptr<content::DevToolsAgentHost> agent_host,
content::WebContents* web_contents,
- protocol::UberDispatcher* dispatcher)
- : agent_host_(agent_host), web_contents_(web_contents->GetWeakPtr()) {
+ protocol::UberDispatcher* dispatcher,
+ bool is_trusted)
+ : agent_host_(agent_host),
+ web_contents_(web_contents->GetWeakPtr()),
+ is_trusted_(is_trusted) {
protocol::Page::Dispatcher::wire(dispatcher, this);
}
@@ -86,6 +89,11 @@
protocol::Response PageHandler::SetSPCTransactionMode(
const protocol::String& mode) {
+ if (!is_trusted_) {
+ return protocol::Response::ServerError(
+ "Permission denied: Page.setSPCTransactionMode requires a trusted "
+ "client");
+ }
if (!web_contents_)
return protocol::Response::ServerError("No web contents to host a dialog.");
@@ -117,6 +125,11 @@
if (!web_contents_) {
return protocol::Response::ServerError("No web contents to host a dialog.");
}
+ if (!is_trusted_) {
+ return protocol::Response::ServerError(
+ "Permission denied: Page.setRPHRegistrationMode requires a trusted "
+ "client");
+ }
custom_handlers::RphRegistrationMode rph_mode =
custom_handlers::RphRegistrationMode::kNone;
diff --git a/chrome/browser/devtools/protocol/page_handler.h b/chrome/browser/devtools/protocol/page_handler.h
index 90b717f..4c2c12fc 100644
--- a/chrome/browser/devtools/protocol/page_handler.h
+++ b/chrome/browser/devtools/protocol/page_handler.h
@@ -31,7 +31,8 @@
public:
PageHandler(scoped_refptr<content::DevToolsAgentHost> agent_host,
content::WebContents* web_contents,
- protocol::UberDispatcher* dispatcher);
+ protocol::UberDispatcher* dispatcher,
+ bool is_trusted);
PageHandler(const PageHandler&) = delete;
PageHandler& operator=(const PageHandler&) = delete;
@@ -99,6 +100,7 @@
base::WeakPtr<content::WebContents> web_contents_;
bool enabled_ = false;
+ const bool is_trusted_;
base::WeakPtrFactory<PageHandler> weak_ptr_factory_{this};
};
Original Bug Report
Bypass of Secure Payment Confirmation dialog via Page.setSPCTransactionMode
Project Fortify, an experimental security project, has identified the following potential security issue.
Overview: The experimental DevTools command Page.setSPCTransactionMode lacks trust restrictions, allowing extensions with the debugger permission to auto-accept Secure Payment Confirmation (SPC) dialogs. This bypasses the primary UI where users review transaction details (payee, amount), leaving only a generic OS-level biometric prompt that lacks context.
Affected files:
chrome/browser/devtools/protocol/page_handler.ccchrome/browser/devtools/chrome_devtools_session.ccthird_party/blink/public/devtools_protocol/domains/Page.pdlcomponents/payments/content/secure_payment_confirmation_controller.cccomponents/payments/content/secure_payment_confirmation_app.cc
Estimated timestamp from git blame: 2025-12-05
Disclaimer
Please note: As an AI agent (Fortify), I do not currently have the ability to run code or build a working Proof of Concept. The following analysis and steps are potential, based on a rigorous static analysis of the Chromium codebase.
Description
The Secure Payment Confirmation (SPC) API relies on a browser-level dialog to securely display the transaction amount and payee to the user before they authorize the payment. However, the DevTools command Page.setSPCTransactionMode (intended for automated testing) is improperly exposed to untrusted DevTools clients, such as Chrome extensions with the debugger permission.
By setting the transaction mode to autoAccept, an attacker can completely bypass the browser’s SPC dialog. The user will only see the subsequent OS-level biometric prompt (e.g., Windows Hello or macOS Touch ID). Because OS-level prompts are generic and do not display transaction details, the user can be tricked into authorizing a tampered payment (e.g., with a highly inflated amount or different payee) that they never had the opportunity to review.
Technical Details
- Lack of Command Restriction: In
third_party/blink/public/devtools_protocol/domains/Page.pdl, thesetSPCTransactionModecommand is markedexperimentalbut lacks therestrictedattribute. - Untrusted Client Access: In
chrome/browser/devtools/chrome_devtools_session.cc,PageHandleris explicitly made available to untrusted clients. Therefore, extensions using thedebuggerAPI can invoke this command without anIsTrusted()block. - UI Bypass: When invoked with
autoAccept,PageHandler::SetSPCTransactionModesets the mode tokAutoAcceptin thePaymentRequestWebContentsManager. Later, whenSecurePaymentConfirmationController::SetupModelAndShowDialogIfApplicableprocesses aPaymentRequest, it checks this mode. SeeingkAutoAccept, it immediately callsOnConfirm()synchronously, skipping the UI review phase entirely and directly triggering the WebAuthn authenticator.
Potential Attacker Steps
- An attacker convinces a user to install a malicious Chrome extension requiring the
debuggerpermission. - The extension attaches the debugger to a tab on a merchant site where the user has an enrolled SPC credential.
- The extension sends the command to auto-accept SPC prompts:
chrome.debugger.sendCommand({tabId: targetTabId}, 'Page.setSPCTransactionMode', {mode: 'autoAccept'}) - The extension injects JavaScript (via
Runtime.evaluateor a content script) to initiate aPaymentRequest, maliciously modifying theamountorpayee_name. - The browser instantly bypasses the SPC dialog. The user only sees a generic OS-level biometric prompt (e.g., “Verify your identity”).
- Unaware of the tampered details, the user completes the biometric challenge, returning a valid cryptographic assertion for the fraudulent transaction.
Suggested Fix
The command should be restricted to trusted DevTools clients.
Modify third_party/blink/public/devtools_protocol/domains/Page.pdl to add the restricted attribute to setSPCTransactionMode:
experimental restricted command setSPCTransactionMode
Alternatively, enforce a strict IsTrusted() or equivalent security check within the implementation of PageHandler::SetSPCTransactionMode.
Evaluated with Chrome root at commit: 0eb4855bda702feaaa8b899336664f97e3df88b8
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. Please feel free to reach out to me if you have concerns or feedback.