diff --git a/crates/tui/src/mcp.rs b/crates/tui/src/mcp.rs index dda1daa0..4cdbdc14 100644 --- a/crates/tui/src/mcp.rs +++ b/crates/tui/src/mcp.rs @@ -203,7 +203,7 @@ fn invalid_json_preview(bytes: &[u8]) -> String { } let trimmed: String = body_text.chars().take(ERROR_BODY_PREVIEW_BYTES).collect(); - let suffix = if body_text.len() > trimmed.len() { + let suffix = if body_text.chars().count() > ERROR_BODY_PREVIEW_BYTES { "…" } else { "" @@ -4013,6 +4013,26 @@ mod tests { ); } + #[test] + fn invalid_json_preview_collapses_lines_and_redacts_secrets() { + let preview = invalid_json_preview( + b"Authorization: Bearer PLACEHOLDER_TOKEN\nAllow connection? api_key=PLACEHOLDER_KEY", + ); + + assert!( + preview.contains("Authorization: Bearer *** Allow connection? api_key=***"), + "preview: {preview}" + ); + assert!( + !preview.contains('\n'), + "preview should be single-line: {preview}" + ); + assert!( + !preview.contains("PLACEHOLDER_TOKEN") && !preview.contains("PLACEHOLDER_KEY"), + "secret leaked: {preview}" + ); + } + /// #420: `StdioTransport::shutdown` reaps the child process by sending /// SIGTERM and giving it a brief grace period before drop fires SIGKILL. /// The test spawns `cat` (which exits immediately on stdin EOF / SIGTERM)