fix(lifecycle): sanitize CI logs and reviewer comments before PTY paste#323
fix(lifecycle): sanitize CI logs and reviewer comments before PTY paste#323areycruzer wants to merge 1 commit into
Conversation
CI nudges appended raw LogTail and review nudges appended raw comment
bodies straight into the message that zellij pastes into the agent's live
pane, while the /sessions/{id}/send endpoint stripped control chars first.
External CI output and reviewer comments could therefore inject terminal
escape sequences into the agent's TTY.
Move the sanitizer out of the HTTP controller into a shared
domain.SanitizeControlChars helper and apply it to both external lifecycle
fields. The dedup signature keeps using the raw bytes (it never reaches the
terminal), and legitimate newlines/tabs are preserved.
Fixes aoagents#322
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
codebanditssss
left a comment
There was a problem hiding this comment.
Approving — this is the right fix for #322.
Root cause is correctly identified and fully addressed. stripUnsafeControlChars only guarded the /send HTTP path; the lifecycle nudge path built messages from attacker-influenced external content (ch.LogTail, reviewer comment bodies) and delivered them through the same zellij PTY paste with no sanitization. Both PTY-delivery paths now share one domain.SanitizeControlChars definition, so they can't drift — that's the real defense against this recurring.
Scope is appropriately minimal: only the two external fields are sanitized; trusted static nudge wording is left as-is; and the dedup signature deliberately stays on the raw bytes since it never reaches the terminal. No DTO/API/frontend churn.
Verified locally against current main (PR was 14 commits behind):
- Merges cleanly into main, no conflicts, no semantic clash with main's newer
LogTailhandling go build ./...,go vet,gofmtall clean- Tests pass for
domain,lifecycle, andhttpd/controllers - The three new tests correctly assert escape/NUL/bell bytes are stripped while newlines, tabs, and visible text survive
CI is now green across the board (Go, CLI E2E, gitleaks). Good to merge — please rebase/update on main if the merge button asks for it.
Fixes #322
Summary
LogTailand review nudges appended raw reviewer comment bodies straight into the message thatzellij.SendMessagepastes into the agent's live pane — whilePOST /sessions/{id}/sendalready stripped control chars first. External CI output / reviewer comments could therefore inject terminal escape sequences into the agent's TTY.domain.SanitizeControlCharshelper so both the send endpoint and the lifecycle nudge path share one definition and can't drift.ch.LogTail(reactions.go) and the joined review comment bodies inreviewContent.Root Cause
stripUnsafeControlCharslived ininternal/httpd/controllers/sessions.goand was only called on the user/sendpath. The lifecycle nudge path (internal/lifecycle/reactions.go) builds its message from external content and delivers it through the samezellijpaste mechanism, but with no sanitization. So the protection that exists for one PTY-delivery path was simply missing on the other.Changes
internal/domain/text.go— newSanitizeControlChars(drops control chars except\n,\r,\t).internal/httpd/controllers/sessions.go— usedomain.SanitizeControlChars; remove the local helper and the now-unusedunicodeimport.internal/lifecycle/reactions.go— sanitizeLogTailand review comment bodies before building the nudge. The dedup signature keeps using the raw bytes (it never reaches the terminal).Test
go build ./...go test ./internal/domain/... ./internal/lifecycle/... ./internal/httpd/controllers/...— passgo vet ./internal/domain/... ./internal/lifecycle/... ./internal/httpd/...— cleanTestSanitizeControlChars(domain),TestPRObservation_CINudgeSanitizesLogTailControlCharsandTestPRObservation_ReviewNudgeSanitizesCommentControlChars(lifecycle) — assert escape/control bytes are stripped while newlines/tabs and visible text survive.Notes
🤖 Generated with Claude Code