Files
ghostty/macos/Sources/Features/ClipboardConfirmation/ClipboardConfirmationController.swift
Mitchell Hashimoto d695ffff3b macos: defer OSC52 clipboard read confirmations until focused
Fixes #10077

Clipboard read confirmations would immediately show a sheet which
grabbed focus. This could be used for a bunch of dumb reasons, including 
DoS attacks. But, it also caused focus/sheet loops for programs that did
OSC52 on focus changes (which was seen via some Neovim configs!).

Now, if a surface is unfocused, we bell the surface and show the confirmation
request on next focus. If the surface is not focused or another request
comes in, we cancel the prior one.

This also fixes some memory management issues around clipboard requests
that were likely small leaks (didn't verify the old bug, but verified
the new code, and eyeballed the old).
2026-08-11 06:44:23 -07:00

61 lines
1.9 KiB
Swift

import Foundation
import Cocoa
import SwiftUI
/// This initializes a clipboard confirmation warning window. The window itself
/// WILL NOT show automatically and the caller must show the window via
/// showWindow, beginSheet, etc.
class ClipboardConfirmationController: NSWindowController {
override var windowNibName: NSNib.Name? { "ClipboardConfirmation" }
private(set) var confirmation: Ghostty.ClipboardConfirmationRequest
weak private var delegate: ClipboardConfirmationViewDelegate?
init(
confirmation: Ghostty.ClipboardConfirmationRequest,
delegate: ClipboardConfirmationViewDelegate
) {
self.confirmation = confirmation
self.delegate = delegate
super.init(window: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) is not supported for this view")
}
/// Replace the request represented by the visible sheet without changing
/// the sheet's focus state. The previous request is cancelled by its
/// SurfaceView before this method is called.
func replaceConfirmation(with confirmation: Ghostty.ClipboardConfirmationRequest) {
guard self.confirmation !== confirmation else { return }
self.confirmation = confirmation
guard isWindowLoaded, let window else { return }
configure(window)
}
// MARK: - NSWindowController
override func windowDidLoad() {
guard let window = window else { return }
configure(window)
}
private func configure(_ window: NSWindow) {
switch confirmation.kind {
case .paste:
window.title = "Warning: Potentially Unsafe Paste"
case .osc_52_read, .osc_52_write:
window.title = "Authorize Clipboard Access"
}
window.contentView = NSHostingView(rootView: ClipboardConfirmationView(
contents: confirmation.contents,
request: confirmation.kind,
delegate: delegate
))
}
}