🐛 fix(desktop): sanitize heterogeneous-agent attachment cache filenames#13937
Conversation
The desktop heterogeneous-agent controller used raw image ids as path segments for cache payload and metadata files. Path-like ids could escape the intended cache directory, and pre-seeded traversal targets could be treated as cache hits. Hashing the cache key removes any path semantics from user-controlled ids while preserving stable cache reuse. A regression test covers both out-of-root write prevention and ignoring pre-seeded traversal cache files. Constraint: The fix must preserve deterministic cache hits without trusting user-controlled path segments Rejected: path.basename(image.id) | collapses distinct ids onto the same filename and leaves edge-case normalization concerns Confidence: high Scope-risk: narrow Reversibility: clean Directive: Any future cache layout change must keep user-controlled identifiers out of direct filesystem path composition Tested: Custom local reproduction against current controller source; custom local validation against patched source; regression test added for desktop controller path handling Not-tested: Upstream vitest/CI run in this workspace (desktop dependencies unavailable locally)
|
@abiatarprado is attempting to deploy a commit to the LobeHub OSS Team on Vercel. A member of the Team first needs to authorize it. |
|
@Innei - This is a desktop platform fix for path traversal in the heterogeneous-agent attachment cache. Please take a look. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d6964d0404
ℹ️ About Codex in GitHub
Your team has set up Codex to 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 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…ehavior The traversal regression test uses a data:text/plain URL under the desktop node test environment, so the controller returns text/plain from the fetch response headers. The expectation now matches the actual runtime behavior instead of assuming the image/png fallback path. Constraint: The regression should validate cache isolation rather than rely on an incorrect MIME fallback assumption Rejected: Mock fetch in the regression test | adds extra indirection without improving the path traversal coverage Confidence: high Scope-risk: narrow Reversibility: clean Directive: Keep this test focused on path safety and cache-hit behavior; avoid coupling it to unrelated transport mocks unless the controller logic changes Tested: Local patched-controller validation harness; static review against desktop vitest node environment behavior Not-tested: Upstream vitest/CI run in this workspace (desktop dependencies unavailable locally)
|
Quick update:
At the moment, the remaining blockers are maintainer-side:
So there does not seem to be an additional contributor agreement/CLA step for me to complete here. The remaining actions look like maintainer approval gates rather than contributor setup issues. Once workflows are approved, the PR should be ready for normal review on its technical merits. |
…amespace The first regression test used a fixed traversal target name under the shared system temp directory. Switching that escape target to a unique name derived from the test's temporary appStoragePath preserves the same out-of-root check while avoiding accidental interaction with unrelated files under /tmp. Constraint: The regression must still verify escape prevention beyond appStoragePath without touching shared fixed temp paths Rejected: Remove the out-of-root assertion entirely | weakens coverage for the exact traversal behavior this PR is meant to guard Confidence: high Scope-risk: narrow Reversibility: clean Directive: Keep filesystem regressions hermetic; if a test needs to reason about escaped paths, derive them from per-test temp namespaces whenever possible Tested: Static review of resolved path behavior before/after the change Not-tested: Upstream vitest/CI run in this workspace (desktop dependencies unavailable locally)
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## canary #13937 +/- ##
===========================================
- Coverage 86.05% 66.78% -19.28%
===========================================
Files 599 2088 +1489
Lines 49891 177438 +127547
Branches 7755 17474 +9719
===========================================
+ Hits 42934 118500 +75566
- Misses 6833 58814 +51981
Partials 124 124
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
|
❤️ Great PR @shaun0927 ❤️ The growth of project is inseparable from user feedback and contribution, thanks for your contribution! If you are interesting with the lobehub developer community, please join our discord and then dm @arvinxx or @canisminor1990. They will invite you to our private developer channel. We are talking about the lobe-chat development or sharing ai newsletter around the world. |
# 🚀 LobeHub v2.1.53 (20260427) **Release Date:** April 27, 2026 **Since v2.1.52:** 194 merged PRs · 17 contributors > Introduce Heterogeneous Agent — Claude Code and Codex run as first-class desktop runtimes, paired with a new Agent Signal package, sharper desktop UX, and a wave of flagship model additions. --- ## ✨ Highlights - **Introduce Heterogeneous Agent** — Claude Code and Codex run as first-class desktop agents: subagent rendering, partial-message streaming, multi-turn resume, terminal error surfacing, rich tool inspectors, and runtime polish. (#14162, #13754, #14067, #14001, #13970, #13942) - **Screen capture & Quick Chat tray** — New desktop screen capture overlay (macOS permission-gated) with Quick Chat tray and upload pipeline improvements; chat input auto-focuses on overlay mount. (#13818, #14097, #14105) - **Desktop topic & tab UX** — Dedicated topic popup window with cross-window sync, Cmd+W/Cmd+T tab shortcuts, TabBar polish, recent working directories expanded to 20, and human approval notifications. (#13957, #13983, #13972, #14036, #14092) - **Git workflow built-in** — One-click pull/push from the branch chip, ahead/behind badge, and submodule/worktree repo detection. (#14041, #13980, #13978) - **Agent Signal package** — New `@lobechat/agent-signal` runtime for dynamic memory feedback signals, with OTel metrics and self-iteration in Lab. (#14157, #14170, #14159, #14169, #14187) - **New models** — Claude Opus 4.7 with `xhigh` effort tier, GPT-5.5, DeepSeek V4 Flash/Pro with reasoning slider, Kimi K2.6, MiMo-V2.5/Pro, gpt-image-2, Qwen3.6 Flash/Plus, and Pixverse-c1. (#13903, #14147, #14114, #14004, #14089, #14039, #13923) - **New providers** — OpenCode Zen, OpenCode Go, and Azure OpenAI Router runtime. (#13943, #14064, #13823) - **Mobile settings overhaul** — Full settings menu and responsive profile layout for mobile. (#14019) --- ## 🏗️ Heterogeneous Agent - Claude Code runtime, working-directory awareness, and sidebar polish. (#13970) - CC subagent rendering with persistent streamed text; parallel-tool orphan fix. (#14001, #13968, #14024) - Per-step usage persisted to each step assistant message. (#13964) - Per-phase workflow expand defaults; full-expand toggle with three-level expansion. (#14171, #13906) - Hetero-mode actions bar; tool inspector polish. (#13963, #14034, #14030) - Codex desktop integration with rich tool rendering and devtools preview. (#14067, #14100) - Codex terminal error surfacing and CLI output tracing. (#14166) - Tighten `isCanUseVision` default and add aggregator fallback. (#14172) - Persist `ccSessionId` in topic metadata for CC multi-turn resume. (#13902) - CC account card, topic filter, and integration polish. (#13955, #13942, #13950) - Token-level deltas streamed via `--include-partial-messages`. (#13929) --- ## 🧠 Agent Signal & Self-Iteration - New `@lobechat/agent-signal` package with dynamic feedback signals. (#14157) - AgentSignalRuntime wired through agent-tracing and observability-otel metrics. (#14170, #14159) - Self-iteration feature flag added to Lab; front-side flag check. (#14169, #14186) - Signal policy for receiving memory feedback dynamically. (#14187) --- ## 💬 Conversation - Queue follow-up sends during running CC turns. (#14179) - Persist per-topic chat scroll position; pin user message + fold long messages. (#14191, #14056) - Inline resend when editing last user message. (#14080) - Disable first-block markdown streaming to prevent flicker. (#14193, #13904) - Prevent Markdown stream replay when vlist remounts streaming items. (#14086) - Stop repinning after manual scroll; unify scroll-to-user + spacer hooks. (#14099, #14132) --- ## 📱 Platforms & Integrations ### Desktop / Electron - Screen capture overlay, Quick Chat tray, and upload pipeline improvements. (#13818) - macOS permission gate for screen capture; auto-focus chat panel input. (#14097, #14105) - Dedicated topic popup window with cross-window sync. (#13957) - TabBar polish: `+` button for new topic, dark theme blend, close icon by default. (#13972, #14203, #13973) - Recent working directories expanded from 5 to 20; submodule/worktree repo detection. (#14036, #13978) - Cmd+W / Cmd+T tab shortcuts and global shortcut consolidation. (#13983, #13880) - Linux icon configuration; human approval desktop notifications. (#14042, #14092) ### Git Workflow - One-click pull/push from branch chip; ahead/behind badge with refactored GitCtr. (#14041, #13980) ### Mobile - Full settings menu and responsive profile layout. (#14019) - Agent route added to mobile router; mobile agent topic route registered. (#14103, #14158) - Session list skeleton row layout corrected. (#14040) ### Bot / Messaging - DM strategy support; bot emoji and markdown render optimization. (#14201, #14091, #14140) - Slack webhook fix; bot platform setup guide reference. (#14052, #14121) --- ## 🤖 Models & Providers ### New models - **Claude Opus 4.7** with `xhigh` effort tier; strip temperature/top_p. (#13903, #13909) - **GPT-5.5**. (#14147) - **DeepSeek V4** Flash/Pro cards with reasoning slider; cache-hit and Pro discount pricing. (#14114, #14209, #14196, #14131) - **Kimi K2.6** model with LobeHub-hosted card. (#14004, #14006) - **MiMo-V2.5 / V2.5-Pro**. (#14089) - **gpt-image-2**, **Qwen3.6 Flash/Plus**, **Pixverse-c1**. (#14039, #13923) ### New providers - **OpenCode Zen** and **OpenCode Go** with env-var support. (#13943, #14064) - **Azure OpenAI Router** runtime support. (#13823) - Model alias mapping for image and video runtimes. (#13896) - Seedance video models migrated to Dreamina. (#14144) ### Runtime reliability - Sanitize invalid tool_call arguments to unbreak strict providers. (#14033) - Tolerate null `function.name` in streaming tool_call deltas. (#14139) - Preserve Gemini 3 `thoughtSignature` in `call_tools_batch` normalization. (#14032) - Downgrade `image_url` parts when target model lacks vision. (#14029) - Preserve Cloudflare provider error context. (#14136) - Use `safety_identifier` for OpenAI Responses API. (#14148) - Unwrap underlying PG error in `formatErrorEventData`. (#14038) --- ## 🖥️ User Experience - **Onboarding** — Preset agent naming suggestions, structured hunk ops for `updateDocument`, persona analytics snapshot, footer promotion pipeline, wrap-up button. (#13931, #13989, #13930, #13853, #13934) - **Document workflow** — Agent documents promoted as primary workspace panel; history management and compare workflow; web-crawl docs associated with agent documents. (#13924, #13725, #13893) - **cmdk** — Agent identity surfaced on topic search results; topic/message search scoped to current agent. (#14204, #13960) - **Floating chat panel** and workspace improvements. (#13887) - **Topic completion status** with dropdown action and filter. (#14005) --- ## 🔧 Tooling - Redis-backed feature flag provider for runtime config. (#14098) - Vite upgraded to 8.0.0 with Rolldown strict execution order. (#12720, #14058) - `@lobechat/model-bank` automated npm release with provenance. (#14015, #14017, #14018) - Skill activation fallback when `activateTools` cannot find identifier. (#14010) - Cron tool: timezone and existing jobs injected into system prompt; clarified `lobe-gtd` and `lobe-cron` descriptions. (#14012, #14013) --- ## 🔒 Security & Reliability - **Security:** uuid bumped to v14 (advisory). (#14083) - **Security:** validate avatar URL and scope old-avatar deletion to owner. (#13982) - **Security:** clear OIDC sessions on better-auth signout; return 401 (not 500) for expired OIDC JWT. (#13916, #14014) - **Reliability:** scope pending-approval check to current assistant turn. (#14182) - **Reliability:** sanitize heterogeneous-agent attachment cache filenames. (#13937) - **Reliability:** reduce subagent task status error noise. (#14026) --- ## 👥 Contributors Huge thanks to **17 contributors** who shipped **194 merged PRs** this week. @hardy · @shaun0927 · @hezhijie0327 · @sxjeru · @arvinxx · @Innei · @tjx666 · @lijian · @neko · @rdmclin2 · @AmAzing129 · @sudongyuer · @CanisMinor · @rivertwilight Plus @lobehubbot and renovate[bot] for maintenance. --- **Full Changelog**: v2.1.52...v2.1.53
…es (lobehub#13937) * Keep heterogeneous-agent attachment cache writes inside the cache root The desktop heterogeneous-agent controller used raw image ids as path segments for cache payload and metadata files. Path-like ids could escape the intended cache directory, and pre-seeded traversal targets could be treated as cache hits. Hashing the cache key removes any path semantics from user-controlled ids while preserving stable cache reuse. A regression test covers both out-of-root write prevention and ignoring pre-seeded traversal cache files. Constraint: The fix must preserve deterministic cache hits without trusting user-controlled path segments Rejected: path.basename(image.id) | collapses distinct ids onto the same filename and leaves edge-case normalization concerns Confidence: high Scope-risk: narrow Reversibility: clean Directive: Any future cache layout change must keep user-controlled identifiers out of direct filesystem path composition Tested: Custom local reproduction against current controller source; custom local validation against patched source; regression test added for desktop controller path handling Not-tested: Upstream vitest/CI run in this workspace (desktop dependencies unavailable locally) * Keep heterogeneous-agent cache regression aligned with runtime MIME behavior The traversal regression test uses a data:text/plain URL under the desktop node test environment, so the controller returns text/plain from the fetch response headers. The expectation now matches the actual runtime behavior instead of assuming the image/png fallback path. Constraint: The regression should validate cache isolation rather than rely on an incorrect MIME fallback assumption Rejected: Mock fetch in the regression test | adds extra indirection without improving the path traversal coverage Confidence: high Scope-risk: narrow Reversibility: clean Directive: Keep this test focused on path safety and cache-hit behavior; avoid coupling it to unrelated transport mocks unless the controller logic changes Tested: Local patched-controller validation harness; static review against desktop vitest node environment behavior Not-tested: Upstream vitest/CI run in this workspace (desktop dependencies unavailable locally) * Keep heterogeneous-agent cache regression isolated to the temp test namespace The first regression test used a fixed traversal target name under the shared system temp directory. Switching that escape target to a unique name derived from the test's temporary appStoragePath preserves the same out-of-root check while avoiding accidental interaction with unrelated files under /tmp. Constraint: The regression must still verify escape prevention beyond appStoragePath without touching shared fixed temp paths Rejected: Remove the out-of-root assertion entirely | weakens coverage for the exact traversal behavior this PR is meant to guard Confidence: high Scope-risk: narrow Reversibility: clean Directive: Keep filesystem regressions hermetic; if a test needs to reason about escaped paths, derive them from per-test temp namespaces whenever possible Tested: Static review of resolved path behavior before/after the change Not-tested: Upstream vitest/CI run in this workspace (desktop dependencies unavailable locally) --------- Co-authored-by: OpenAI Codex <codex@example.com>
💻 Change Type
🔗 Related Issue
Closes #13936
🔀 Description of Change
This keeps the fix intentionally narrow to improve mergeability:
image.idpath segments inHeterogeneousAgentCtr.resolveImage()with a stable SHA-256 cache keyheteroAgent/filesWhy this shape:
path.basename(image.id)would prevent some escapes, but it can still collapse different ids onto the same filename🧪 How to Test
Local validation performed:
heteroAgent/filesSuggested CI/local test command:
📝 Additional Information
v2.1.52-canary.6contains the feature; latest stablev2.1.51does not yet include this controller)TempFileManager.writeTempFile(), but keeps the scope limited to the desktop attachment cache