Lion Cipher Lab
Stop terminal escape sequences in LLM output from hijacking an analyst's terminal (deterministic stub model)
- AI Security
- Advanced
- ~75 min
- Free · no account required
Why it matters
Command-line triage tools are starting to put a language model between the logs and the analyst: the tool reads a few hundred log lines, the model writes a summary, and the tool prints it in the terminal. Part of every log line is written by whoever caused it - a username, a hostname, a request path - and models quote the lines they summarise. A terminal does not just display text: certain characters are instructions to it. They can make text invisible, move the cursor back and draw a calmer line over a 'Failed password for root', put a hyperlink on the screen whose target is not the text you…
What you'll learn
- treating the analyst's terminal as an output SINK: knowing which control characters a terminal acts on (ESC, CSI, OSC, SGR, CR, backspace) and what each can do - hide text, overwrite a line, set a misleading hyperlink, write the clipboard, set the window title
- proving terminal effects SAFELY: rendering to a file and parsing it with a small interpreter that reports what a terminal would do, instead of printing a payload to your own screen
- measuring four render paths on a 300-line corpus: naive printing let 86 of 86 planted lines take effect; a 7-bit-only strip still let 41 through (every 8-bit C1 and every bare CR/backspace line)
- building a full control-character escaper and an allowlist renderer that both took planted effects to 0 of 86 and flagged all 86 visibly
- pricing each fix on legitimately coloured output: escaping made 0 of 48 coloured lines readable (+58% characters); the allowlist kept all 48 readable but kept colour on only 33
- naming what a terminal-control filter does not catch - a Unicode bidi override passed every mode - and what stripping silently rewrites, such as an unterminated OSC swallowing the rest of a line
Steps in this lab
- The words first: control characters, ESC, CSI, OSC, SGR, C1 - and the fact that the model is a stub
- Open a terminal, check Python, make a scratch folder (Windows, macOS, Linux)
- Build 300 log lines: honest ones, legitimately coloured ones, and seven kinds of planted canary sequence
- Write the STUB summariser and read its whole error model: it never invents a sequence, it only copies one
- Build vt.py: a tiny interpreter that reports what a terminal WOULD do, so you never have to let it
- Write the four render paths: naive, strip 7-bit ESC, escape everything, and an allowlist that re-applies only your colours
- Measure: planted effects reaching the terminal per mode, and the damage each mode does to honest and coloured lines
- Read the results: why strip7 half-works, why escape and allowlist both work, and what each one costs
- The honest negatives: bidi passes everything, escaping is expensive, stripping rewrites evidence, and 8-bit is a worst case
- Why this, not that - and how this lab differs from its neighbours
- When NOT to - and how a pro does it differently
- One experiment, an optional local model, then clean up