diff --git a/src/Surface.zig b/src/Surface.zig index 3117a4dbe..66f5dee70 100644 --- a/src/Surface.zig +++ b/src/Surface.zig @@ -2455,15 +2455,19 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool /// Call this to complete a clipboard request sent to apprt. This should /// only be called once for each request. The data is immediately copied so /// it is safe to free the data after this call. +/// +/// If "allow_unsafe" is false, then the data is checked for "safety" prior. +/// If unsafe data is detected, this will return error.UnsafePaste. Unsafe +/// data is defined as data that contains newlines, though this definition +/// may change later to detect other scenarios. pub fn completeClipboardRequest( self: *Surface, req: apprt.ClipboardRequest, data: []const u8, - force: bool, // Dialog has been shown, and ignoring unsafe pastes. + allow_unsafe: bool, ) !void { switch (req) { - .paste => try self.completeClipboardPaste(data, force), - // TODO: Support sanaization for OSC 52 + .paste => try self.completeClipboardPaste(data, allow_unsafe), .osc_52 => |kind| try self.completeClipboardReadOSC52(data, kind), } } @@ -2489,22 +2493,22 @@ fn startClipboardRequest( try self.rt_surface.clipboardRequest(loc, req); } -fn sanatizeClipboardPaste(data: []const u8) !void { - // Split into lines. - var lines = std.mem.splitSequence(u8, data, "\n"); - - // If there's only one line no need to proceed. - if (std.mem.eql(u8, lines.next().?, data)) return; - - // Warning popup. - return error.UnsafePaste; -} - -fn completeClipboardPaste(self: *Surface, data: []const u8, force: bool) !void { +fn completeClipboardPaste( + self: *Surface, + data: []const u8, + allow_unsafe: bool, +) !void { if (data.len == 0) return; - if (!force and self.config.clipboard_paste_protection) { - try sanatizeClipboardPaste(data); + // If we have paste protection enabled, we detect unsafe pastes and return + // an error. The error approach allows apprt to attempt to complete the paste + // before falling back to requesting confirmation. + if (self.config.clipboard_paste_protection and + !allow_unsafe and + !terminal.isSafePaste(data)) + { + log.info("potentially unsafe paste detected, rejecting until confirmation", .{}); + return error.UnsafePaste; } const bracketed = bracketed: { diff --git a/src/apprt/glfw.zig b/src/apprt/glfw.zig index 83be181ef..41bfae892 100644 --- a/src/apprt/glfw.zig +++ b/src/apprt/glfw.zig @@ -588,8 +588,8 @@ pub const Surface = struct { }, }; - // Complete our request - // TODO: Support sanaization for GLFW (force: false) + // Complete our request. We always allow unsafe because we don't + // want to deal with user confirmation in this runtime. try self.core_surface.completeClipboardRequest(state, str, true); } diff --git a/src/config/Config.zig b/src/config/Config.zig index 3319f9d09..fbb92e5ba 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -423,8 +423,11 @@ keybind: Keybinds = .{}, /// This does not affect data sent to the clipboard via "clipboard-write". @"clipboard-trim-trailing-spaces": bool = true, -/// Creates a pop-up window when active, warning the user that they are pasting -/// contents that contains more than one line. This could be a "copy paste attack" +/// Require confirmation before pasting text that appears unsafe. This helps +/// prevent a "copy/paste attack" where a user may accidentally execute unsafe +/// commands by pasting text with newlines. +/// +/// This currently only works on Linux (GTK). @"clipboard-paste-protection": bool = true, /// The total amount of bytes that can be used for image data (i.e. diff --git a/src/terminal/main.zig b/src/terminal/main.zig index 8c0a7fc74..a752d64eb 100644 --- a/src/terminal/main.zig +++ b/src/terminal/main.zig @@ -1,5 +1,7 @@ const builtin = @import("builtin"); +pub usingnamespace @import("sanitize.zig"); + const charsets = @import("charsets.zig"); const stream = @import("stream.zig"); const ansi = @import("ansi.zig");