Repository navigation
[Bug] Desktop 0.2.1-rc.2: persistent terminal leaks PSReadLine prediction ghosts (shell history) into the model context #10070
Replies: 2 comments
|
Thanks for the correction pass — the negative result ( 1. Nothing in the package disables prediction — grep-verified. 2. Why the ghost survives — it is not "the sanitizer cannot tell faint from typed".
⇒ No rewrite of the sanitizer can delete a ghost, because "which text is still on screen" is a property of a screen, and erasure in a terminal is another redraw rather than a retraction. That is the whole reason your suggestion 3 is the only one that can work. 3. The faint/italic distinction is destroyed before any decision could use it. The CSI branch ( 4. Your race window is real, and it has a second consequence. 5. What works today, without a rebuild. Both options are safe because the prompt bootstrap is typed into the PTY after spawn (
Either closes the history half. The staircase stays, since it is a property of the filter, not of PSReadLine. 6. The oracle argument is the strongest part of the report — keep it. 7. Where the fix belongs. The cheap half is one line in the prompt bootstrap — core. The structural half is your suggestion 3, and the package already depends on Plugin-mountability, since it decides whether this could ship outside core: |
|
@argszero 感谢这轮回复 —— 流过滤器 vs 屏幕、 中文
EnglishThanks — the "stream filter vs a screen" framing, the
|
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Desktop 0.2.1-rc.2:持久终端把 PSReadLine 的预测「幽灵补全」当成输入显示(可能暴露 shell 历史)
摘要
实验性「持久终端」在 Windows + PowerShell 下,
terminal_send返回给模型的 viewport 会混入 PSReadLine inline prediction 的幽灵补全文本(faint/italic 的预览补全),以及每条命令的逐字符重绘帧。因此看到的「旧命令重复出现」不是跨 session 泄漏(见文末更正),而是:
ConsoleHost_history.txt里取历史做 inline prediction;TerminalSanitizer删除全部 ANSI 后,把幽灵文本与重绘帧当纯文本拼接。影响:模型侧 viewport 被污染;幽灵可能包含 shell 历史里的敏感命令(实测出现过
ssh-add $env:USERPROFILE\.ssh\id_ed25519),等于是把历史命令发给模型提供商。严重度 P1(信息暴露 / 数据外发;不影响命令执行)。
环境
desktop复现
@deepseek-ai/dsh-experimental-terminal-bundle)。terminal_send "echo AAA"。echo a×5(历史里的旧命令,作为预测幽灵)+echo A…echo AAA(逐字符重绘)+AAA。决定性实验
新开一个 session,先执行:
再发
echo ZZZ,viewport 变成:没有任何幽灵文本 ⇒ 根因确认是 inline prediction。
旁证
ConsoleHost_history.txt里含测试命令(echo a8 次、echo BBB、Get-Date等);Get-History只有 2 条(启动命令 + 本次命令)⇒ 不是 shell 进程复用;terminal_read历史隔离正常;terminal_open都是新 pwsh 进程。根因(三层叠加)
PredictionSource = HistoryAndPlugin(非交互-Command查询会显示None,容易误判为已关闭)。新 session 加载 OS 级共享的ConsoleHost_history.txt,把前缀匹配的历史命令画成 InlinePrediction(faint/italic)。TerminalSanitizer是流过滤器,不是屏幕(sanitize.ts:20-24自己写了 emulation deferred)::84-95)跳过参数、丢弃光标定位 ⇒ 逐键重绘的 6 帧拼在同一行(不是各占一行);normalizeTerminalText(:186-188)把每个\r改写成\n,但 raw 流里 CR 只出现在每次渲染结束时 ⇒ 只影响行提交,拆不开帧;\x08原样透传(不处理),但当前路径 BS=0 次,与本案无关。⇒ 任何 sanitizer 改写都删不掉幽灵:终端擦除是「另一次重绘」,不是撤回;「屏幕上还剩什么」是屏幕属性。
startSend在write之前就this.active = operation(session.ts:346),appendOutput无条件this.active?.append(text)(:537)⇒ 写入前到达的任何字节都会进新 send 的 viewport。竞态只影响结算时机(且:393在 write 前还有第二次resetReadinessEvidence,普通 send 的宽窗口不成立)。结果:预测文本 + 逐字符重绘帧被拼接进 viewport / motd / read 历史。
影响
安全(数据外发)
terminal_send的 viewport ⇒ 成为模型上下文的一部分 ⇒ 随下一次 LLM 请求发送到模型提供商端;ConsoleHost_history.txt,可能包含凭据相关命令、内网地址、私有路径;ssh/export/curl之类前缀,就能定向钓出历史命令,且不提交、不执行、不产生「读文件」类审计;terminal_send返回里出现过ssh-add $env:USERPROFILE\.ssh\id_ed25519(原始记录:out\terminal-cross-session-evidence.md);submit=false即可;暴露是持续的(上下文重发 + history 持续追加);可用性
terminal_sendviewport、terminal_openmotd、terminal_read历史被重复文本与幽灵补全污染;建议
PWSH_PROMPT_SETUP里加Set-PSReadLineOption -PredictionSource None(core 一行);@xterm/headless屏幕快照(包已依赖它),或解析 SGR 维护样式状态后按样式过滤 —— 后者可行,只是当前实现没做;shellArgs用法注意:非空会整体替换方言默认(config.ts:86-88),要重复-NoLogo -NoProfile。更正说明
初版把现象写成「新 session 带上一次关闭 session 的输入(跨会话泄漏 / 隔离问题)」。经复核,这个定性是错的:新 session 的 shell 是新进程、
Get-History干净、PTY 无复用;「旧命令」来自 PSReadLine 预测(读全局 history 文件)。关闭 prediction 后幽灵消失,实锤。English summary
On Desktop 0.2.1-rc.2 (Windows/PowerShell), the experimental persistent terminal returns PSReadLine inline-prediction "ghost" completions (faint/italic) and per-character redraw frames as if they were typed input. After
TerminalSanitizerstrips all ANSI, both get concatenated into the model-facing viewport/motd/history.This is not cross-session leakage (corrected): each
terminal_openspawns a new pwsh,Get-Historyis clean, and PTY buffers are not reused. The "old commands" come from PSReadLine prediction reading the OS-level sharedConsoleHost_history.txt. Proof: runningSet-PSReadLineOption -PredictionSource Nonemakesecho ZZZrender as plain progressive redraw with no ghost text.Impact: polluted model-side output, inflated tokens, and possible exposure of sensitive history entries (observed
ssh-add $env:USERPROFILE\.ssh\id_ed25519as a ghost completion). Execution itself is unaffected. Severity P2.Suggested fix: disable
PredictionSourceat startup, and/or make the sanitizer preserve semantics (distinguish ghost text and redraw frames) or use the bundled@xterm/headlessfor a screen snapshot.All reactions