CVE-2026-13998
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifcomponents/remote_cocoa/app_shim/select_file_dialog_bridge.mm |
modified |
Files Changed
components/remote_cocoa/app_shim/select_file_dialog_bridge.mm
Patch
From bf4170bc5dc1d0dddf26d57645aa3c8fcd9aaf90 Mon Sep 17 00:00:00 2001 From: Avi Drissman <[email protected]> Date: Mon, 18 May 2026 12:13:16 -0700 Subject: [PATCH] Prevent key repeats from triggering the save panel To address https://crbug.com/40085079, https://crrev.com/c/875522 added a delegate to prevent an open panel from triggering due to a held-down Enter key. This is a good idea, so extend this to the save panel as well. Fixed: 514070501 Link: https://chromium-review.googlesource.com/id/I03a6e330b6a5ffb2b8f7540fa559c3a16a6a6964 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7856762 Commit-Queue: Bryan Oltman <[email protected]> Reviewed-by: Bryan Oltman <[email protected]> Auto-Submit: Avi Drissman <[email protected]> Cr-Commit-Position: refs/heads/main@{#1632327} --- diff --git a/components/remote_cocoa/app_shim/select_file_dialog_bridge.mm b/components/remote_cocoa/app_shim/select_file_dialog_bridge.mm index 6531f80..decff13 100644 --- a/components/remote_cocoa/app_shim/select_file_dialog_bridge.mm +++ b/components/remote_cocoa/app_shim/select_file_dialog_bridge.mm @@ -168,11 +168,29 @@ } // namespace -// A bridge class to act as the modal delegate to the save/open sheet and send -// the results to the C++ class. +// ----- SelectFileDialogDelegate ----- + @interface SelectFileDialogDelegate : NSObject <NSOpenSavePanelDelegate> @end +@implementation SelectFileDialogDelegate + +- (BOOL)panel:(id)sender validateURL:(NSURL*)url error:(NSError**)outError { + // Refuse to accept users closing the dialog with a key repeat, since the key + // may have been first pressed while the user was looking at insecure content. + // See https://crbug.com/40085079 and https://crbug.com/514070501. + auto currentEvent = NSApp.currentEvent; + if (currentEvent.type == NSEventTypeKeyDown && currentEvent.ARepeat) { + return NO; + } + + return YES; +} + +@end + +// ----- ExtensionDropdownHandler ----- + // Target for NSPopupButton control in file dialog's accessory view. @interface ExtensionDropdownHandler : NSObject { @private @@ -192,22 +210,6 @@ - (void)popupAction:(id)sender; @end -@implementation SelectFileDialogDelegate - -- (BOOL)panel:(id)sender validateURL:(NSURL*)url error:(NSError**)outError { - // Refuse to accept users closing the dialog with a key repeat, since the key - // may have been first pressed while the user was looking at insecure content. - // See https://crbug.com/40085079. - if (NSApp.currentEvent.type == NSEventTypeKeyDown && - NSApp.currentEvent.ARepeat) { - return NO; - } - - return YES; -} - -@end - @implementation ExtensionDropdownHandler - (instancetype)initWithDialog:(NSSavePanel*)dialog @@ -426,9 +428,6 @@ open_dialog.canChooseFiles = YES; open_dialog.canChooseDirectories = NO; } - - delegate_ = [[SelectFileDialogDelegate alloc] init]; - open_dialog.delegate = delegate_; } if (default_dir) { panel_.directoryURL = [NSURL fileURLWithPath:default_dir]; @@ -437,6 +436,11 @@ panel_.nameFieldStringValue = default_filename; } + // Ensure that key-repeat events do not trigger the dialog. See the class + // comment on |SelectFileDialogDelegate|. + delegate_ = [[SelectFileDialogDelegate alloc] init]; + panel_.delegate = delegate_; + // Ensure that |callback| (rather than |this|) be retained by the block. auto ended_callback = base::BindRepeating( &SelectFileDialogBridge::OnPanelEnded, weak_factory_.GetWeakPtr());
Original Bug Report
Potential macOS NSSavePanel keyjacking allows unauthorized file write via held Enter key
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: The macOS implementation of the file save dialog (NSSavePanel) in Chromium is missing a keyjacking guard that prevents automated dialog confirmation via held keys. A malicious website could potentially exploit this to obtain a persistent File System Access write handle by inducing a user to hold the ‘Enter’ key during a file picker request. This bypasses the intended user review step as the dialog is immediately confirmed by the repeating key events.
Affected files:
components/remote_cocoa/app_shim/select_file_dialog_bridge.mmui/shell_dialogs/select_file_dialog_mac.mmchrome/browser/file_system_access/chrome_file_system_access_permission_context.cccontent/browser/file_system_access/file_system_access_manager_impl.cc
Estimated timestamp from git blame: 2018-01-22
Summary
On macOS, Chromium’s implementation of the native file selection dialogs uses a delegate (SelectFileDialogDelegate) to provide security checks. One such check is designed to prevent ‘keyjacking’—where a malicious site tricks a user into holding down a key (like ‘Enter’) to immediately and accidentally confirm a security-sensitive dialog.
The protection is implemented in components/remote_cocoa/app_shim/select_file_dialog_bridge.mm within the panel:validateURL:error: method, which rejects confirmation if the triggering event is a key repeat (ARepeat). However, this delegate is currently only assigned to NSOpenPanel instances (used for opening files or folders). For NSSavePanel (used by window.showSaveFilePicker()), the delegate is never assigned, leaving the dialog vulnerable to automated confirmation via held keys.
Potential Impact
An attacker could obtain a persistent read/write handle to a new or existing file in a user-accessible directory (like ‘Downloads’ or ‘Desktop’) without the user’s conscious consent. By social engineering a user to hold the ‘Return’ or ‘Enter’ key (e.g., as part of a game or a ‘Hold to continue’ prompt), a malicious page can call showSaveFilePicker(). The held key satisfies the transient user activation requirement and, due to the missing guard, immediately triggers the ‘Save’ action on the resulting native macOS sheet.
If the suggested filename is unique and the extension is considered ‘safe’ (e.g., .txt), Chromium will return a writable handle to the origin without further security prompts. This allows the attacker to write arbitrary content to the user’s local disk.
Vulnerability Details
In components/remote_cocoa/app_shim/select_file_dialog_bridge.mm, the SelectFileDialogDelegate implements the following protection:
- (BOOL)panel:(id)sender validateURL:(NSURL*)url error:(NSError**)outError {
// Refuse to accept users closing the dialog with a key repeat
if (NSApp.currentEvent.type == NSEventTypeKeyDown &&
NSApp.currentEvent.ARepeat) {
return NO;
}
return YES;
}
In the Show method of SelectFileDialogBridge, the delegate is only assigned in the branch handling Open dialogs:
// components/remote_cocoa/app_shim/select_file_dialog_bridge.mm
if (type_ == SelectFileDialogType::kSaveAsFile) {
panel_ = [NSSavePanel savePanel];
// ... [NSSavePanel specific config] ...
// NOTE: delegate_ is NOT assigned here.
} else {
panel_ = [NSOpenPanel openPanel];
// ...
delegate_ = [[SelectFileDialogDelegate alloc] init];
open_dialog.delegate = delegate_; // Correctly assigned for OpenPanel
}
Potential Steps to Reproduce
- Use a macOS system with Chromium.
- Navigate to a malicious page that induces the user to hold the ‘Enter’ key (e.g., via a full-screen overlay or game mechanic).
- While the user is holding the key, the page executes:
window.showSaveFilePicker({ suggestedName: 'config.txt', startIn: 'downloads' }); - Observe that the native macOS Save sheet appears and immediately disappears (confirmed) due to the repeating ‘Enter’ events.
- The page now has a
FileSystemFileHandleand can perform writes to~/Downloads/config.txtwithout the user having seen or interacted with the dialog.
Suggested Fix
The SelectFileDialogDelegate should be instantiated and assigned to panel_.delegate for all dialog types, including kSaveAsFile. This ensures the ARepeat check is active for both Open and Save operations.
Evaluated with Chrome root at commit: 1a8d40fc44df2088d5945c0bf53584038aa1614a
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.