client: confirm an unbracketed paste that carries control bytes, not only line breaks #217

Closed
opened 2026-10-21 13:19:46 +09:00 by natsukium · 0 comments
Owner

Problem

With bracketed paste off, a paste reaches the program byte for byte (frame_paste in crates/felis-daemon/src/serve/streaming.rs returns the bytes unchanged when ?2004 is off), and the REQ-804 gate (paste_needs_confirmation in crates/felis-client-core/src/confirm.rs) fires only on \n or \r. A control byte other than a line break therefore lands as a typed key with no bar:

  • ^O runs the line in bash/readline (operate-and-get-next) and zsh (accept-line-and-down-history), so rm -rf ~/x^O executes without a newline.
  • ^C cancels the line or the foreground job, ^D on an empty prompt exits the shell, ^U kills the line, ^X^E opens $EDITOR.
  • ESC sequences arrive as keys (history recall, Alt bindings).

Bracketed paste off is a normal state: bash with enable-bracketed-paste off, a shell after a TUI crashed or reset, cat / read, or a program that sent ?2004l (logged per REQ-902, not blocked). Reasoned from the source; not yet reproduced in a window.

Repro: printf '\e[?2004l' in a shell, then paste text containing ^O or ESC with no line break.

Peers

  • kitty paste_actions (default confirm) asks before pasting text with any C0 control other than newline, space or tab.
  • Ghostty replaces dangerous C0 bytes in both modes and confirms an unsafe paste (src/input/paste.zig, clipboard-paste-protection).
  • foot strips C0 controls from an unbracketed paste (selection.c).

Proposed fix (0.3.x)

Widen the REQ-804 gate on the client: with ?2004 off, a paste containing any C0 control other than HT raises the bar, not only one containing \n / \r. The bar text generalizes beyond "Paste N lines?". Confirmed bytes still ship verbatim.

frame_paste stays as is, so felis sessions send TEXT, the bridge, and PipeTarget::Paste keep the behavior docs/reference/cli.md documents (controls stripped only under ?2004). Stripping controls on the daemon side in unbracketed mode was rejected for a patch release because it changes that automation surface.

Cascade: REQ-804 wording, docs/explanation/input.md "Confirmation bar", CHANGELOG (Fixed). Tests in confirm.rs: ^O, ESC, ^C without a newline raise the bar; HT alone does not.

## Problem With bracketed paste off, a paste reaches the program byte for byte (`frame_paste` in `crates/felis-daemon/src/serve/streaming.rs` returns the bytes unchanged when `?2004` is off), and the REQ-804 gate (`paste_needs_confirmation` in `crates/felis-client-core/src/confirm.rs`) fires only on `\n` or `\r`. A control byte other than a line break therefore lands as a typed key with no bar: - `^O` runs the line in bash/readline (operate-and-get-next) and zsh (accept-line-and-down-history), so `rm -rf ~/x^O` executes without a newline. - `^C` cancels the line or the foreground job, `^D` on an empty prompt exits the shell, `^U` kills the line, `^X^E` opens `$EDITOR`. - `ESC` sequences arrive as keys (history recall, Alt bindings). Bracketed paste off is a normal state: bash with `enable-bracketed-paste off`, a shell after a TUI crashed or `reset`, `cat` / `read`, or a program that sent `?2004l` (logged per REQ-902, not blocked). Reasoned from the source; not yet reproduced in a window. Repro: `printf '\e[?2004l'` in a shell, then paste text containing `^O` or `ESC` with no line break. ## Peers - kitty `paste_actions` (default `confirm`) asks before pasting text with any C0 control other than newline, space or tab. - Ghostty replaces dangerous C0 bytes in both modes and confirms an unsafe paste (`src/input/paste.zig`, `clipboard-paste-protection`). - foot strips C0 controls from an unbracketed paste (`selection.c`). ## Proposed fix (0.3.x) Widen the REQ-804 gate on the client: with `?2004` off, a paste containing any C0 control other than HT raises the bar, not only one containing `\n` / `\r`. The bar text generalizes beyond "Paste N lines?". Confirmed bytes still ship verbatim. `frame_paste` stays as is, so `felis sessions send TEXT`, the bridge, and `PipeTarget::Paste` keep the behavior `docs/reference/cli.md` documents (controls stripped only under `?2004`). Stripping controls on the daemon side in unbracketed mode was rejected for a patch release because it changes that automation surface. Cascade: REQ-804 wording, `docs/explanation/input.md` "Confirmation bar", CHANGELOG (Fixed). Tests in `confirm.rs`: `^O`, `ESC`, `^C` without a newline raise the bar; HT alone does not.
Sign in to join this conversation.
No milestone
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
felis-terminal/felis#217
No description provided.