security: strip ANSI/OSC escapes from API-controlled output

Merged
JE
4mo ago
+2117-128

Strip ANSI/OSC escapes from API-controlled output

(Threat: T4 terminal injection via API-controlled strings carrying ANSI/OSC sequences.)

API-controlled strings (project/person/entity names, summaries, notices) reached lipgloss.Render/stdout and the --watch TUI without ansi.Strip, in render paths the generic table/cell path already protected.

Fix

  • Sink-only: sanitize at every terminal sink, not at the source. Summary/Notice/Diagnostic are stored verbatim by WithSummary/WithNotice/WithDiagnostic (so machine/JSON output preserves byte fidelity, consistent with error/hint/data) and stripped only where they're emitted to a terminal — the styled and markdown renderers and the quiet-mode stderr diagnostic (render.go, envelope.go).
  • --watch TUI: strip creator/action/title/excerpt + the person/project description (timeline.go); rune-truncation otherwise preserves escape bytes and the alt-screen executes OSC sequences.
  • Presenter: strip in formatText/formatPerson/formatPeople and the rendered affordance commands (presenter/format.go, presenter/render.go).

Tests

Tests assert JSON summary/notice are verbatim and that every terminal sink (styled/markdown/plain RenderError, summary/notice, breadcrumbs, attachment metadata, and the quiet-mode stderr notice:) neutralizes C1/CSI/OSC escapes.


Part 2/6 of the stacked security series. Base: security/01-api-token-leak.

šŸ“š Stack (merge bottom-up)

  1. #478 — reject foreign-host URLs in api (base main)
  2. #479 — strip ANSI/OSC escapes from output
  3. #480 — harden OAuth discovery / token endpoints
  4. #481 — close config trust-boundary gaps + completion gate
  5. #482 — tighten config-dir perms + plugin scope argv
  6. #483 — bump toolchain/x-net + CI gates

Each is independent except #482 depends on #481 (shared root.go). #478 can land first/alone.