Skip to content
Shell & TerminalDeep Dive Published Updated 3 min readViews unavailable

Bracketed Paste Mode: How Terminals and Shells Mark Pasted Text Safely

How bracketed paste wraps pasted bytes, how line editors use the markers, where trust ends, and how terminal programs enable, parse, disable, and test it.

Bracketed paste mode lets a compatible terminal wrap pasted content in distinct control sequences. A compatible line editor can then treat the entire paste as inserted text instead of executing each embedded newline immediately. It reduces accidental command execution, but it does not make pasted commands trustworthy or prevent a malicious terminal from forging input.

The application opts in

An interactive application sends the xterm private-mode sequence for bracketed paste when it is ready to parse the corresponding markers, and disables the mode when leaving. In readable notation, the terminal sends a “paste start” control sequence, the raw pasted bytes, then a “paste end” sequence. The exact bytes are documented by xterm and implemented by many terminal emulators.

A line editor such as GNU Readline can insert newlines into the editable buffer, quote/control-display them, or wait for explicit confirmation. Without bracketed mode, pasted newline bytes are indistinguishable from the user pressing Enter. Shell behavior therefore depends on terminal support, the active line editor, its configuration, and whether the program is actually in an interactive editing mode.

Do not enable the terminal mode from a shell startup file unless the active reader can parse the markers. Otherwise the escape bytes may appear as literal characters in commands. Full-screen or custom REPL programs should own the mode while active and return it to the prior/default state on exit.

Parsing is a streaming problem

Input reads can split a marker across several chunks or combine marker, content, and end marker in one read. A correct parser maintains a small state machine and a bounded prefix buffer. It does not call strstr() once on each read and assume packet boundaries.

The body can contain newlines, tabs, control characters, invalid text encoding, and byte sequences resembling a marker. Terminal protocols are not authenticated. Define whether the program escapes control bytes, rejects oversized pastes, normalizes line endings, or allows a literal end-marker sequence. Never feed paste bytes to eval or another parser before showing the user the resulting command.

Pastes can be enormous. Cap buffer size, keep the UI responsive, and offer cancellation. A multi-megabyte clipboard should not exhaust a small terminal client. If the application supports multiline execution, display a clear boundary and require an explicit action to run rather than letting the final newline become implicit consent.

Security improvement has limits

Bracketed paste helps against the classic mistake of pasting several lines from a web page and immediately running the first line. It does not detect hidden Unicode, misleading comments, carriage returns, terminal escape sequences, or a command that is visibly dangerous. The terminal controls what bytes it reports, and remote sessions forward a channel that may be influenced by multiple components.

Users should paste into an editor or quoted here-document when reviewing complex commands, verify line breaks and characters, and avoid executing secrets or opaque installation pipelines. Applications should visually distinguish pasted regions where practical.

Restore mode through every lifecycle path

Enable bracketed paste after terminal initialization and disable it before normal exit, crash handoff where possible, and suspension to an outer shell. Re-enable after resume. Pair it with restoration of alternate-screen, mouse, cursor, and termios modes owned by the same UI.

Test compatible and incompatible terminals, tmux/screen/SSH layers, partial markers, nested applications, pasted multiline text, control bytes, huge payloads, Ctrl-C, crash, and suspend/resume. Bracketed paste is a cooperative protocol that gives the line editor context. Its value comes from careful parsing and explicit confirmation—not from treating an escape sequence as an input security boundary.

Related:

Sources:

Comments