You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
os/v3/puppy/console-tui: log and catalogue text go into cells unchanged, so an escape sequence in a chat prompt or a model name becomes one on the appliance's screen #1117
os/v3/puppy/console-tui draws two things it did not write itself: the tail of Lemonade's log, and the model/recipe/backend names
from /api/v1/models. Both go into cells unchanged, and a cell's symbol is written through to the terminal unchanged - so an
escape sequence that arrives as data becomes an escape sequence on the appliance's own screen.
Measured. A log line containing \x1b[2J\x1b[31m (staged at /var/log/lemond-onebit.log, then --snapshot 80x25):
main today : ESC bytes=2 literal '[2J'=1
LEM 2026-11-05 06:00:02.000 [Warn] (Server) model name "evil\x1b[2J\x1b[31mRED" loaded
i.e. the frame contains a clear-screen and a colour command, positioned at the pane's row. On a live terminal that is a clear
and a colour change in the middle of the console - and the pane is the surface a person stands in front of.
Where such bytes come from, without anyone being malicious:
Lemonade's log at log_level: debug carries chat prompts (a demo page's textarea is a paste target), model names and the
engine's own output - and the engine does emit escapes deliberately: every progress bar line ends with \x1b[K, which
reached the pane as a literal [K on the cycle-25 take's own log;
a catalogue name comes from a Hub and is drawn in the MODELS panel and in the engine: row.
Fix:facts::screen_text() - strip CSI and OSC sequences whole, TAB to a space (the panes have no tab stops), every other
control character dropped, everything else left alone (°C, é, the box-drawing the panes use). Applied where external text
enters: log lines (both LEM and KRN) and the catalogue's id/recipe/backend.
Measured after: the same staged line renders evilRED, 0 ESC bytes in the frame, and both committed snapshots stay
byte-identical (the fixtures contain no escapes).
Activity