Skip to content

fix(feishu): avoid local media fallback leaks#98251

Merged
steipete merged 4 commits into
openclaw:mainfrom
ooiuuii:fix/feishu-local-media-fallback-paths
Jul 16, 2026
Merged

fix(feishu): avoid local media fallback leaks#98251
steipete merged 4 commits into
openclaw:mainfrom
ooiuuii:fix/feishu-local-media-fallback-paths

Conversation

@ooiuuii

@ooiuuii ooiuuii commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Closes #98250

What Problem This Solves

Fixes an issue where Feishu recipients could receive host-local or private media references as fallback text when a media upload/fetch failed. The affected surfaces include local-image text auto-send fallback, sendMedia upload fallback, document-comment media fallback, and reply-dispatcher final media fallback.

Why This Change Was Made

This adds a Feishu-local media fallback helper that only includes http:// or https:// fallback links after shared SSRF/DNS validation, suppresses local/private media fetch failures, and otherwise preserves the caption text with Media upload failed. Please try again.

The successful media upload path is unchanged. Public URL fallback remains available when the URL passes the existing public-host checks; Feishu auth, account selection, TTS routing, and provider behavior are out of scope.

User Impact

Feishu users no longer see local filesystem paths, file:// URLs, relative local references, loopback/private URLs, or DNS-private media references in message/comment fallback text after media upload failure. Public media URLs can still be shown as fallback links when they are safe to expose.

Evidence

Runtime proof on PR commit 71d08b4bfc, using synthetic media references only:

Feishu media fallback proof on PR commit 71d08b4bfc
local path:
  output=spoken reply\n\nMedia upload failed. Please try again.
  unsafe reference exposed=false
loopback URL:
  output=spoken reply\n\nMedia upload failed. Please try again.
  unsafe reference exposed=false
public URL:
  output=spoken reply\n\n📎 https://example.com/openclaw-voice.mp3
  public fallback preserved=true

Focused verification:

  • node scripts\run-vitest.mjs extensions\feishu\src\outbound.test.ts --testNamePattern="does not leak local-image paths if auto-send fails|does not leak local media paths in comment-thread media fallbacks|does not leak loopback URLs in comment-thread media fallbacks|keeps skipped voice text in the upload failure fallback|does not leak URLs rejected by private-DNS media checks"
    • 1 Test File passed
    • 5 passed | 46 skipped
  • node scripts\run-vitest.mjs extensions\feishu\src\reply-dispatcher.test.ts --testNamePattern="keeps skipped voice text in the upload failure fallback|does not leak local media paths in the upload failure fallback|does not leak URLs rejected by private-DNS checks in the upload failure fallback"
    • 1 Test File passed
    • 3 passed | 83 skipped

Full touched test files:

  • node scripts\run-vitest.mjs extensions\feishu\src\outbound.test.ts
    • 1 Test File passed
    • 51 passed
  • node scripts\run-vitest.mjs extensions\feishu\src\reply-dispatcher.test.ts
    • 1 Test File passed
    • 86 passed

Static checks and review:

  • node_modules\.bin\oxlint.CMD extensions\feishu\src\outbound.ts extensions\feishu\src\outbound.test.ts extensions\feishu\src\reply-dispatcher.ts extensions\feishu\src\reply-dispatcher.test.ts extensions\feishu\src\media-fallback.ts
  • node scripts\run-oxlint.mjs --tsconfig config/tsconfig/oxlint.extensions.json extensions\feishu\src\media-fallback.ts extensions\feishu\src\outbound.ts extensions\feishu\src\outbound.test.ts extensions\feishu\src\reply-dispatcher.ts extensions\feishu\src\reply-dispatcher.test.ts
  • node scripts\run-oxlint-shards.mjs --threads=8
  • node scripts\run-tsgo.mjs -p tsconfig.extensions.json --incremental --tsBuildInfoFile .artifacts/tsgo-cache/extensions.tsbuildinfo
  • node scripts\run-tsgo.mjs -p test\tsconfig\tsconfig.extensions.test.json --incremental --tsBuildInfoFile .artifacts\tsgo-cache\extensions-test.tsbuildinfo
  • node scripts\check-extension-package-tsc-boundary.mjs --mode=compile
  • node scripts\check-extension-package-tsc-boundary.mjs --mode=canary
  • git diff --check
  • $env:PYTHONUTF8='1'; python .agents\skills\autoreview\scripts\autoreview --mode local
    • autoreview clean: no accepted/actionable findings reported
  • $env:PYTHONUTF8='1'; python .agents\skills\autoreview\scripts\autoreview --mode commit --commit HEAD
    • autoreview clean: no accepted/actionable findings reported

@openclaw-barnacle openclaw-barnacle Bot added channel: feishu Channel integration: feishu size: M labels Jun 30, 2026
@clawsweeper

clawsweeper Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed June 30, 2026, 4:14 PM ET / 20:14 UTC.

Summary
The PR adds a Feishu media fallback helper and routes local-image, comment-thread, sendMedia failure, and reply-dispatcher media-error fallbacks through SSRF-validated public-link handling.

PR surface: Source +105, Tests +234. Total +339 across 5 files.

Reproducibility: yes. Source inspection on current main shows local-image, comment-thread, sendMedia, and reply-dispatcher fallback paths can put raw media references into Feishu-visible text after delivery failure.

Review metrics: 1 noteworthy metric.

  • Fallback surfaces tightened: 4 changed. The diff covers the reported local-image auto-send, sendMedia upload failure, comment-thread media, and reply-dispatcher media-error fallback paths.

Root-cause cluster
Relationship: fixed_by_candidate
Canonical: #98250
Summary: The linked issue is the canonical report for the Feishu media fallback disclosure, and this PR is its direct candidate fix.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Merge readiness
Overall: 🐚 platinum hermit
Proof: 🐚 platinum hermit
Patch quality: 🐚 platinum hermit
Result: ready for maintainer review.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • none.

Risk before merge

  • [P2] The PR intentionally stops showing local, private, and DNS-blocked media references that existing Feishu users may previously have seen as fallback text; maintainers should accept that compatibility change as the security tradeoff before merge.

Maintainer options:

  1. Accept the fallback tightening (recommended)
    Treat suppression of local/private/DNS-blocked media fallback references as the intended security behavior and land after exact-head checks pass.
  2. Pause for fallback policy review
    If maintainers want to preserve more of the old Feishu fallback behavior, pause this PR for an explicit channel/security policy decision before merge.

Next step before merge

  • [P2] The remaining action is maintainer/security review of the compatibility tradeoff and normal exact-head CI gating; there is no narrow automated repair to queue right now.

Security
Cleared: The diff mitigates a Feishu fallback disclosure path and does not add dependencies, workflows, package scripts, credential handling, or broader code-execution surface.

Review details

Best possible solution:

Land this Feishu-local sanitizer after maintainers accept the fallback compatibility tightening and exact-head CI is green.

Do we have a high-confidence way to reproduce the issue?

Yes. Source inspection on current main shows local-image, comment-thread, sendMedia, and reply-dispatcher fallback paths can put raw media references into Feishu-visible text after delivery failure.

Is this the best way to solve the issue?

Yes. A Feishu-owned helper reused across outbound and reply-dispatcher paths is the narrowest maintainable fix, and it correctly relies on shared plugin SDK SSRF/media contracts instead of adding a new core path or config option.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 640258d7b31d.

Label changes

Label justifications:

  • P1: The PR fixes a current Feishu channel path that can expose local paths or private media references to recipients when media delivery fails.
  • merge-risk: 🚨 compatibility: The PR deliberately changes existing Feishu fallback text by suppressing local/private/DNS-blocked media references that were previously sent as text or links.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🐚 platinum hermit and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (terminal): The PR body contains after-fix terminal runtime output on the current head showing local and loopback references suppressed and a public URL fallback preserved; contributors should keep any future proof redacted for private paths, URLs, tokens, or endpoints.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body contains after-fix terminal runtime output on the current head showing local and loopback references suppressed and a public URL fallback preserved; contributors should keep any future proof redacted for private paths, URLs, tokens, or endpoints.
Evidence reviewed

PR surface:

Source +105, Tests +234. Total +339 across 5 files.

View PR surface stats
Area Files Added Removed Net
Source 3 120 15 +105
Tests 2 236 2 +234
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 5 356 17 +339

What I checked:

  • Repository policy applied: Root and extensions-scoped AGENTS.md were read; the extension guide permits plugin-local imports from public plugin SDK subpaths, and root policy treats fallback behavior as compatibility-sensitive. (AGENTS.md:1, 640258d7b31d)
  • Current main source reproduction: outbound fallbacks: Current main can fall through from failed local-image auto-send into plain text, append raw mediaUrl for comment-thread media fallback, and append 📎 ${mediaUrl} after sendMedia failure. (extensions/feishu/src/outbound.ts:595, 640258d7b31d)
  • Current main source reproduction: reply dispatcher: Current main formats reply-dispatcher media fallback text from the raw mediaUrl and sends it through the Feishu text reply path when media delivery fails. (extensions/feishu/src/reply-dispatcher.ts:82, 640258d7b31d)
  • Dependency contract checked: The plugin SDK exposes LocalMediaAccessError and SSRF helpers; resolvePinnedHostnameWithPolicy rejects blocked hostnames and DNS answers resolving to private/internal addresses under the default policy. (src/infra/net/ssrf.ts:541, 640258d7b31d)
  • PR helper behavior: On the PR head, the new helper only preserves http/https media references after blocked-hostname and pinned-DNS checks; otherwise it preserves caption text and returns generic upload-failure text. (extensions/feishu/src/media-fallback.ts:18, 71d08b4bfc6d)
  • PR caller coverage: The PR routes local-image failures, comment-thread media fallback, sendMedia failure fallback, and reply-dispatcher media-error fallback through the sanitized Feishu helper. (extensions/feishu/src/outbound.ts:600, 71d08b4bfc6d)

Likely related people:

  • steipete: GitHub file history shows repeated recent Feishu outbound/test and shared media/SDK refactor work adjacent to the fallback and SSRF/media-loading paths. (role: recent area contributor; confidence: medium; commits: 77d9ac30bb8d, 538605ff44d2, 58912f8fd842; files: extensions/feishu/src/outbound.ts, extensions/feishu/src/outbound.test.ts, src/media/web-media.ts)
  • obviyus: GitHub commit metadata shows obviyus authored or committed the two most recent reply-dispatcher changes near the affected final media reply surface. (role: recent reply-dispatcher committer; confidence: high; commits: f078962d1759, 62fa674a399e; files: extensions/feishu/src/reply-dispatcher.ts, extensions/feishu/src/reply-dispatcher.test.ts)
  • xydigit-zt: Commit history credits xydigit-zt with recent Feishu reply target routing work in the same reply-dispatcher module. (role: adjacent feature author; confidence: medium; commits: 62fa674a399e; files: extensions/feishu/src/reply-dispatcher.ts)
  • vincentkoc: GitHub file history shows recent Feishu media maintenance and review-adjacent work, including media helper cleanup and CJK filename recovery touching the media surface. (role: recent Feishu media contributor; confidence: medium; commits: ff4808f94d67, 7b659543e13b; files: extensions/feishu/src/media.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. P1 High-priority user-facing bug, regression, or broken workflow. labels Jun 30, 2026
@clawsweeper clawsweeper Bot added rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. and removed rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. labels Jun 30, 2026
@ooiuuii
ooiuuii force-pushed the fix/feishu-local-media-fallback-paths branch from 802ca8d to f310ae4 Compare June 30, 2026 19:52
@clawsweeper clawsweeper Bot added the merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. label Jun 30, 2026
@ooiuuii
ooiuuii force-pushed the fix/feishu-local-media-fallback-paths branch from f310ae4 to 71d08b4 Compare June 30, 2026 20:10
@clawsweeper clawsweeper Bot added rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. labels Jun 30, 2026
@steipete steipete self-assigned this Jul 16, 2026
@steipete
steipete force-pushed the fix/feishu-local-media-fallback-paths branch from 71d08b4 to dc85905 Compare July 16, 2026 21:46
@steipete
steipete force-pushed the fix/feishu-local-media-fallback-paths branch from dc85905 to 12756f0 Compare July 16, 2026 22:32
@steipete
steipete force-pushed the fix/feishu-local-media-fallback-paths branch from 12756f0 to e0859a2 Compare July 16, 2026 23:21
@steipete
steipete force-pushed the fix/feishu-local-media-fallback-paths branch from e0859a2 to ad70916 Compare July 16, 2026 23:41
@steipete
steipete merged commit b1eaf53 into openclaw:main Jul 16, 2026
88 of 101 checks passed
@steipete

Copy link
Copy Markdown
Contributor

Merged via squash.

github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jul 17, 2026
* Fix Feishu local media fallback leaks

* refactor(feishu): clarify media fallback link style

* fix(feishu): reject credentialed media fallbacks

* fix(feishu): reject ambiguous media fallback URLs

---------

Co-authored-by: Peter Steinberger <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

channel: feishu Channel integration: feishu merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. P1 High-priority user-facing bug, regression, or broken workflow. proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. size: M status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Feishu media fallback can expose local media references

2 participants