Confirm unsafe pastes and programs' clipboard access #17

Closed
opened 2026-10-14 20:00:56 -05:00 by jeff · 0 comments
Owner

Ask before an unsafe paste, and before a program's clipboard access, with a confirmation: an overlay over the pane it concerns. The terms are in GLOSSARY.md.

Today

  • Nothing is checked. Window.paste (src/Window.zig, the s.stream.handler.paste call) and drag and drop (src/Dnd.zig) both pass allow_unsafe = true, so a line break in pasted text reaches a program without bracketed paste as Enter.
  • Ghostty's paste encoding, which harrier uses, already turns ESC, NUL, DEL and the termios control characters into spaces. A pasted ESC [201~ cannot end bracketed paste early, and control characters never arrive.
  • OSC 52 writes are honored with no prompt and no setting (Session.clipboardWrite, src/termio/Session.zig). OSC 52 reads are not supported: harrier never sets the clipboard_read effect.
  • There is no confirmation UI of any kind. The overlays (profile selector, search field) are the nearest thing.

Unsafe paste

  • A paste is unsafe when the text holds a line break and the program has not turned on bracketed paste (mode 2004). Nothing else counts. In particular ESC [201~ under bracketed paste does not count, because the ESC is already a space by then.
  • Checked on every route to a program: ctrl+shift+v (paste), shift+insert (paste_selection), middle-click, and drag and drop. Pastes into the profile selector and the search field are not checked; they drop control characters already.
  • The confirmation shows the text, as much as fits, with its line breaks made visible (⏎), and offers Paste and Cancel. Cancel is the default.
  • The text is never altered beyond what the encoding already does: the user's answer pastes exactly what was asked for, or nothing.

Clipboard access

  • Reading is new. On .ask, the confirmation names the pane, and the machine when the program is on another one, and offers Allow, Allow for this pane, and Deny. Deny is the default.
    • Allowed: the reply is the clipboard's text as it is when the user answers, base64-encoded, as OSC 52 has it.
    • Denied, or on .deny: the reply is empty (ESC ] 52 ; c ; ESC \), as xterm's is, so a program waiting for a reply gets one.
    • Allow for this pane lasts until the pane closes.
  • Writing is as now on .allow. On .ask, the same three answers, the confirmation showing the text to be written. A write denied is dropped; OSC 52 has no reply for one.
  • The location (c, p, s) is honored as it is for writes now.

The confirmation

  • An overlay over its pane, in the style of the profile selector. While it is open it takes that pane's keyboard; the pointer can click its buttons. Escape means Cancel or Deny; Enter means the highlighted button, which starts on the default.
  • It waits on its own pane. A question from a pane that is not shown — a hidden tab, a window without the focus — waits there: the tab gets a mark of its own (📋, not the bell's 🔔), and the window asks for the desktop's attention, as a rung bell does. Looking at the pane clears the mark.
  • There is no timeout.
  • A pane has at most one question waiting. A newer clipboard-access question replaces an older one, which is answered as denied. An unsafe paste is never replaced, since the user asked for it, in the pane they were looking at.
  • A pane closing with a question waiting answers it as denied.
  • The same on Wayland and Windows.

Settings

.clipboard = .{
    .read = .ask,          // .ask, .allow, .deny
    .write = .allow,       // .ask, .allow, .deny
    .confirm_unsafe_paste = true,
},

A profile may set clipboard_read and clipboard_write, overriding clipboard.read and clipboard.write for its panes, the way it sets bell_sound. confirm_unsafe_paste is global only: it is about the user, not about the program.

Not in this

Kitty's clipboard protocol (mode 5522), which reading would make possible: a separate issue.

Done looks like

  • The settings above, documented in the README, with config errors that name them.
  • An ADR recording that programs may now read the clipboard, behind a confirmation; harrier refused reads until now (the comment above Session.clipboardWrite).
  • Unit tests for the unsafe-paste rule and for the one-question-per-pane rule.
  • The sway VM test extended: an unsafe paste is asked about and cancelled and then pasted; an OSC 52 read is asked about, denied (empty reply) and then allowed (the clipboard's text); .deny answers without asking.
Ask before an **unsafe paste**, and before a program's **clipboard access**, with a **confirmation**: an overlay over the pane it concerns. The terms are in GLOSSARY.md. ## Today - Nothing is checked. `Window.paste` (src/Window.zig, the `s.stream.handler.paste` call) and drag and drop (src/Dnd.zig) both pass `allow_unsafe = true`, so a line break in pasted text reaches a program without bracketed paste as Enter. - Ghostty's paste encoding, which harrier uses, already turns ESC, NUL, DEL and the termios control characters into spaces. A pasted `ESC [201~` cannot end bracketed paste early, and control characters never arrive. - OSC 52 writes are honored with no prompt and no setting (`Session.clipboardWrite`, src/termio/Session.zig). OSC 52 reads are not supported: harrier never sets the `clipboard_read` effect. - There is no confirmation UI of any kind. The overlays (profile selector, search field) are the nearest thing. ## Unsafe paste - A paste is unsafe when the text holds a line break and the program has not turned on bracketed paste (mode 2004). Nothing else counts. In particular `ESC [201~` under bracketed paste does not count, because the ESC is already a space by then. - Checked on every route to a program: ctrl+shift+v (`paste`), shift+insert (`paste_selection`), middle-click, and drag and drop. Pastes into the profile selector and the search field are not checked; they drop control characters already. - The confirmation shows the text, as much as fits, with its line breaks made visible (⏎), and offers **Paste** and **Cancel**. Cancel is the default. - The text is never altered beyond what the encoding already does: the user's answer pastes exactly what was asked for, or nothing. ## Clipboard access - **Reading** is new. On `.ask`, the confirmation names the pane, and the machine when the program is on another one, and offers **Allow**, **Allow for this pane**, and **Deny**. Deny is the default. - Allowed: the reply is the clipboard's text as it is when the user answers, base64-encoded, as OSC 52 has it. - Denied, or on `.deny`: the reply is empty (`ESC ] 52 ; c ; ESC \`), as xterm's is, so a program waiting for a reply gets one. - **Allow for this pane** lasts until the pane closes. - **Writing** is as now on `.allow`. On `.ask`, the same three answers, the confirmation showing the text to be written. A write denied is dropped; OSC 52 has no reply for one. - The location (`c`, `p`, `s`) is honored as it is for writes now. ## The confirmation - An overlay over its pane, in the style of the profile selector. While it is open it takes that pane's keyboard; the pointer can click its buttons. Escape means Cancel or Deny; Enter means the highlighted button, which starts on the default. - It waits on its own pane. A question from a pane that is not shown — a hidden tab, a window without the focus — waits there: the tab gets a mark of its own (📋, not the bell's 🔔), and the window asks for the desktop's attention, as a rung bell does. Looking at the pane clears the mark. - There is no timeout. - A pane has at most one question waiting. A newer clipboard-access question replaces an older one, which is answered as denied. An unsafe paste is never replaced, since the user asked for it, in the pane they were looking at. - A pane closing with a question waiting answers it as denied. - The same on Wayland and Windows. ## Settings ``` .clipboard = .{ .read = .ask, // .ask, .allow, .deny .write = .allow, // .ask, .allow, .deny .confirm_unsafe_paste = true, }, ``` A profile may set `clipboard_read` and `clipboard_write`, overriding `clipboard.read` and `clipboard.write` for its panes, the way it sets `bell_sound`. `confirm_unsafe_paste` is global only: it is about the user, not about the program. ## Not in this Kitty's clipboard protocol (mode 5522), which reading would make possible: a separate issue. ## Done looks like - The settings above, documented in the README, with config errors that name them. - An ADR recording that programs may now read the clipboard, behind a confirmation; harrier refused reads until now (the comment above `Session.clipboardWrite`). - Unit tests for the unsafe-paste rule and for the one-question-per-pane rule. - The sway VM test extended: an unsafe paste is asked about and cancelled and then pasted; an OSC 52 read is asked about, denied (empty reply) and then allowed (the clipboard's text); `.deny` answers without asking.
jeff closed this issue 2026-10-14 22:11:32 -05:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
jeff/harrier#17
No description provided.