fix(telegram): stop auto-linking destinations in /policy show#32
Merged
nnemirovsky merged 2 commits intomainfrom Apr 13, 2026
Merged
fix(telegram): stop auto-linking destinations in /policy show#32nnemirovsky merged 2 commits intomainfrom
nnemirovsky merged 2 commits intomainfrom
Conversation
Telegram was rendering every destination string in /policy show output as a clickable blue link (example.com, api.github.com, etc.) because the message was sent in plain text mode and Telegram auto-detects URL patterns. Wrap the output in <pre>...</pre> and send with ParseMode=HTML + DisableWebPagePreview. Inside a preformatted block Telegram leaves URL patterns alone, and the monospace rendering also aligns the columns better. - commands.go: htmlPreOpen/htmlPreClose constants, htmlEscapeText helper, wrap both policyShow (engine fallback) and policyShowFromStore in <pre>. Escape destination, tool, pattern, replacement, name, source, default verdict, and protocols. - approval.go: sendReply path sniffs the <pre> prefix and switches to HTML parse mode with web-page preview disabled. Other replies are still plain text. - Tests: TestPolicyShowEscapesHTML and a wrapping check in TestPolicyShowIncludesAllFields.
56f3500 to
6b3946f
Compare
Extends the /policy show fix to the other Telegram surfaces that render destinations or URLs: approval prompts and policy mutation confirmations (allow/deny). Without this, Telegram auto-links the destination in strings like 'HTTPS api.github.com:443' or 'Added allow rule: example.com' as a clickable blue URL. - bot.go FormatApprovalMessage: wrap dest:port and the rendered request URL in <code>. MCP tool name also in <code>. - commands.go policyAllow / policyDeny: wrap dest in <code> on both store and in-memory paths. - Consolidate the duplicated htmlEscape helpers: commands.go now uses the existing htmlEscape from bot.go, local wrapper removed. - Update bot_test.go expectations to match the <code>-wrapped output.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Destinations across Telegram surfaces (
/policy show, approval prompts,/policy allow|denyconfirmations) rendered as clickable blue links because Telegram auto-detects URL patterns in plain-text messages.Fix: wrap destinations and request URLs in
<code>...</code>and send with HTML parse mode. Inside<code>Telegram does not auto-link URLs, and text still flows and wraps naturally at spaces (unlike a full<pre>block, which forced mid-identifier wrapping on long lines).Changes
internal/telegram/commands.go:htmlCodehelper wraps a string in<code>...</code>after HTML-escaping.policyShowandpolicyShowFromStorewrap destination, tool, pattern, replacement, protocols, default verdict in<code>. Section headers (ALLOW,DENY,ASK,REDACT) go through<b>.policyAllow/policyDenyconfirmation messages wrap the destination in<code>on both the store and in-memory paths.internal/telegram/bot.goFormatApprovalMessage:host:portin the connection line, the full request URL, and the MCP tool name all go through<code>.internal/telegram/approval.go: the reply sender enables HTML parse mode (with web-page preview off) when the response contains<code>or<b>. Plain-text replies are unchanged because HTML-escaping prevents those tags from appearing in non-HTML output.htmlEscapeTextfromcommands.go; everything now uses the existinghtmlEscapeinbot.go.TestPolicyShowEscapesHTML, tag-presence checks inTestPolicyShowIncludesAllFields, and updatedTestFormatApprovalMessageexpectations.Test plan
go test ./...passesgo build ./...cleangofumptclean/policyrenders destinations as inline monospace, no blue linkshost:portand request URLs as monospace/policy allow <dest>confirmation shows<dest>as monospace