Conversation
Bracketed paste (DEC mode 2004) fences pasted text between ESC[200~ and ESC[201~ so the application treats it as data. Pasted text was wrapped as-is, so clipboard content containing ESC[201~ closed the fence early and everything after it reached the application as typed input: the paste injection attack. Remove every ESC[201~ from the text before fencing it, repeating until none is left so a marker split around another cannot be reassembled. Native Ghostty never trusts such a paste either (isSafe in src/input/paste.zig). Both paste paths, the browser paste event and Terminal.paste(), now share one encoder. Unbracketed pastes are unchanged.
Pasted control characters can run commands in bash and zsh (CVE-2026-26982). Native Ghostty fixed this in v1.3.0 (ghostty-org/ghostty#10746) the way xterm does: NUL, BS, ENQ, EOT, ESC, DEL and the line-discipline characters (Ctrl+C, Ctrl+\, Ctrl+U, Ctrl+Z, Ctrl+Q, Ctrl+S, Ctrl+W, Ctrl+V, Ctrl+R, Ctrl+O) are replaced with spaces in either paste mode, since the pty line discipline acts on them even inside a bracketed paste. ghostty-web encodes pastes in TypeScript, so a newer Ghostty core would not have fixed it here. Port the same rule and Ghostty's test cases. Replacing ESC also means pasted text can no longer close the bracketed-paste fence with ESC[201~, which supersedes stripping that marker. Tabs, newlines and carriage returns are kept, as in Ghostty.
livenson
added a commit
to waldur/ghostty-web
that referenced
this pull request
Sep 13, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Pasted control characters can run commands in bash and zsh (CVE-2026-26982). Native Ghostty fixed this in v1.3.0 (ghostty-org/ghostty#10746), following xterm. ghostty-web encodes pastes in TypeScript rather than through Ghostty's
src/input/paste.zig, so it is still affected, and a newer Ghostty core alone would not fix it.A second, related problem: bracketed paste (DEC mode 2004) wraps pasted text in
ESC[200~…ESC[201~, and the text was wrapped as-is. Clipboard content containingESC[201~closed the bracket early, and everything after it reached the application as if typed. For example, pastingls\x1b[201~id\rinto a shell with bracketed paste enabled ranid.Change
lib/paste.tswithencodePaste(text, bracketed), a port of Ghostty 1.3.0'sencode. In either paste mode, it replaces these bytes with a space, the same list Ghostty copied from xterm:ls\x1b[201~id\rnow arrives as\x1b[200~ls [201~id\r\x1b[201~.InputHandler(the browserpasteevent andinsertFromPaste) andTerminal.paste().\nto\routside bracketed paste. That is not a security measure, so this PR leaves it as it was.Testing
lib/paste.test.tscovers the encoder, including the cases from Ghostty'spaste.zigtests. There are also bracketed-paste cases ininput-handler.test.tsandterminal.test.ts.bun run fmt && bun run lint && bun run typecheck && bun test && bun run buildall pass, with tests run against a WASM built from this tree (Zig 0.15.2).ClipboardEventwas checked against a build containing this change.#169 also reworks the bracketed-paste code in
terminal.ts; happy to rebase onto whichever lands first.