fix(pty): strip device-query requests and drain leaked stdin responses - #435
Conversation
|
Thanks for the pull request. A maintainer will review it when available. Please keep the PR focused, explain the why in the description, and make sure local checks pass before requesting review. Contribution guide: https://github.com/AI-Shell-Team/aish/blob/main/CONTRIBUTING.md |
|
Template check passed. Thanks for updating the pull request description. |
📝 WalkthroughWalkthroughThe change filters terminal query requests from normal PTY output and drains leaked terminal responses before readline. Interactive and session commands retain raw output through nested guards. The shell preserves remaining type-ahead when it removes terminal responses. ChangesTerminal response handling
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant PTY
participant Terminal
participant Readline
participant stdin
PTY->>Terminal: filter normal query requests
PTY->>Terminal: pass through interactive/session output
Terminal->>stdin: return terminal reports
Readline->>stdin: drain leading reports before reading
Readline->>Readline: invoke rustyline with preserved type-ahead
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@crates/aish-pty/src/persistent.rs`:
- Around line 100-124: Make device-query filtering stream-aware in both sites:
update strip_device_queries in crates/aish-pty/src/persistent.rs (lines 100-124)
to preserve incomplete CSI query candidates across PTY reads, resolving them
only when subsequent bytes confirm or reject the sequence while writing output
in order; update the raw-mode input handling in
crates/aish-shell/src/keyboard/raw_mode.rs (lines 104-108) to retain incomplete
report candidates across stdin reads and prevent their prefixes from reaching
rustyline until resolved.
In `@crates/aish-shell/src/keyboard/raw_mode.rs`:
- Around line 179-203: The report_prefix_len parser currently treats query
request sequences ending in n or t as discardable responses. Restrict
recognition to valid terminal response parameter shapes, excluding requests such
as ESC[5n, ESC[6n, and ESC[18t, while preserving existing response handling; add
tests asserting each request returns a prefix length of zero.
- Around line 91-104: Update the input-draining flow around
InputRawGuard::enter, set_stdin_nonblocking, stdin_readable_now, and
read_pending so raw mode and nonblocking I/O are established before the
readiness probe. Preserve the zero-timeout poll before any read, and return
early when no input is readable or raw-mode setup fails.
In `@crates/aish-shell/src/readline.rs`:
- Around line 747-750: Ensure crate::keyboard::drain_terminal_responses() runs
immediately before every readline call, including the continuation readline path
in the multiline input flow. Preserve the existing initial-prompt cleanup and
add the same cleanup to the continuation path so pending terminal responses
cannot reach rustyline.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 1764a7e2-2f0c-4fb7-883d-3878935a128a
📒 Files selected for processing (4)
crates/aish-pty/src/persistent.rscrates/aish-shell/src/keyboard/mod.rscrates/aish-shell/src/keyboard/raw_mode.rscrates/aish-shell/src/readline.rs
cf2abd1 to
5d1baa7
Compare
|
Note GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer. |
Terminal device-query responses (CPR/DA/DSR) leaked into stdin and were read by rustyline as key events, corrupting the input line with garbled fragments like `0;115;0cRRR`. Two-layer defense: 1. Output side (aish-pty write_stdout_all): strip forwarded device-query *request* sequences (CPR/DA1/DA2/DA3/DSR) so the real terminal is never asked to respond. Real TUI/remote programs (vim/less/ssh/...) go through a passthrough flag, now a reference-counted AtomicUsize so nested/ concurrent commands keep the outer guard armed until all inner guards drop. 2. Input side (aish-shell drain_terminal_responses): before each readline (including the multiline continuation path), enter raw mode + O_NONBLOCK, then non-blocking poll stdin. Raw mode is entered *before* the poll because canonical (cooked) mode only releases complete lines and a terminal report (ESC[2;2R) has no line delimiter — poll would otherwise never see it. Pending reports are read, the leading run of complete report sequences is dropped, and genuine type-ahead is reinjected via TIOCSTI. When TIOCSTI is rejected by the kernel and non-report bytes are lost, a warn (tail_len/reinjected) is logged for diagnosis. strip_device_queries distinguishes requests from responses: the request scan treats only '>'/'=' as private markers (not '?'), so DA1 responses `ESC[?64;1c` pass through; responses carry ';' which falls outside the digit scan and are preserved. Tests: strip_device_queries x7, report_prefix_len x8; clippy --all-targets -D warnings clean. Fixes AI-Shell-Team#434
5d1baa7 to
62e88ed
Compare
|
Note GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer. |
Summary
0;115;0cRRR)。详见 [Bug]: 终端设备查询响应泄漏进 stdin,破坏输入行(乱码注入) #434。Change Type
Scope
User-visible Changes
修复远程 ssh/telnet 会话、tmux/screen、man/less 等场景下输入行被终端响应乱码污染的问题。用户不再需要在 prompt 里手动清除
0;115;0c之类的残留片段。Compatibility
Testing
cargo test -p aish-pty strip_device_queries→ 7/7cargo test -p aish-shell report_prefix→ 8/8cargo clippy -p aish-pty --all-targets -- -D warnings→ 零警告cargo clippy -p aish-shell --all-targets -- -D warnings→ 零警告poll()对无换行的 report 返回不可读,raw 模式下可读——确认输入端必须在 poll 前进 raw 模式Checklist
实现细节
1. 输出端(
aish-ptywrite_stdout_all):剥离转发到真实终端的设备查询 请求(CPR/DA1/DA2/DA3/DSR),从源头阻止真实终端被诱导响应。真正的 TUI/远程程序(vim/less/ssh…)走 passthrough 透传,标志由bool改为引用计数AtomicUsize,修正嵌套/并发命令下内层 guard 提前关闭的隐患。2. 输入端(
aish-shelldrain_terminal_responses):每次 readline 前(含 multiline 续行>)进 raw 模式 +O_NONBLOCK再pollstdin。raw 模式必须在 poll 前——canonical 模式只按行释放输入,而 report 无行分隔符,poll 否则永远看不到。残留响应则丢弃前导完整 report 序列,剩余用户 type-ahead 用TIOCSTI重新注入;TIOCSTI被内核禁用且非 report 字节丢失时记warn(tail_len/reinjected)。strip_device_queries区分 请求 与 响应:请求 private marker 只认>/=(不认?),避开误删 DA1 响应ESC[?64;1c;响应参数带;自然落出 digit 扫描而被保留。Fixes #434