// Package terminaltext makes untrusted text safe to render in a terminal. package terminaltext import "strings" // Sanitize removes ANSI CSI, OSC (including title/clipboard commands), other // ESC/C1 control strings, and non-printing controls while preserving printable // Unicode, newlines, and tabs. func Sanitize(input string) string { const ( normal = iota escape csi osc controlString oscEscape controlStringEscape ) state := normal var output strings.Builder output.Grow(len(input)) for _, current := range input { switch state { case normal: switch current { case '\x1b': state = escape case '\u009b': state = csi case '\u009d': state = osc case '\u0090', '\u0098', '\u009e', '\u009f': state = controlString case '\n', '\t': output.WriteRune(current) default: if current >= ' ' && current != '\x7f' && !(current >= '\u0080' && current <= '\u009f') { output.WriteRune(current) } } case escape: switch current { case '[': state = csi case ']': state = osc case 'P', 'X', '^', '_': state = controlString case '\x1b': // Stay in escape state for repeated ESC bytes. default: state = normal } case csi: if current >= 0x40 && current <= 0x7e { state = normal } case osc: switch current { case '\a': state = normal case '\x1b': state = oscEscape } case controlString: if current == '\x1b' { state = controlStringEscape } case oscEscape: if current == '\\' { state = normal } else if current != '\x1b' { state = osc } case controlStringEscape: if current == '\\' { state = normal } else if current != '\x1b' { state = controlString } } } return output.String() }