Skip to content

docs(agent): document the sanitize-before-render contract on SetToolStreamCallback#100

Merged
xunholy merged 1 commit into
mainfrom
docs/stream-callback-sanitize-contract
Jun 24, 2026
Merged

docs(agent): document the sanitize-before-render contract on SetToolStreamCallback#100
xunholy merged 1 commit into
mainfrom
docs/stream-callback-sanitize-contract

Conversation

@xunholy

@xunholy xunholy commented Jun 24, 2026

Copy link
Copy Markdown
Owner

Summary

Follow-up to the v0.741 quarantine audit (#99). The streaming dispatch path is currently safe — the only consumer (the REPL) neutralises control bytes in renderStreamFrame, and the web server doesn't wire a frame callback (streaming tools fall back to the quarantined Handler there). But that safety rests on an undocumented contract.

Unlike the final return string (which dispatch runs through quarantineOutputsanitizeControlChars + injection wrapping), per-frame data is forwarded RAW to the consumer. frame.Bytes is attacker-controllable tool output (firmware log lines, RF scan rows, device names) that can carry ANSI/control sequences. A future consumer — a web cockpit stream, a new UI — that renders frames without sanitising would silently reintroduce the terminal-injection vector the rest of the codebase defends against.

This documents that contract on SetToolStreamCallback so the invariant is explicit at the API boundary, not just implicit in one consumer. No code behaviour change.

Verification

  • go build ./... clean; go vet clean; golangci-lint 0 issues; agent streaming/quarantine tests pass.

Doc-only — no release on its own; rides with the next feature release.

…treamCallback

Follow-up to the v0.741 quarantine audit. The streaming dispatch path is
currently safe — the only consumer, the REPL, neutralises control bytes
in renderStreamFrame ("quote anything with control characters ... so the
operator-facing log can't be hijacked by a hostile capture"), and the web
server doesn't wire a frame callback (streaming tools fall back to the
quarantined Handler there).

But that safety rests on an UNDOCUMENTED contract: unlike the final
return string (which dispatch runs through quarantineOutput's
sanitizeControlChars), per-frame data is forwarded RAW to the consumer.
frame.Bytes is attacker-controllable tool output (firmware log lines, RF
scan rows, device names) that can carry ANSI/control sequences. A future
consumer — a web cockpit stream, a new UI — that renders frames without
sanitising would silently reintroduce the terminal-injection vector the
rest of the codebase defends against.

This documents that contract on SetToolStreamCallback so the invariant is
explicit at the API boundary, not just implicit in one consumer. No code
behaviour change.

Verification: go build ./... clean; go vet clean; golangci-lint 0 issues;
the agent streaming/quarantine tests pass. Doc-only — no release on its
own; rides with the next feature release.
@xunholy xunholy merged commit 62584bd into main Jun 24, 2026
10 of 11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant