aboutsummaryrefslogtreecommitdiff
path: root/src/raw_input.rs
diff options
context:
space:
mode:
authorakbash <akbash@herdr.dev>2026-07-21 17:33:07 +0300
committerGitHub <noreply@github.com>2026-07-21 17:33:07 +0300
commit6382bd433a66683d29455409e1dbb16776727bbf (patch)
treebad2554aff913b03ccb6c36bc9dadce46bbec6e1 /src/raw_input.rs
parentfd29addd719e3451bea0a1cc20d93374d7bea2d9 (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.rs28
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 {