security: strip ANSI/OSC escapes from API-controlled output#479
Conversation
There was a problem hiding this comment.
Pull request overview
This PR mitigates terminal-injection risk by stripping ANSI/OSC escape sequences from API-controlled output before it reaches styled, markdown, presenter, and watch-mode render paths.
Tip
If you aren't ready for review, convert to a draft PR.
Click "Convert to draft" or run gh pr ready --undo.
Click "Ready for review" or run gh pr ready to reengage.
Changes:
- Adds source-level sanitization for response summaries, notices, and diagnostics.
- Adds sink-level stripping in styled/markdown renderers and presenter affordance formatting.
- Sanitizes timeline watch TUI fields before rendering/truncation and adds coverage for response option sanitization.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
internal/output/envelope.go |
Strips ANSI/OSC escapes in response options and entity presenter summary/notice output. |
internal/output/render.go |
Applies defense-in-depth stripping in styled and markdown response renderers. |
internal/output/output_test.go |
Adds tests for summary/notice/diagnostic source-level sanitization. |
internal/presenter/format.go |
Sanitizes formatted API-controlled text and person names. |
internal/presenter/render.go |
Sanitizes rendered affordance commands after template interpolation. |
internal/commands/timeline.go |
Sanitizes watch TUI event fields and project/person descriptions. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
b6d427d to
2eacce7
Compare
7b1d3ea to
61aea03
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 61aea0335f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
61aea03 to
e06c5c2
Compare
2eacce7 to
447cbf3
Compare
e06c5c2 to
9103d13
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9103d138ee
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
447cbf3 to
2734359
Compare
9103d13 to
dd63a13
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: dd63a1322a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
dd63a13 to
4566262
Compare
API-controlled strings (names, summaries, notices, headlines, affordance
commands) reached styled/markdown sinks and the --watch TUI without ANSI
stripping, allowing terminal/OSC injection. Sanitize WithSummary/WithNotice/
WithDiagnostic at the source, the timeline watch TUI fields (creator name
stripped before the empty check so an all-escape name still falls back to a
placeholder), and presenter format helpers, with defense-in-depth at the
render sinks. Stripping is centralized in RenderTemplate + RenderHeadline so
schema headlines ({{.name}}/{{.subject}}/{{.content}}) and affordance commands
are all covered, including the identity-label headline fallback.
4566262 to
c7b69c7
Compare
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--watchTUI withoutansi.Strip, in render paths the generic table/cell path already protected.Fix
ansi.StripinWithSummary/WithNotice/WithDiagnostic— protects all current and future callers.Summary/Noticeare emitted in the styled and markdown renderers (render.go,envelope.go).--watchTUI: strip creator/action/title/excerpt + the person/project description (timeline.go); rune-truncation otherwise preserves escape bytes and the alt-screen executes OSC sequences.formatText/formatPerson/formatPeopleand the rendered affordance commands (presenter/format.go,presenter/render.go).Tests
TestWithSummaryStripsANSIlocks in source-level stripping.Part 2/6 of the stacked security series. Base:
security/01-api-token-leak.📚 Stack (merge bottom-up)
apito prevent token leak #478 — reject foreign-host URLs inapi(basemain)Each is independent except #482 depends on #481 (shared
root.go). #478 can land first/alone.