echo(color=False) and strip_ansi do NOT sanitize C0/C1 control characters — terminal-escape fixes need their own stripper
Mitigating terminal escape-sequence injection (OSC 52 clipboard writes, cursor-up/clear-line log tampering), the tempting one-liner is to route output through the CLI library's "no color" switch — e.g. face's echo(..., color=False) or boltons' strutils.strip_ansi. An OSTIF audit recommended exactly that as an alternative fix. It is insufficient, and the gap is invisible unless you test with a control-character corpus.
Verified on face 26.0.1 + boltons 25.0.0:
s = 'a\rb\x1b[Gc\x1b]52;c;aG#\x07d'
face echo(color=False) -> 'a\rbc52;c;aG#\x07d' # \r and BEL survive
boltons strip_ansi(s) -> 'a\rbc52;c;aG#\x07d' # identicalBoth strip complete ANSI/CSI sequences (ESC-prefixed), but pass through bare C0 controls: \r (carriage return — overdraws the line on the terminal), \x07 (BEL), and the OSC 52 payload bytes once the ESC is stripped. The \x1b]52;c;<b64>\x07 clipboard-write attack degrades to junk text rather than being neutralized, and \r remains a live line-overdraw primitive on its own.
Correct mitigation for any tool that echoes file-sourced strings: own a re.compile(r'[\x00-\x1f\x7f-\x9f]') substitution (replace with U+FFFD, not delete — deletion lets tampering vanish silently) applied to values before display, then let the CLI library add its own color codes afterward. Beware ordering: sanitize before your own diff/colorizer, or you strip your own ANSI.
Also check how the control chars arrive: YAML/JSON readers often reject raw control bytes at the reader layer, but double-quoted escapes ("\x1b[2K") decode to real control chars after that check — so a schema regex like .* still admits them. Tighten the field regexes (^[^\x00-\x1f\x7f-\x9f]+\Z) and sanitize at display; both layers, because file history/back-compat will feed old files to new code.
Mitigating terminal escape-sequence injection (OSC 52 clipboard writes, cursor-up/clear-line log tampering), the tempting one-liner is to route output through the CLI library's "no color" switch — e.g. face's echo(..., color=False) or boltons' strutils.strip_ansi. An OSTIF audit recommended exactly that as an alternative fix. It is insufficient, and the gap is invisible unless you test with a control-character corpus.
Verified on face 26.0.1 + boltons 25.0.0:
s = 'a\rb\x1b[Gc\x1b]52;c;aG#\x07d'
face echo(color=False) -> 'a\rbc52;c;aG#\x07d' # \r and BEL survive
boltons strip_ansi(s) -> 'a\rbc52;c;aG#\x07d' # identicalBoth strip complete ANSI/CSI sequences (ESC-prefixed), but pass through bare C0 controls: \r (carriage return — overdraws the line on the terminal), \x07 (BEL), and the OSC 52 payload bytes once the ESC is stripped. The \x1b]52;c;<b64>\x07 clipboard-write attack degrades to junk text rather than being neutralized, and \r remains a live line-overdraw primitive on its own.
Correct mitigation for any tool that echoes file-sourced strings: own a re.compile(r'[\x00-\x1f\x7f-\x9f]') substitution (replace with U+FFFD, not delete — deletion lets tampering vanish silently) applied to values before display, then let the CLI library add its own color codes afterward. Beware ordering: sanitize before your own diff/colorizer, or you strip your own ANSI.
Also check how the control chars arrive: YAML/JSON readers often reject raw control bytes at the reader layer, but double-quoted escapes ("\x1b[2K") decode to real control chars after that check — so a schema regex like .* still admits them. Tighten the field regexes (^[^\x00-\x1f\x7f-\x9f]+\Z) and sanitize at display; both layers, because file history/back-compat will feed old files to new code.