diff options
| author | akbash <akbash@herdr.dev> | 2026-07-21 17:33:07 +0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-07-21 17:33:07 +0300 |
| commit | 6382bd433a66683d29455409e1dbb16776727bbf (patch) | |
| tree | bad2554aff913b03ccb6c36bc9dadce46bbec6e1 /src/raw_input.rs | |
| parent | fd29addd719e3451bea0a1cc20d93374d7bea2d9 (diff) | |
fix: reject oversized text pastes safely (#1676)
refs #1665
Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
Diffstat (limited to 'src/raw_input.rs')
| -rw-r--r-- | src/raw_input.rs | 28 |
1 files changed, 28 insertions, 0 deletions
diff --git a/src/raw_input.rs b/src/raw_input.rs index a6ca316a..191688c3 100644 --- a/src/raw_input.rs +++ b/src/raw_input.rs @@ -107,6 +107,21 @@ pub(crate) const GHOSTTY_COLOR_SCHEME_LIGHT_REPORT: &[u8] = b"\x1b[?997;2n"; const BRACKETED_PASTE_START: &[u8] = b"\x1b[200~"; const BRACKETED_PASTE_END: &[u8] = b"\x1b[201~"; +/// Returns whether `data` is exactly one complete bracketed-paste sequence. +/// +/// Client transport uses this to distinguish recoverable oversized interactive +/// pastes from generic oversized input, which remains a protocol violation. +pub(crate) fn is_complete_text_bracketed_paste(data: &[u8]) -> bool { + if !data.starts_with(BRACKETED_PASTE_START) { + return false; + } + let Some(end) = find_subsequence(data, BRACKETED_PASTE_END) else { + return false; + }; + end + BRACKETED_PASTE_END.len() == data.len() + && std::str::from_utf8(&data[BRACKETED_PASTE_START.len()..end]).is_ok() +} + #[derive(Debug)] pub enum RawInputEvent { Key(TerminalKey), @@ -1135,6 +1150,19 @@ mod tests { } #[test] + fn complete_text_bracketed_paste_requires_one_exact_utf8_sequence() { + assert!(is_complete_text_bracketed_paste(b"\x1b[200~hello\x1b[201~")); + assert!(!is_complete_text_bracketed_paste(b"\x1b[200~hello")); + assert!(!is_complete_text_bracketed_paste( + b"\x1b[200~hello\x1b[201~rest" + )); + assert!(!is_complete_text_bracketed_paste( + b"\x1b[200~one\x1b[201~\x1b[200~two\x1b[201~" + )); + assert!(!is_complete_text_bracketed_paste(b"\x1b[200~\xff\x1b[201~")); + } + + #[test] fn parses_sgr_mouse() { let (RawInputEvent::Mouse(mouse), consumed) = extract_one_event(b"\x1b[<0;20;10M").unwrap() else { |
