sanitize

package
v0.6.1-rc.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 4, 2026 License: Apache-2.0 Imports: 2 Imported by: 0

Documentation

Overview

Package sanitize is the one output chokepoint for terminal-unsafe text (the August 2026 chat-rebuild audit, no longer in the tree, Part 10.2 items 6-7).

Anything that reaches the terminal and was not authored by our own renderer — a model reply, a tool result, a worker trace line — is a channel someone else's bytes can drive. Escape sequences are how a terminal emulator is remote-controlled: OSC 52 writes the system clipboard, OSC 0/1/2 rewrite the window/tab title, OSC 8 can point a clickable span at an arbitrary URL, and the DCS/APC/PM/SOS string types exist for the same reason CSI does — to make the emulator DO something, not print something. Sanitize.Text (and its palette-remapping sibling, TextWithPalette) is the single place that content gets to cross from "somebody else's bytes" to "safe to draw."

What survives (the allowlist)

  • plain text, including all valid UTF-8 beyond ASCII
  • '\n' and '\t'
  • SGR sequences: CSI parameter-string 'm' (ESC '[' ... 'm'), the color and style codes lipgloss/our renderers already emit. Nothing else introduced by ESC survives — cursor movement, screen/line erase, mode-setting, and every other CSI final byte are stripped, along with both 2- and 3-byte ESC forms (ESC 'c', ESC '(' 'B', ...).

What is neutralized

OSC of any kind (52 clipboard, 0/1/2 title, 8 hyperlink, 7/9/133/777/99 and anything unrecognized — the list is not an allowlist, so a new OSC number needs no code change here), DCS/APC/PM/SOS strings, every C1 control byte (0x80-0x9F, both as a raw byte and via the classic ESC-prefixed 7-bit forms), and any C0 control other than '\n'/'\t'. Malformed input (a truncated OSC, a lone trailing ESC, an escape nested inside another escape's payload, invalid UTF-8) is consumed defensively and never panics — see the fuzz corpus in sanitize_test.go, written after this codebase's escape-blind-truncate incident (internal/tui's ANSI-aware truncate/wrapText).

OSC 8 policy

The product itself adopts OSC 8 hyperlinks (audit notes 5.21) — the threat is not the sequence, it's letting UNTRUSTED text mint one. So OSC 8 arriving inside model/tool/trace output is stripped like any other OSC, leaving the visible link text behind; our own renderer is the only thing allowed to wrap a span in a real OSC 8 link, and it does so after this chokepoint, from our own chrome strings, never from sanitized content.

Palette remap (10.2.7)

TextWithPalette additionally remaps raw ANSI-16 SGR color codes (the classic 30-37/40-47/90-97/100-107 families) through a caller-supplied Table, so a tool's own colors cohere with ours instead of clashing with whatever the token layer eventually defines (Wave 2). The mechanism lands now; Identity is the only table anyone plugs in until then, and calling with Identity is a single 16-int array compare — no extra allocation, no second pass over the text.

Index

Constants

This section is empty.

Variables

View Source
var Identity = Table{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}

Identity is the zero-cost default palette: every color maps to itself. Wave 2's token layer plugs a real Table in here; until then every caller either omits the palette (Text) or passes Identity explicitly.

Functions

func Text

func Text(s string) string

Text returns s with every terminal-executable sequence neutralized, leaving SGR styling, plain text, and newlines/tabs untouched. Benign input — the overwhelming common case on the render hot path — costs one linear scan for an ESC or C1 byte and returns the original string with no allocation: the fast-path check lives directly in Text/TextWithPalette, not inside render, specifically so the common case never enters a function whose body declares a strings.Builder (see render's doc comment) and therefore never pays for one, even in the failure mode where the compiler's escape analysis can't prove that builder stack-allocatable.

func TextWithPalette

func TextWithPalette(s string, t Table) string

TextWithPalette sanitizes s exactly like Text and, in the same pass, remaps ANSI-16 SGR color codes through t. Passing Identity is exactly as cheap as calling Text.

Types

type Table

type Table [16]int

Table remaps the 16 classic ANSI SGR colors (indices 0-7 standard, 8-15 bright) onto replacement color indices. Table[i] is the index tool output's color i is redrawn as; Identity leaves every color where it found it.

func (Table) IsIdentity

func (t Table) IsIdentity() bool

IsIdentity reports whether t performs no remapping. Callers holding a Table from settings/config can use this to skip TextWithPalette entirely and call Text instead — the two are behaviorally identical for an identity table, but this avoids even the one array-compare per call.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL