pranshuparmar--witr
36b3af2e3d
PR Check / Code Quality: Format (push) Failing after 1s
PR Check / Code Quality: Lint (darwin) (push) Failing after 0s
PR Check / Code Quality: Lint (freebsd) (push) Failing after 1s
PR Check / Code Quality: Lint (windows) (push) Failing after 1s
PR Check / Code Quality: Lint (linux) (push) Failing after 1s
PR Check / Security: Vulnerability Scan (push) Failing after 0s
Update Documentation / update-docs (push) Failing after 2s
PR Check / Code Quality: Vendor (push) Failing after 1s
PR Check / Code Quality: Coverage (push) Failing after 0s
PR Check / Tests: Unit (macos-latest) (push) Has been cancelled
PR Check / Tests: Unit (ubuntu-24.04) (push) Has been cancelled
PR Check / Tests: Unit (ubuntu-24.04-arm) (push) Has been cancelled
PR Check / Tests: Unit (windows-latest) (push) Has been cancelled
121 行
3.0 KiB
Go
121 行
3.0 KiB
Go
package output
|
|
|
|
import (
|
|
"strings"
|
|
"unicode"
|
|
"unicode/utf8"
|
|
)
|
|
|
|
const hexDigits = "0123456789abcdef"
|
|
|
|
// SanitizeTerminal makes a string safe to print to an interactive terminal
|
|
// by just replacing control characters with visible escape sequences (e.g. "\x1b")
|
|
// Examples:
|
|
// - "hi\x1b[31mred" -> "hi\x1b[31mred" (ESC becomes visible)
|
|
// - "nul:\x00" -> "nul:\x00"
|
|
// - "bad:\xff" -> "bad:\xff" (invalid UTF-8 byte)
|
|
// - "a\tb\nc" -> "a\tb\nc" (tabs/newlines are untouched)
|
|
func SanitizeTerminal(s string) string {
|
|
idx := 0
|
|
// fast path: scan until we find a control rune / invalid UTF-8 byte
|
|
for idx < len(s) {
|
|
r, size := utf8.DecodeRuneInString(s[idx:])
|
|
if r == utf8.RuneError && size == 1 {
|
|
break
|
|
}
|
|
if r == '\n' || r == '\t' {
|
|
idx += size
|
|
continue
|
|
}
|
|
if unicode.IsControl(r) {
|
|
break
|
|
}
|
|
idx += size
|
|
}
|
|
if idx == len(s) {
|
|
return s
|
|
}
|
|
|
|
var b strings.Builder
|
|
b.Grow(len(s) + 8)
|
|
b.WriteString(s[:idx])
|
|
|
|
// slow path: walk the remainder and rewrite any control bytes/runes
|
|
for idx < len(s) {
|
|
r, size := utf8.DecodeRuneInString(s[idx:])
|
|
if r == utf8.RuneError && size == 1 {
|
|
// preserve invalid bytes without letting them act as controls just in case
|
|
appendEscapedByte(&b, s[idx])
|
|
idx++
|
|
continue
|
|
}
|
|
|
|
if r == '\n' || r == '\t' {
|
|
b.WriteRune(r)
|
|
idx += size
|
|
continue
|
|
}
|
|
|
|
if unicode.IsControl(r) {
|
|
appendEscapedRune(&b, r)
|
|
idx += size
|
|
continue
|
|
}
|
|
b.WriteString(s[idx : idx+size])
|
|
idx += size
|
|
}
|
|
|
|
return b.String()
|
|
}
|
|
|
|
// SanitizeTerminalLine is SanitizeTerminal for values rendered as a single line
|
|
// or table cell (process names, command lines, env entries, paths).
|
|
// SanitizeTerminal intentionally preserves \n and \t for multi-line layout; in a
|
|
// single-line field those let an embedded value forge extra output lines or
|
|
// shift columns, so they are escaped here too.
|
|
func SanitizeTerminalLine(s string) string {
|
|
if strings.ContainsAny(s, "\n\t") {
|
|
s = strings.NewReplacer("\n", `\n`, "\t", `\t`).Replace(s)
|
|
}
|
|
return SanitizeTerminal(s)
|
|
}
|
|
|
|
func appendEscapedByte(b *strings.Builder, bt byte) {
|
|
b.WriteString(`\x`)
|
|
b.WriteByte(hexDigits[bt>>4])
|
|
b.WriteByte(hexDigits[bt&0x0f])
|
|
}
|
|
|
|
// while this looks extremely bad, it just outputs this:
|
|
// - r = 0x1b -> "\x1b"
|
|
// - r = 0x2028 -> "\u2028"
|
|
// - r = 0x1f600 -> "\U0001f600"
|
|
func appendEscapedRune(b *strings.Builder, r rune) {
|
|
// 0xFF: "\xHH" (simple byte escape)
|
|
if r <= 0xFF {
|
|
appendEscapedByte(b, byte(r))
|
|
return
|
|
}
|
|
|
|
// <= 0xFFFF: "\uHHHH" (BMP escape)
|
|
if r <= 0xFFFF {
|
|
b.WriteString(`\u`)
|
|
b.WriteByte(hexDigits[(r>>12)&0x0f])
|
|
b.WriteByte(hexDigits[(r>>8)&0x0f])
|
|
b.WriteByte(hexDigits[(r>>4)&0x0f])
|
|
b.WriteByte(hexDigits[r&0x0f])
|
|
return
|
|
}
|
|
|
|
// otherwise: "\UHHHHHHHH" (full 32-bit escape)
|
|
b.WriteString(`\U`)
|
|
b.WriteByte(hexDigits[(r>>28)&0x0f])
|
|
b.WriteByte(hexDigits[(r>>24)&0x0f])
|
|
b.WriteByte(hexDigits[(r>>20)&0x0f])
|
|
b.WriteByte(hexDigits[(r>>16)&0x0f])
|
|
b.WriteByte(hexDigits[(r>>12)&0x0f])
|
|
b.WriteByte(hexDigits[(r>>8)&0x0f])
|
|
b.WriteByte(hexDigits[(r>>4)&0x0f])
|
|
b.WriteByte(hexDigits[r&0x0f])
|
|
}
|