Skip to content

fix: paste not working in PTY sessions#89

Merged
inureyes merged 4 commits intomainfrom
feature/issue-88-fix-paste-pty
Dec 16, 2025
Merged

fix: paste not working in PTY sessions#89
inureyes merged 4 commits intomainfrom
feature/issue-88-fix-paste-pty

Conversation

@inureyes
Copy link
Member

@inureyes inureyes commented Dec 16, 2025

Summary

This PR fixes clipboard paste operations in PTY mode SSH sessions by implementing bracketed paste mode support and handling Event::Paste events.

Fixes #88

Changes

Terminal State Management (src/pty/terminal.rs)

  • Import EnableBracketedPaste and DisableBracketedPaste from crossterm
  • Enable bracketed paste mode in TerminalStateGuard::new()
  • Disable bracketed paste mode in restore_terminal_state() for proper cleanup

Input Event Handling (src/pty/session/input.rs)

  • Add Event::Paste(text) handler in handle_input_event()
  • Increase SmallVec capacity from 8 to 64 bytes to efficiently handle paste content
  • Convert pasted text to bytes and send to SSH channel
  • Add 8 comprehensive unit tests for paste functionality

PTY Message Types (src/pty/mod.rs)

  • Update PtyMessage::LocalInput to use SmallVec<[u8; 64]> for consistency

Testing

All 455 tests pass, including 8 new paste event tests:

  • test_paste_event_small - Small text paste (< 64 bytes)
  • test_paste_event_large - Large text paste (> 64 bytes)
  • test_paste_event_empty - Empty paste
  • test_paste_event_special_chars - Special characters and newlines
  • test_paste_event_unicode - Unicode characters
  • test_paste_event_multiline - Multi-line script paste
  • test_key_event_still_works - Regular key events still work
  • test_resize_event_ignored - Resize events still ignored

Platform Support

This fix enables paste operations on:

  • macOS: Cmd+V
  • Linux: Ctrl+Shift+V
  • Windows: Ctrl+V

Technical Details

  • Bracketed paste mode: Properly distinguishes paste operations from rapid keystrokes
  • Memory efficiency: SmallVec<[u8; 64]> keeps most paste content stack-allocated
  • Proper cleanup: Bracketed paste mode is disabled on session termination
  • Large paste support: Content exceeding 64 bytes is automatically heap-allocated

Test Plan

  • Unit tests for paste event handling pass
  • All existing tests (455) pass
  • Build succeeds in release mode
  • No clippy warnings
  • Manual testing on macOS with Cmd+V
  • Manual testing on Linux with Ctrl+Shift+V
  • Manual testing on Windows with Ctrl+V

Resolves clipboard paste issues in PTY SSH sessions by implementing
bracketed paste mode support and handling Event::Paste events.

Changes:
- Enable bracketed paste mode in TerminalStateGuard::new()
- Disable bracketed paste mode in restore_terminal_state()
- Add Event::Paste handler in handle_input_event()
- Increase SmallVec capacity from 8 to 64 bytes for paste content
- Update PtyMessage::LocalInput to use SmallVec<[u8; 64]>
- Add comprehensive unit tests for paste event handling

This fixes paste operations on all platforms (macOS Cmd+V, Linux
Ctrl+Shift+V, Windows Ctrl+V) by properly capturing and forwarding
pasted content through the SSH channel.

Tests: All 455 tests pass, including 8 new paste event tests
@inureyes inureyes added priority:high High priority issue status:review Under review type:bug Something isn't working type:enhancement New feature or request labels Dec 16, 2025
@inureyes
Copy link
Member Author

inureyes commented Dec 16, 2025

Security & Performance Review

Analysis Summary

  • Scope: changed-files (3 files)
  • Languages: Rust
  • Total issues: 5
  • Critical: 0 | High: 1 | Medium: 3 | Low: 1

Prioritized Issue List

HIGH (1 issue)

H1: Missing paste content size limit - potential memory exhaustion

  • File: src/pty/session/input.rs:37-41
  • Issue: The Event::Paste handler converts paste content directly to bytes without any size validation. A malicious or accidental paste of extremely large content (e.g., multi-GB clipboard) could cause memory exhaustion.
  • Impact: An attacker or user could paste extremely large content, causing the application to allocate unbounded memory, potentially leading to OOM conditions or system instability.
  • Current Code:
    Event::Paste(text) => {
        let bytes = text.into_bytes();
        Some(SmallVec::from_slice(&bytes))
    }
  • Recommendation: Add a size limit check (e.g., 1MB or configurable) before processing paste content. Log a warning and truncate or reject oversized pastes.

MEDIUM (3 issues)

M1: Empty paste returns Some(empty vec) instead of None

  • File: src/pty/session/input.rs:37-41
  • Issue: Empty paste events return Some(SmallVec::from_slice(&[])) rather than None, which is inconsistent with other event handlers (e.g., resize returns None for no-op). This causes unnecessary channel sends with empty data.
  • Impact: Minor inefficiency - sends empty messages through the channel when nothing needs to be transmitted.
  • Recommendation: Return None for empty paste content to avoid unnecessary processing.

M2: Channel overflow on large paste - silent data drop

  • File: src/pty/session/session_manager.rs:241-246
  • Issue: When try_send fails due to a full channel (256 message capacity), the input task breaks out of its loop entirely. For large pastes that fill the channel, this causes the session to terminate rather than gracefully handling backpressure.
  • Impact: During high-volume paste operations, the session may unexpectedly terminate instead of applying backpressure.
  • Recommendation: Consider implementing backpressure (blocking send with timeout) for paste operations specifically, or at minimum log a warning before terminating.

M3: Bracketed paste mode not enabled in new_without_raw_mode() path

  • File: src/pty/terminal.rs:94-104
  • Issue: The new_without_raw_mode() constructor does not enable bracketed paste mode, but there's no corresponding cleanup check. If terminal state is later modified, there could be asymmetry in paste mode handling.
  • Impact: Low - this path sets _needs_cleanup: false so cleanup won't run, but the inconsistency could cause confusion for future maintainers.
  • Recommendation: Add documentation clarifying that bracketed paste mode is intentionally not enabled in this path.

LOW (1 issue)

L1: Unnecessary memory copy in paste handler

  • File: src/pty/session/input.rs:37-41
  • Issue: The code calls text.into_bytes() (which consumes the String) and then SmallVec::from_slice(&bytes) which copies the data again. Since into_bytes() already produces a Vec<u8>, this results in an extra allocation and copy for pastes larger than 64 bytes.
  • Impact: Performance optimization opportunity - double allocation for large pastes.
  • Current Code:
    let bytes = text.into_bytes();
    Some(SmallVec::from_slice(&bytes))
  • Recommendation: Use SmallVec::from_vec(text.into_bytes()) to avoid the extra copy for heap-allocated content.

Positive Findings

The implementation has several good qualities:

  1. Proper terminal state cleanup: Bracketed paste mode is correctly disabled in restore_terminal_state() using the RAII pattern via Drop implementation.

  2. Comprehensive test coverage: 8 new tests cover various paste scenarios including empty, small, large, unicode, special characters, and multi-line content.

  3. SmallVec optimization: The 64-byte inline capacity is appropriate for most paste operations while still handling larger content via heap spillover.

  4. Escape sequence filtering: The existing EscapeSequenceFilter provides protection against terminal response sequences appearing in output (though paste input is a separate concern).

  5. Bounded channel: The PTY_MESSAGE_CHANNEL_SIZE of 256 provides reasonable backpressure protection.


Security Considerations

Paste content is NOT sanitized for escape sequences in input direction

The paste handler sends raw bytes directly to the SSH channel without filtering. This is correct behavior for a terminal emulator - paste content should be transmitted verbatim to the remote shell. The bracketed paste mode (\x1b[200~ ... \x1b[201~) that crossterm uses helps the remote shell distinguish paste from typed input, allowing it to handle any potentially dangerous content appropriately.

However, if the remote shell does not support bracketed paste mode, pasted content containing escape sequences could affect the remote terminal. This is a known limitation of terminal emulators and is not specific to this PR.


Manual Testing Checklist

  • Test paste of small text (< 64 bytes) - verify correct transmission
  • Test paste of large text (> 64 bytes, > 1KB, > 1MB) - verify memory behavior
  • Test paste with Unicode characters - verify encoding preservation
  • Test paste with control characters (Ctrl+C, etc.) - verify they're transmitted literally
  • Test rapid consecutive pastes - verify channel doesn't overflow
  • Test paste during session shutdown - verify no panic
  • Verify terminal state restored after session ends (bracketed paste disabled)

Summary

This PR correctly implements paste support with proper terminal state management. The main concern is H1 (missing size limit) which should be addressed before merge to prevent potential memory exhaustion. The medium and low issues are recommendations for robustness and efficiency improvements.

inureyes and others added 3 commits December 16, 2025 13:48
- Add 1MB size limit to prevent memory exhaustion attacks (H1 issue)
- Return None for empty paste to avoid unnecessary channel sends (M1 issue)
- Use from_vec() instead of into_bytes() + from_slice() to avoid double copy (L1 issue)
- Add test for paste size limit behavior

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add tests for all Ctrl+key combinations (C, D, Z, A, E, U, K, W, L, R, B, F)
- Add tests for arrow keys (Up, Down, Left, Right)
- Add tests for all function keys (F1-F12) and verify F13+ returns None
- Add tests for special keys (Enter, Tab, Backspace, Esc, Home, End, PageUp, PageDown, Insert, Delete)
- Add tests for KeyEventKind::Release and Repeat being ignored
- Add tests for modifier keys (Shift accepted, Alt/Meta ignored for chars)
- Add tests for Unicode and emoji character input
- Add test for mouse events returning None
- Add test for Ctrl+char out of range
@inureyes inureyes self-assigned this Dec 16, 2025
@inureyes inureyes changed the title Fix paste not working in PTY sessions fix: paste not working in PTY sessions Dec 16, 2025
@inureyes inureyes merged commit 3c4b1a7 into main Dec 16, 2025
3 checks passed
@inureyes inureyes deleted the feature/issue-88-fix-paste-pty branch December 16, 2025 05:24
inureyes added a commit that referenced this pull request Dec 16, 2025
- Fix terminal escape sequence responses displayed on first prompt when starting tmux (#90)
- Fix paste not working in PTY sessions (#89)
- Bump dependencies to latest versions
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

priority:high High priority issue status:review Under review type:bug Something isn't working type:enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix: paste not working in PTY sessions

1 participant