Module: Parse::TerminalSafe
Overview
Neutralizes terminal control sequences in untrusted text before it is
written to a terminal, a log record, or an IRB inspect line.
Parse Server returns whatever a tenant stored. Any string that originates in the database, in a server error message, or in an LLM answer is attacker-influenced, and a raw ESC byte in that string is not inert once it reaches a TTY. It can clear the screen, rewrite lines the operator has already read, retitle the window, or (via OSC 52) write the attacker's payload into the system clipboard so the operator's next paste runs it. Bidirectional overrides are the same class of problem: they reorder a line so what the operator reads is not what the bytes say.
This module is the SDK's single answer to that. It escapes rather than
deletes, so the offending bytes stay visible in the output (rendered as
\e, \u202E, and friends) and an operator can still see that
something tried.
It sanitizes rendering, never storage. result.text, object.title, and
the parsed response body keep their exact bytes. Only the human-readable
form built for a terminal or a log line runs through here.
Constant Summary collapse
- UNSAFE_RANGES =
Codepoint ranges neutralized on every call:
- 0x00-0x08, 0x0B-0x1F: C0 controls except TAB (0x09) and LF (0x0A). This is where ESC (0x1B, the CSI/OSC/DCS introducer), BEL (0x07, the OSC string terminator), CR (0x0D, overwrite-the-line), and BS (0x08, erase-the-previous-character) live.
- 0x7F: DEL.
- 0x80-0x9F: C1 controls, the 8-bit forms of the same introducers (0x9B is CSI, 0x9D is OSC, 0x90 is DCS).
- 0x061C: Arabic letter mark, an implicit bidirectional control that reorders a line exactly like the explicit marks below.
- 0x200B-0x200F: zero-width space, non-joiner, joiner, LRM, RLM.
- 0x2028-0x2029: line and paragraph separator. Widely treated as a line break by terminals, editors, and log readers, so leaving them intact would reintroduce the forged-record problem that escaping LF solves. A legitimate line break is LF, which is preserved.
- 0x202A-0x202E: bidirectional embedding and override.
- 0x2060-0x2064: word joiner and the invisible math operators.
- 0x2066-0x2069: the bidirectional isolates.
- 0xFEFF: zero-width no-break space (a BOM appearing mid-string).
TAB and LF are deliberately absent: both are ordinary formatting in multi-line output. #sanitize_line escapes LF as well.
[ 0x00..0x08, 0x0B..0x1F, 0x7F..0x9F, 0x061C..0x061C, 0x200B..0x200F, 0x2028..0x2029, 0x202A..0x202E, 0x2060..0x2064, 0x2066..0x2069, 0xFEFF..0xFEFF, ].freeze
- UNSAFE_RE =
build_pattern(UNSAFE_RANGES)
- UNSAFE_LINE_RE =
The same set plus LF, for output that must occupy exactly one line.
build_pattern(UNSAFE_RANGES + [0x0A..0x0A])
- NAMED_ESCAPES =
Readable escapes for the controls an operator is most likely to see. Everything else falls back to
\xNNor\uNNNN. { "\0" => "\\0", "\a" => "\\a", "\b" => "\\b", "\n" => "\\n", "\v" => "\\v", "\f" => "\\f", "\r" => "\\r", "\e" => "\\e", }.freeze
Instance Method Summary collapse
-
#sanitize(str) ⇒ String
Escape terminal control sequences in
str, preserving newlines and tabs. -
#sanitize_line(str) ⇒ String
Escape terminal control sequences and newlines, so untrusted text cannot forge additional lines.
Instance Method Details
#sanitize(str) ⇒ String
Escape terminal control sequences in str, preserving newlines and tabs.
98 99 100 |
# File 'lib/parse/terminal_safe.rb', line 98 def sanitize(str) escape(str, UNSAFE_RE) end |
#sanitize_line(str) ⇒ String
Escape terminal control sequences and newlines, so untrusted text cannot forge additional lines. Use for anything written as a single log record or a single console line.
108 109 110 |
# File 'lib/parse/terminal_safe.rb', line 108 def sanitize_line(str) escape(str, UNSAFE_LINE_RE) end |