From 770635955a07a6e85bdad9fc77eff54652774336 Mon Sep 17 00:00:00 2001 From: "Brandon McGinty (chatgpt)" Date: Sun, 9 Aug 2026 23:54:49 -0400 Subject: [PATCH] Sanitize terminal widget text --- uiterm/label.go | 2 +- uiterm/textbox.go | 2 +- uiterm/textview.go | 2 +- uiterm/tree.go | 2 +- uiterm/ui_regression_test.go | 11 +++++++++++ uiterm/view.go | 11 +++++++++++ 6 files changed, 26 insertions(+), 4 deletions(-) diff --git a/uiterm/label.go b/uiterm/label.go index e0d4a80..088da5a 100644 --- a/uiterm/label.go +++ b/uiterm/label.go @@ -40,7 +40,7 @@ func (l *Label) uiDraw() { if ch, _, err := reader.ReadRune(); err != nil { chr = ' ' } else { - chr = ch + chr = safeRune(ch) } termbox.SetCell(x, y, chr, termbox.Attribute(l.Fg), termbox.Attribute(l.Bg)) } diff --git a/uiterm/textbox.go b/uiterm/textbox.go index 304ca99..e3e2fc6 100644 --- a/uiterm/textbox.go +++ b/uiterm/textbox.go @@ -63,7 +63,7 @@ func (t *Textbox) uiDraw() { if ch, _, err := reader.ReadRune(); err != nil { chr = ' ' } else { - chr = ch + chr = safeRune(ch) } termbox.SetCell(x, y, chr, termbox.Attribute(t.Fg), termbox.Attribute(t.Bg)) } diff --git a/uiterm/textview.go b/uiterm/textview.go index 14ef76e..e03ff4e 100644 --- a/uiterm/textview.go +++ b/uiterm/textview.go @@ -146,7 +146,7 @@ func (t *Textview) uiDraw() { var chr rune = ' ' if reader != nil { if ch, _, err := reader.ReadRune(); err == nil { - chr = ch + chr = safeRune(ch) } //no err } //reader != nil termbox.SetCell(x, y, chr, termbox.Attribute(t.Fg), termbox.Attribute(t.Bg)) diff --git a/uiterm/tree.go b/uiterm/tree.go index 5944355..0990c72 100644 --- a/uiterm/tree.go +++ b/uiterm/tree.go @@ -177,7 +177,7 @@ func (t *Tree) uiDraw() { dx := x - t.x0 if reader != nil && level*2 <= dx { if ch, _, err := reader.ReadRune(); err == nil { - chr = ch + chr = safeRune(ch) fg, bg = item.TreeItemStyle(fg, bg, t.active && t.activeLine == line) } } diff --git a/uiterm/ui_regression_test.go b/uiterm/ui_regression_test.go index 7f5dee8..4598959 100644 --- a/uiterm/ui_regression_test.go +++ b/uiterm/ui_regression_test.go @@ -32,3 +32,14 @@ func TestCloseIsNonblockingAndIdempotent(t *testing.T) { t.Fatal("Close did not signal shutdown") } } + +func TestSafeRuneRemovesTerminalControlCharacters(t *testing.T) { + for _, r := range []rune{'\x1b', '\x7f', '\u202e'} { + if got := safeRune(r); got != ' ' { + t.Errorf("safeRune(%U) = %U, want space", r, got) + } + } + if got := safeRune('A'); got != 'A' { + t.Fatalf("safeRune altered printable text: %U", got) + } +} diff --git a/uiterm/view.go b/uiterm/view.go index 606e3cd..3ee420b 100644 --- a/uiterm/view.go +++ b/uiterm/view.go @@ -1,5 +1,16 @@ package uiterm +import "unicode" + +// safeRune prevents text supplied by a server or another user from being +// interpreted as a terminal control sequence when termbox flushes its cells. +func safeRune(r rune) rune { + if unicode.IsControl(r) || unicode.Is(unicode.Bidi_Control, r) { + return ' ' + } + return r +} + type View interface { uiInitialize(ui *Ui) uiSetActive(active bool)