Skip to content

fix(feishu extension): forward reply target context in outbound sends#33575

Closed
bmendonca3 wants to merge 2 commits intoopenclaw:mainfrom
bmendonca3:bm/33572-feishu-outbound-reply-target-forwarding
Closed

fix(feishu extension): forward reply target context in outbound sends#33575
bmendonca3 wants to merge 2 commits intoopenclaw:mainfrom
bmendonca3:bm/33572-feishu-outbound-reply-target-forwarding

Conversation

@bmendonca3
Copy link
Copy Markdown
Contributor

Summary

Describe the problem and fix in 2–5 bullets:

  • Problem: extensions/feishu outbound sendText/sendMedia dropped reply target context (replyToId/threadId).
  • Why it matters: replies sent through outbound adapter paths could lose reply threading and post as non-replies.
  • What changed: added reply target resolver (replyToId ?? threadId) and forwarded replyToMessageId through text/media send paths.
  • What did NOT change (scope boundary): no Feishu monitor/dispatcher logic changes, no duplicate suppression changes, no dependency changes.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

User-visible / Behavior Changes

Feishu extension outbound sends now preserve reply target context by mapping replyToId/threadId to replyToMessageId for both text and media sends.

Security Impact (required)

  • New permissions/capabilities? (Yes/No) No
  • Secrets/tokens handling changed? (Yes/No) No
  • New/changed network calls? (Yes/No) No
  • Command/tool execution surface changed? (Yes/No) No
  • Data access scope changed? (Yes/No) No
  • If any Yes, explain risk + mitigation:

Repro + Verification

Environment

  • OS: macOS (local dev)
  • Runtime/container: Node 22 + pnpm
  • Model/provider: N/A
  • Integration/channel (if any): Feishu extension (extensions/feishu)
  • Relevant config (redacted): N/A (unit-level adapter repro)

Steps

  1. Call Feishu outbound sendText or sendMedia with replyToId or threadId set.
  2. Observe args passed to sendMessageFeishu / sendMediaFeishu in extensions/feishu/src/outbound.ts.
  3. In pre-fix code, replyToMessageId is not forwarded.

Expected

  • Reply target context is forwarded as replyToMessageId.

Actual

  • Reply target context is dropped.

Evidence

Attach at least one:

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Failing before fix:

  • pnpm test extensions/feishu/src/outbound.test.ts
  • New tests failed because replyToMessageId was missing.

Passing after fix:

  • pnpm test extensions/feishu/src/outbound.test.ts
  • pnpm check

Human Verification (required)

What you personally verified (not just CI), and how:

  • Verified scenarios: replyToId forwarding on sendText, threadId fallback forwarding on sendMedia.
  • Edge cases checked: existing local-image auto-convert and renderMode tests remain passing.
  • What you did not verify: live Feishu delivery against a real tenant/chat.

Compatibility / Migration

  • Backward compatible? (Yes/No) Yes
  • Config/env changes? (Yes/No) No
  • Migration needed? (Yes/No) No
  • If yes, exact upgrade steps:

Failure Recovery (if this breaks)

  • How to disable/revert this change quickly: revert commit c129a691fc.
  • Files/config to restore: extensions/feishu/src/outbound.ts, extensions/feishu/src/outbound.test.ts.
  • Known bad symptoms reviewers should watch for: Feishu outbound replies losing thread/reply target.

Risks and Mitigations

List only real risks for this PR. Add/remove entries as needed. If none, write None.

  • Risk: low risk of reply target precedence mismatch.
    • Mitigation: explicit resolver (replyToId first, then threadId) covered by regression tests.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 3, 2026

Greptile Summary

This PR correctly fixes a bug in the Feishu extension where outbound sendText and sendMedia dropped reply threading context (replyToId/threadId).

Changes verified:

  • New resolveReplyToMessageId helper properly handles the priority (check replyToId first with trimming, fall back to threadId with coercion and trimming, handle null/undefined correctly). The implementation correctly handles empty strings via truthiness checks rather than nullish coalescing.
  • replyToMessageId is correctly forwarded in sendText through all branches: normal text, card/markdown render mode, and local-image auto-convert.
  • replyToMessageId is correctly forwarded in sendMedia through all branches: caption text via sendOutboundText, media upload, media-upload fallback-to-link, and text-only (no mediaUrl) path.
  • Test coverage is complete: regression tests verify replyToId forwarding on sendText, threadId fallback on both sendText and sendMedia with caption, and the resolver is exercised across both text and media code paths.
  • The downstream send primitives (sendMessageFeishu, sendMarkdownCardFeishu, sendMediaFeishu) all accept and consume replyToMessageId, confirming the fix is complete end-to-end.

The implementation is correct, backward-compatible, and ready to merge.

Confidence Score: 5/5

  • Safe to merge — the bug fix is correctly scoped, all code paths are covered, and the implementation handles edge cases properly.
  • After verification: (1) The resolveReplyToMessageId helper correctly handles all priority/fallback scenarios with proper trimming and coercion, including empty strings via truthiness checks. (2) Both sendText and sendMedia forward the resolved replyToMessageId through all branches. (3) Test coverage is complete: caption assertions are present, both replyToId and threadId paths are tested. (4) This is a pure bug fix with no architectural changes, backward-compatible, and all downstream functions already accept the new parameter.
  • No files require special attention.

Last reviewed commit: f85ec61

@bmendonca3
Copy link
Copy Markdown
Contributor Author

@greptileai

@aisle-research-bot
Copy link
Copy Markdown

aisle-research-bot bot commented Mar 4, 2026

🔒 Aisle Security Analysis

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

# Severity Title
1 🟠 High Feishu outbound IDOR: unvalidated replyToId/threadId can redirect replies to arbitrary message/chat

1. 🟠 Feishu outbound IDOR: unvalidated replyToId/threadId can redirect replies to arbitrary message/chat

Property Value
Severity High
CWE CWE-639
Location extensions/feishu/src/outbound.ts:84-112

Description

feishuOutbound.sendText / sendMedia now accept replyToId/threadId and forward it as replyToMessageId to the Feishu send layer.

Because Feishu’s im.message.reply endpoint routes based on the replied-to message_id (not the provided to chat id), allowing a caller-controlled replyToId enables sending messages into the chat that contains that message ID, even if it differs from to.

This becomes a cross-conversation / cross-tenant risk when replyToId is derived from untrusted sources, e.g.:

  • outbound payload directive parsing can set payload.replyToId from text tags like [[reply_to: ...]] (see normalizeReplyPayloadsForDelivery()parseReplyDirectives()), meaning external callers (gateway send) or prompt-injected LLM output can supply arbitrary IDs.
  • the adapter also treats threadId as a fallback reply target and passes it as a message id.

Impact:

  • Message redirection: attacker who can influence replyToId can cause the bot/app to post in any Feishu chat where the selected account’s app has access, by using a message_id from that chat.
  • Audit/telemetry confusion: the send path returns chatId based on the requested to (receiveId), even though the reply is delivered to the replied-to message’s chat, leading to incorrect routing metadata.

Vulnerable code (new forwarding of user-controlled IDs):

sendText: async ({ cfg, to, text, accountId, replyToId, threadId }) => {
  const replyToMessageId = resolveReplyToMessageId({ replyToId, threadId });
  ...
  return sendMessageFeishu({ cfg, to, text, accountId, replyToMessageId });
}

Relevant sink (reply uses only replyToMessageId to choose destination):

if (replyToMessageId) {
  return await client.im.message.reply({
    path: { message_id: replyToMessageId },
    data: { ... }
  });
}

Recommendation

Constrain replyToMessageId to the correct account + conversation before using it.

Recommended options (choose one or combine):

  1. Allow-list reply targets (strongest): only permit replyToId values that were produced by your own inbound processing for the same session/conversation (e.g., stored in session state), and ignore/strip [[reply_to:...]] directives from untrusted gateway/user sends.

  2. Verify message belongs to the intended chat before replying:

import { getMessageFeishu } from "./send.js";

const replyInfo = replyToMessageId
  ? await getMessageFeishu({ cfg, messageId: replyToMessageId, accountId })
  : null;

if (!replyInfo || replyInfo.chatId !== normalizeFeishuTarget(to) /* or resolved receiveId */) {// refuse to reply (or fall back to non-reply create)
  replyToMessageId = undefined;
}
  1. Do not treat threadId as a message id for Feishu. Keep threadId semantics channel-specific (e.g., Telegram topics) and add an explicit replyToId field only when the caller is trusted.

Also consider returning/recording the actual destination chat id for replies (if Feishu API doesn’t provide it, fetch/verify it) to avoid misleading logs/auditing.


Analyzed PR: #33575 at commit 7230339

Last updated on: 2026-03-04T17:28:35Z

@Takhoffman
Copy link
Copy Markdown
Contributor

Thanks again for this work.

This PR is being closed as part of an AI-assisted PR merge/triage workflow.

This landed indirectly via the synthesized PR #33789, and your contribution is credited in the changelog and as a co-author on the merge commit.

Closing this PR as superseded by #33789. If anything here looks incorrect or incomplete, reply to reopen and we can reassess.

@Takhoffman
Copy link
Copy Markdown
Contributor

Closing as superseded by #33789.

@Takhoffman Takhoffman closed this Mar 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

channel: feishu Channel integration: feishu size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Feishu extension outbound sendText/sendMedia drops reply target context

2 participants