Skip to content

Keep escape-sequence payloads out of Debug output - #185

Merged
tomlm merged 1 commit into
tomlm:mainfrom
confetty-sh:no-terminal-data-in-logs
Nov 1, 2026
Merged

tomlm merged 1 commit into
tomlm:mainfrom
confetty-sh:no-terminal-data-in-logs

Conversation

@JohnCampionJr

Copy link
Copy Markdown
Collaborator

Some of the parser's Debug.WriteLine lines quote text taken from the output stream. A host that bridges Trace.Listeners into its logging then copies terminal data into its log. An OSC payload is program output: it can carry clipboard data (OSC 52), titles, hyperlinks, notification bodies or any text a program printed, including text from untrusted content such as cat of a file. This is the library-side counterpart of tomlm/Iciclecreek.Avalonia.Terminal#201, which stopped the control logging typed text.

Changes

I audited all 17 Debug writes in src/XTerm.NET (there are no Trace.* or Console.* calls) and changed 3, all in InputHandler.cs:

Site Was Now
unknown OSC raw parts[0]: the text before the first ;, or the whole payload when there is none the number if it parses as a plain integer, otherwise non-numeric command, N chars
unknown CSI the full identifier (the parser collects any number of intermediates) quoted only when 3 characters or fewer, otherwise final 'X' after N intermediates
unhandled OSC the OscCommand enum name (int)command, the numeric id (it was already safe; this keeps the OSC lines consistent)

Kept: the mode lines in InputHandler.cs and InputHandler.Modes.cs (unknown or unhandled modes, Meta/Alt/Win32 toggles), which log an int mode or a TerminalMode name, never stream text. BufferReflow.cs's Debug.Assert has no message. Nothing on the input-encoding side (Generate*Input, Paste, Kitty) logs anything.

Test

DiagnosticOutputTests adds a TraceListener for the test, removed in finally, and writes S3CR3T-sentinel through:

  • an OSC with no number, OSC 9999, and an OSC whose number is the sentinel;
  • OSC 52 (raw and base64), OSC 2 and OSC 8;
  • a CSI with 32 intermediates;
  • DCS, DECRQSS, APC and PM strings;
  • Paste and GenerateCharInput.

It asserts that the sentinel, its base64 and the run of intermediates never appear. Under #if DEBUG it also asserts that the three redacted lines did arrive, so an empty capture can't pass. Restoring the raw OSC text turns it red, and so does lifting the CSI cap. It and the OSC, OSC passthrough, mode handling, private CSI dispatch and InputHandler tests pass 297/297. The full test project wasn't run.

🤖 Generated with Claude Code

Debug.WriteLine reaches every Trace listener a host installs, and hosts
bridge those into application logs. Two lines quoted text a program
printed:

- "Unknown OSC sequence" logged the text before the first ';'. With no
  ';' that is the whole OSC payload. It now logs the number when it is
  one, else only the length.
- "Unknown CSI sequence" logged the identifier, whose intermediates the
  parser collects without a limit. A long one is now reported by its
  final byte and count.

"Unhandled OSC command" logs the numeric id. The mode lines log only
integers or enum names, so they stay.

The test writes a sentinel through OSC, CSI, DCS, APC, PM, paste and
character input and checks that a Trace listener never sees it. In a
Debug build it also checks that the redacted lines did arrive.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@tomlm
tomlm merged commit d987870 into tomlm:main Nov 1, 2026
5 checks passed
JohnCampionJr added a commit to tomlm/Iciclecreek.Avalonia.Terminal that referenced this pull request Nov 1, 2026
3.0.2 keeps escape-sequence payloads out of XTerm.NET's Debug output
(tomlm/XTerm.NET#185), the library half of #201.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

2 participants