Skip to content

fix(telegram): hide tool progress by default#71351

Merged
steipete merged 3 commits into
openclaw:mainfrom
neeravmakwana:fix/telegram-tool-progress-opt-in
Apr 25, 2026
Merged

fix(telegram): hide tool progress by default#71351
steipete merged 3 commits into
openclaw:mainfrom
neeravmakwana:fix/telegram-tool-progress-opt-in

Conversation

@neeravmakwana

Copy link
Copy Markdown
Contributor

Summary

  • Fixes Bug: Tool call progress messages leaking to Telegram as visible messages #71320 by making Telegram tool-progress preview updates opt-in instead of default-on.
  • Suppresses the generic default tool/progress messages on Telegram so tool lifecycle status does not leak as separate chat messages.
  • Updates Telegram docs, streaming docs, config UI hint text, and changelog to match the runtime default.

Root Cause

Telegram enabled resolveChannelStreamingPreviewToolProgress() with the shared default of true, so tool/item/plan lifecycle events were turned into visible Working… preview text during normal Telegram replies. Disabling that path alone would have allowed the generic Working: progress messages to surface separately, so Telegram also needs to suppress default tool-progress message delivery at runtime.

Why This Is Safe

The change is Telegram-scoped. Final assistant replies, block streaming, reasoning streaming, status reactions, media tool payloads, approval payloads, and error delivery continue to use the existing delivery paths. Users who intentionally want visible Telegram progress can still opt in with channels.telegram.streaming.preview.toolProgress=true, which keeps progress inside the edited preview message.

Security / Runtime Controls Unchanged

This does not rely on prompt instructions. The suppression is enforced in the Telegram dispatch runtime via suppressDefaultToolProgressMessages, while the existing runtime filtering that still allows media, approval, and error payloads remains unchanged.

Self-Review

  • Test helper defaults match production defaults: empty Telegram config now suppresses progress in the regression test and production code.
  • Config default documentation is aligned in Telegram docs, streaming docs, config UI hints, and changelog.
  • Generated config docs were checked and did not change.

Tests

  • pnpm test extensions/telegram/src/bot-message-dispatch.test.ts (pass)
  • pnpm test:extension telegram (pass)
  • pnpm config:docs:check (pass)
  • git diff --check (pass)
  • pnpm check:changed (fails in unrelated extension-wide typecheck before reaching changed tests; errors are duplicate nested @mariozechner/pi-ai / pi-agent-core type identities in other extensions such as extensions/amazon-bedrock-mantle, extensions/anthropic, and extensions/codex)

Made with Cursor

@aisle-research-bot

aisle-research-bot Bot commented Apr 25, 2026

Copy link
Copy Markdown

🔒 Aisle Security Analysis

We found 1 potential security issue(s) in this PR:

# Severity Title
1 🟡 Medium Telegram preview Markdown injection via capped inline-code backtick fence in tool-progress formatting
1. 🟡 Telegram preview Markdown injection via capped inline-code backtick fence in tool-progress formatting
Property Value
Severity Medium
CWE CWE-116
Location extensions/telegram/src/bot-message-dispatch.ts:222-228

Description

formatProgressAsMarkdownCode() attempts to wrap tool progress lines in Markdown inline-code using a backtick fence longer than any backtick run in the content, but caps the fence length at 10.

Because Telegram preview rendering later calls renderTelegramHtmlText() (markdown → HTML) with linkification enabled, an attacker-controlled progressText containing 10 or more consecutive backticks within the first 300 characters can prematurely close the inline-code span and inject arbitrary Markdown after it.

This enables:

  • Injection of clickable links (including tg://user?id=... mentions) and other formatting into the streaming preview
  • Potential user deception/phishing via injected link labels

Vulnerable code:

const maxBacktickRun = Math.max(
  0,
  ...Array.from(clipped.matchAll(/`+/g), (match) => match[0].length),
);
const fence = "`".repeat(Math.min(maxBacktickRun + 1, MAX_PROGRESS_MARKDOWN_FENCE_CHARS));
return `${fence}${clipped}${fence}`;

Example payload (in tool progress text) that can break out when MAX_PROGRESS_MARKDOWN_FENCE_CHARS = 10:

  • "".repeat(10) + " label"`

This would generate a 10-backtick fence, which is not longer than the internal 10-backtick run, allowing the injected [label](tg://...) to be parsed as a link.

Recommendation

Do not cap the fence length below maxBacktickRun + 1.

Safer options:

  1. Remove the cap (preferred):
const fence = "`".repeat(maxBacktickRun + 1);
return `${fence}${clipped}${fence}`;
  1. If you must bound output size, escape/encode progress text instead of relying on Markdown fences (e.g., render as HTML and escape):
import { escapeHtml } from "./format"; // or a local helper

function formatProgressAsHtmlCode(text: string): string {
  const clipped = clipProgressMarkdownText(text);
  return `<code>${escapeHtml(clipped)}</code>`;
}

…and ensure preview updates use textMode: "html" or otherwise avoid interpreting attacker-controlled Markdown.

  1. Alternatively, hard-replace backticks inside progress text:
const sanitized = clipped.replace(/`/g, "ˋ");
return `\`${sanitized}\``;

Any approach should guarantee that attacker-controlled content cannot terminate the intended code span and introduce new Telegram entities (links/mentions).


Analyzed PR: #71351 at commit ee6927a

Last updated on: 2026-04-25T02:27:29Z

@greptile-apps

greptile-apps Bot commented Apr 25, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR changes Telegram's tool-progress behavior from opt-out to opt-in by passing false as the default to resolveChannelStreamingPreviewToolProgress and unconditionally setting suppressDefaultToolProgressMessages: true so tool lifecycle events are never delivered as separate chat messages unless the user explicitly enables streaming.preview.toolProgress. Documentation, config UI hints, and changelog are updated consistently, and two new regression tests cover both the default-suppressed and the explicit-opt-in paths.

Confidence Score: 5/5

Safe to merge — the change is Telegram-scoped, well-tested, and does not affect other channels or delivery paths.

No logical, security, or correctness issues found. The default override is correctly threaded through the function call, suppressDefaultToolProgressMessages is correctly made unconditional, tests cover both the off (default) and on (opt-in) paths, and all documentation is aligned.

No files require special attention.

Reviews (1): Last reviewed commit: "fix(telegram): hide tool progress by def..." | Re-trigger Greptile

@openclaw-barnacle openclaw-barnacle Bot added docs Improvements or additions to documentation channel: telegram Channel integration: telegram size: S labels Apr 25, 2026
@neeravmakwana

Copy link
Copy Markdown
Contributor Author

Addressed the review findings in follow-up commit 2edd0dd4fd.

What changed:

  • Kept Telegram quiet by default, but clarified and tested that runtime-enforced safety/control payloads are not hidden by generic progress suppression. In particular, execApproval tool payloads still deliver when suppressDefaultToolProgressMessages is active; media-only tool payloads and error payloads remain covered by the existing dispatcher exceptions.
  • Hardened the explicit opt-in Telegram progress preview path. Tool/progress entries are now inserted as Markdown code spans before Telegram markdown-to-HTML rendering, so attacker-shaped progress strings like [label](tg://user?id=123) stay literal and do not become links or mentions in the live preview.
  • Extended the Telegram dispatch regression test to cover the default-suppressed path, explicit opt-in progress rendering, and the no-anchor rendering behavior.

Why the behavior is still safe:

  • The fix does not depend on prompt wording. Telegram suppresses default progress through the dispatch runtime, and opt-in preview progress is still gated by channels.telegram.streaming.preview.toolProgress=true.
  • Final assistant responses, block streaming, reasoning streaming, status reactions, media tool payloads, approval payloads, and error delivery continue to use the existing runtime delivery paths.

Validation:

  • pnpm test extensions/telegram/src/bot-message-dispatch.test.ts src/auto-reply/reply/dispatch-from-config.test.ts
  • pnpm test:extension telegram
  • pnpm config:docs:check
  • git diff --check

Known unrelated validation note:

  • pnpm check:changed still fails before reaching the changed tests due to existing extension-wide duplicate nested @mariozechner/pi-ai / pi-agent-core type identity errors in other extensions, as noted in the PR body.

@neeravmakwana

Copy link
Copy Markdown
Contributor Author

@codex /review

@steipete
steipete force-pushed the fix/telegram-tool-progress-opt-in branch from 2edd0dd to 529f89a Compare April 25, 2026 02:17

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2edd0dd4fd

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +215 to +216
const fence = "`".repeat(maxBacktickRun + 1);
return `${fence}${text}${fence}`;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Handle edge backticks when wrapping progress text in code spans

The new wrapper builds ${fence}${text}${fence} directly, which breaks for progress strings that start or end with a backtick because the delimiter run merges with content (for example, input `foo becomes `````foo````). In that case markdown no longer treats it as code, so link/format parsing can reappear in Telegram previews despite this change intending to neutralize it. This affects Telegram when streaming.preview.toolProgress=true and a tool/item progress line begins or ends with `.

Useful? React with 👍 / 👎.

@steipete
steipete force-pushed the fix/telegram-tool-progress-opt-in branch from 529f89a to ee6927a Compare April 25, 2026 02:18
@steipete

Copy link
Copy Markdown
Contributor

Deep-reviewed and pushed a maintainer fixup on top of this PR.

Review findings:

  • Root cause checks out: Telegram was using the shared preview tool-progress default, so tool/item lifecycle events could become visible Working… draft/progress text.
  • Runtime suppression is in the right layer: suppressDefaultToolProgressMessages still allows media, error, and exec-approval payloads through the core dispatcher.
  • The remaining issue was the explicit opt-in preview formatter: long backtick runs in tool progress could force unbounded Markdown fence allocation and oversized preview text.

What I changed:

  • Rebased onto current main and resolved the changelog conflict.
  • Added bounded progress preview formatting: max 300 progress chars and max 10 Markdown fence chars.
  • Added a regression with a long backtick/link payload proving the preview remains bounded and does not render a Telegram HTML link.

Local verification:

  • pnpm test extensions/telegram/src/bot-message-dispatch.test.ts src/auto-reply/reply/dispatch-from-config.test.ts
  • pnpm config:docs:check
  • pnpm check:changed

Thanks @neeravmakwana.

@steipete
steipete merged commit f29e15c into openclaw:main Apr 25, 2026
9 checks passed
@steipete

Copy link
Copy Markdown
Contributor

Landed via rebase onto main.

  • Source head: ee6927a
  • Merge commit: f29e15c
  • Local gates: pnpm test extensions/telegram/src/bot-message-dispatch.test.ts src/auto-reply/reply/dispatch-from-config.test.ts; pnpm config:docs:check; pnpm check:changed

Thanks @neeravmakwana.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

channel: telegram Channel integration: telegram docs Improvements or additions to documentation size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: Tool call progress messages leaking to Telegram as visible messages

2 participants