fix(desktop): harden remaining source path validation#5914
Conversation
|
@qwen-code /triage |
|
Thanks for the PR! Template looks good ✓ — all required sections present, bilingual body complete. On direction: clean security hardening that closes the last unvalidated source-slug-to-path construction site in the session MCP credential cache. Natural follow-up to #5829 and #5911. The three gaps — credential cache path construction without slug guard, validation diagnostics echoing unsafe paths, and RPC errors misclassified as I/O failures — are all legitimate defensive edges. Clearly aligned with the project's security posture. On approach: scope is tight and each change addresses a named gap. Extracting credential cache code from 中文说明感谢贡献! 模板完整 ✓ — 所有必填章节齐全,正文双语完整。 方向:干净的安全加固,闭合了 session MCP credential cache 中最后一个未经 slug guard 验证的 source-slug-to-path 构造点。自然承接 #5829 和 #5911。三个缺口——credential cache 路径构造缺少 slug 验证、validation 诊断回显不安全路径、RPC 错误被误分类为 I/O 失败——都是合理的防御性边界。与项目安全姿态完全对齐。 方案:范围紧凑,每项改动针对明确缺口。将 credential cache 代码从 — Qwen Code · qwen3.7-max |
Code ReviewIndependent proposal (from PR description, before reading diff): add Comparison with diff: the PR's approach matches or exceeds the independent proposal on every point. credential-cache.ts — Clean extraction from sources.ts (RPC handler) — validators.ts — permissions-config.ts — Single-line addition of credential-manager-renew.test.ts — Removing the unused No blockers found. All changes are focused, correct, and follow project conventions. TestingThis PR is non-UI security hardening of internal desktop path validation (credential cache reads, validation diagnostics, RPC error classification in the Electron main process). There's no CLI invocation path that exercises this behavior — tmux real-scenario testing is not applicable here. The behavior is best validated through the unit tests included in the PR (8 + 14 tests passing per the author's report). Unable to run the desktop Bun test suite in this CI environment (Bun not installed). 中文说明代码审查独立方案(从 PR 描述出发,未看 diff 前):在 credential cache 路径构造前加 与 diff 对比: PR 方案在每一点上都匹配或超过独立方案。 credential-cache.ts — 从 sources.ts(RPC handler) — validators.ts — permissions-config.ts — 在 catch 块中单行添加 credential-manager-renew.test.ts — 移除未使用的 未发现 blocker。 所有改动聚焦、正确,遵循项目惯例。 测试本 PR 是非 UI 安全加固,针对桌面端内部路径验证(credential cache 读取、validation 诊断、Electron 主进程 RPC 错误分类)。没有 CLI 调用路径可以触发此行为——tmux 真实场景测试不适用。行为最好通过 PR 中包含的单元测试验证(作者报告 8 + 14 测试通过)。 当前 CI 环境无法运行桌面 Bun 测试套件(未安装 Bun)。 — Qwen Code · qwen3.7-max |
ReflectionGoing back to the independent proposal: I would have made the same changes — add slug validation before path construction, sanitize diagnostic paths, separate error logging, add debug logging, fix test isolation. The PR matches on every point and the module extraction is a reasonable improvement for testability. The code is straightforward. Every change in the diff serves the stated goal. No drive-by refactors, no scope creep, no formatting churn. 9 files changed, +203/-56 — proportional to the work. This is a focused, correct security hardening PR that closes the last unvalidated source-slug-to-path construction site. The included tests cover the key scenarios (traversal rejection, valid slug acceptance, null returns for missing/expired caches, separated RPC diagnostics, test isolation). The approach is the right one, and the execution is clean. LGTM, looks ready to ship. ✅ 中文说明总结回顾独立方案:我会做同样的改动——路径构造前加 slug 验证、脱敏诊断路径、分离错误日志、添加 debug 日志、修复测试隔离。PR 在每一点上都匹配,模块提取是对可测试性的合理改进。 代码简洁直接。 diff 中的每个改动都服务于声明的目标。没有顺手重构,没有范围蔓延,没有格式化噪音。9 个文件变更,+203/-56——与工作量成正比。 这是一个聚焦、正确的安全加固 PR,闭合了最后一个未经验证的 source-slug-to-path 构造点。包含的测试覆盖了关键场景(路径遍历拒绝、有效 slug 接受、缺失/过期 cache 返回 null、分离的 RPC 诊断、测试隔离)。方案正确,执行干净。 可以合并 ✅ — Qwen Code · qwen3.7-max |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
LGTM, looks ready to ship. ✅
✅ Maintainer local verification — PASS (recommend merge)I built and ran this PR locally on a clean worktree at the PR head ( Environment: macOS (Darwin 25.5.0), Bun 1.3.14, Node v22.22.2 · isolated worktree, 1. Focused tests — reproduce the PR's results
2. Adversarial mutation testing (the part that matters)A passing test proves nothing unless it fails with the fix removed — so I reverted each fix and confirmed the regression is real: (A) The path-traversal guard is load-bearing. Removing With the PR's guard in place, both cases return (B) The test-isolation fix is real. Re-adding the removed Removing the unused mock (as this PR does) makes the pair order-independent again. ✅ 3. Typecheck & build
I confirmed every one of the 77 4. Reviewer-side audit (no blockers)
5. CI / merge note
VerdictBehavior matches every claim in the PR description; both fixes are proven load-bearing by mutation; types/build are clean for the changed packages; the only 🇨🇳 中文版(完整对应)✅ 维护者本地验证 —— 通过(建议合并)我在 PR head( 环境: macOS(Darwin 25.5.0)、Bun 1.3.14、Node v22.22.2 · 独立 worktree、 1. 聚焦测试 —— 复现 PR 结果
2. 对抗性变异测试(最关键的部分)测试通过本身不说明问题,除非把修复删掉后测试会失败——所以我逐个回退修复,确认回归是真实存在的: (A) 路径穿越 guard 是有效负载。 把 带上 PR 的 guard 后,两种情况都返回 (B) 测试隔离修复是真实的。 把被删掉的 移除这个没用到的 mock(正是本 PR 的做法)让这组测试重新与运行顺序无关。✅ 3. 类型检查与构建
我确认了这 77 个 4. 审查者视角审计(无阻塞项)
5. CI / 合并说明
结论行为与 PR 描述的每一条声明一致;两处修复都经变异测试证明是有效负载;改动包的类型/构建干净;唯一的 |
This PR hardens the remaining desktop source slug-to-path edges from #5909 without relaxing the source slug guard introduced by #5829 or duplicating the validation/RPC behavior normalization in #5911.
What this PR does
../sessionscannot be joined intosources/../sessions/.credential-cache.json.nullinstead of surfacing a hard tool failure.filefields for invalid source slugs by using placeholders such assources/<invalid>/config.jsonandsources/<invalid>/permissions.jsoninstead of echoing unsafe slug-derived paths.Why it's needed
This is a follow-up to #5829 and #5911. #5829 added the core path traversal guard for source deletion by rejecting path-like source slugs before filesystem path construction. #5911 normalizes several validation and RPC boundary behaviors around invalid or legacy source slugs.
A few remaining defensive edges still needed cleanup. The session MCP credential cache helper directly joined
sourceSluginto a filesystem path, making it the last reviewed source-slug-to-path construction site that did not explicitly run the slug guard first. Some diagnostics also made invalid slug failures harder to interpret: validation results could display paths derived from the unsafe slug itself, the raw permissions loader silently swallowed failures, and the source permissions RPC handler logged invalid slug assertions as generic permissions file read errors.This PR keeps the same strict slug regex and focuses on the remaining API boundaries: source slugs are validated before credential cache path construction, invalid slug diagnostics are classified as caller input issues rather than disk I/O, unsafe display paths are redacted, and the local test pair that previously depended on run order is deterministic again.
Possible call chain / impact
The affected paths are session MCP credential cache reads, source validation diagnostics, source permissions RPC diagnostics, raw permissions loading, and local test isolation:
Before this PR, the credential cache path was constructed directly from
sourceSlug. A path-like slug such as../sessionscould resolve towardsources/../sessions/.credential-cache.jsonbefore the read failed or returned data. This PR validates the slug before joining the cache path; invalid cache slugs still returnnullthrough the credential manager, preserving the existing missing-credential behavior.Before this PR, invalid input such as
../sessionscould be reflected in the validation result assources/../sessions/config.json. This PR keeps the validation error message but uses a sanitized display path so validation output does not look like a resolved workspace path.Before this PR, an invalid slug assertion in this path was logged as
Error reading permissions config, which points maintainers toward filesystem or JSON problems. This PR logs invalid slug failures with a warning specific to source slug validation while preserving the existingnullreturn contract.Before this PR, running the renew-endpoint credential tests before the storage source slug tests in one Bun process could leave the storage module mocked, causing the later storage test to list no real sources. This PR removes the unused storage mock so the focused pair is order-independent.
This PR only changes these remaining hardening and diagnostics paths. It does not relax the source slug regex, change source creation or deletion behavior, alter source config parsing, change guide parsing, modify source connection tests, or duplicate the
config_validate,source_test,validateAllSources, and source delete RPC behavior changes handled in #5911.Reviewer Test Plan
How to verify
Confirm that:
sourceSlug: "../sessions"returnsnulland does not readsessions/.credential-cache.jsonoutside the source directory.sources/<invalid>/config.jsonorsources/<invalid>/permissions.jsonwhile preserving the underlyingInvalid source slugerror message.nullfor invalid slugs and logInvalid source slug for permissions:as a warning instead of logging a generic permissions file read error.credential-manager-renew.test.tsbeforestorage-source-slug.test.tsno longer leaves source storage mocked for the second file.Commands run:
cd packages/desktop && bun test packages/session-mcp-server/src/credential-cache.test.ts packages/shared/src/config/__tests__/source-slug-validation.test.ts packages/shared/src/agent/__tests__/permissions-config-source-slug.test.ts packages/server-core/src/handlers/rpc/sources.test.ts-> 8 pass, 0 failcd packages/desktop && bun test packages/shared/src/sources/__tests__/credential-manager-renew.test.ts packages/shared/src/sources/__tests__/storage-source-slug.test.ts-> 14 pass, 0 failcd packages/desktop/packages/shared && bun run tsc --noEmit-> passcd packages/desktop/packages/server-core && bun run tsc --noEmit-> passcd packages/desktop/packages/session-mcp-server && bun run build-> passcd packages/desktop/packages/session-mcp-server && bun run tsc --noEmit-> fails in existing../session-tools-core/src/**.tsimport extension configuration errors; no errors are reported for this PR's changed session MCP files after the new local imports were switched to extensionless formUbuntu 24.04.2 LTS,Linux 6.8.0-53-generic,Node v22.23.1,Bun 1.3.14):cd packages/desktop && ELECTRON_SKIP_BINARY_DOWNLOAD=1 bun install-> passcd packages/desktop && bun test packages/session-mcp-server/src/credential-cache.test.ts packages/shared/src/config/__tests__/source-slug-validation.test.ts packages/shared/src/agent/__tests__/permissions-config-source-slug.test.ts packages/server-core/src/handlers/rpc/sources.test.ts-> 8 pass, 0 failcd packages/desktop && bun test packages/shared/src/sources/__tests__/credential-manager-renew.test.ts packages/shared/src/sources/__tests__/storage-source-slug.test.ts-> 14 pass, 0 failcd packages/desktop/packages/shared && bun run tsc --noEmit-> passcd packages/desktop/packages/server-core && bun run tsc --noEmit-> passcd packages/desktop/packages/session-mcp-server && bun run build-> passcd packages/desktop/packages/session-mcp-server && bun run tsc --noEmit-> reproduces the existing../session-tools-core/src/**.tsimport extension configuration errors only; no changed session MCP files are reportedEvidence (Before & After)
No UI evidence; this is non-UI path handling, diagnostics, and test isolation behavior covered by focused regression tests.
Tested on
Environment (optional)
Windows PowerShell, Bun 1.3.12, Node v22.22.3. JD-Cloud Ubuntu 24.04.2 LTS (
Linux 6.8.0-53-generic), Bun 1.3.14, Node v22.23.1.Risk & Scope
nullbefore path construction, which is intentionally the same observable result as a missing or unusable credential cache.Linked Issues
Closes #5909
中文说明
本 PR 加固 #5909 中剩余的桌面端 source slug 到文件系统路径的边界,同时不放宽 #5829 引入的 source slug guard,也不重复 #5911 已处理的 validation / RPC 行为归一化。
What this PR does
../sessions这类 path-like 输入不能被拼成sources/../sessions/.credential-cache.json。null,不会变成硬失败。file字段做脱敏,使用sources/<invalid>/config.json和sources/<invalid>/permissions.json这类占位路径,而不是回显由不安全 slug 拼出来的路径。Why it's needed
这是 #5829 和 #5911 的后续。#5829 已经通过在文件系统路径构造前拒绝 path-like source slug,修复了 source deletion 的核心路径穿越防护。#5911 则统一了若干 invalid / legacy source slug 在 validation 和 RPC 边界上的行为。
剩余的一些防御性边界仍需要收尾。session MCP credential cache helper 会直接把
sourceSlug拼进文件系统路径,它是本轮 review 中剩余的、没有显式先走 slug guard 的 source-slug-to-path 构造点。部分诊断也会让 invalid slug 失败更难排查:validation result 可能展示由不安全 slug 拼出来的路径,raw permissions loader 会静默吞掉失败,source permissions RPC handler 会把 invalid slug assertion 记录成泛化的 permissions file read error。本 PR 保留相同的严格 slug regex,并只聚焦剩余 API 边界:credential cache 路径构造前先验证 source slug,invalid slug 诊断被归类为调用方输入问题而不是磁盘 I/O,unsafe display path 被占位符替代,本地测试组合也不再依赖运行顺序。
Possible call chain / impact
受影响路径是 session MCP credential cache read、source validation diagnostics、source permissions RPC diagnostics、raw permissions loading 以及本地测试隔离:
在本 PR 之前,credential cache path 直接由
sourceSlug构造。../sessions这类 path-like slug 可能在读取失败或返回数据前,先被解析到sources/../sessions/.credential-cache.json。本 PR 在 join cache path 前验证 slug;无效 cache slug 仍然通过 credential manager 返回null,保持原有 missing credential 语义。在本 PR 之前,
../sessions这类 invalid input 可能在 validation result 里显示为sources/../sessions/config.json。本 PR 保留 validation error message,但使用脱敏 display path,避免验证输出看起来像一个被解析过的 workspace path。在本 PR 之前,这条路径里的 invalid slug assertion 会被记录成
Error reading permissions config,容易让维护者误判成文件系统或 JSON 问题。本 PR 对 invalid slug 使用专门 warning,同时保留原有null返回契约。在本 PR 之前,如果 renew-endpoint credential tests 先于 storage source slug tests 在同一个 Bun 进程里运行,storage module mock 可能残留,导致后一个 storage test 列不出真实 source。本 PR 移除未使用的 storage mock,让这个聚焦测试组合不再依赖运行顺序。
本 PR 只改变这些剩余 hardening 和 diagnostics 路径。它不会放宽 source slug regex,不会改变 source 创建或删除行为,不会改变 source config parsing,不会改变 guide parsing,不会修改 source connection tests,也不重复 #5911 中已处理的
config_validate、source_test、validateAllSources和 source delete RPC 行为变更。Reviewer Test Plan
How to verify
确认:
sourceSlug: "../sessions"返回null,并且不会读取 source 目录外的sessions/.credential-cache.json。sources/<invalid>/config.json或sources/<invalid>/permissions.json,同时保留底层Invalid source slug错误信息。null,并以Invalid source slug for permissions:记录 warning,而不是泛化 permissions file read error。credential-manager-renew.test.ts在storage-source-slug.test.ts前运行时,不再把 source storage mock 泄漏给第二个测试文件。已运行命令:
cd packages/desktop && bun test packages/session-mcp-server/src/credential-cache.test.ts packages/shared/src/config/__tests__/source-slug-validation.test.ts packages/shared/src/agent/__tests__/permissions-config-source-slug.test.ts packages/server-core/src/handlers/rpc/sources.test.ts-> 8 pass, 0 failcd packages/desktop && bun test packages/shared/src/sources/__tests__/credential-manager-renew.test.ts packages/shared/src/sources/__tests__/storage-source-slug.test.ts-> 14 pass, 0 failcd packages/desktop/packages/shared && bun run tsc --noEmit-> passcd packages/desktop/packages/server-core && bun run tsc --noEmit-> passcd packages/desktop/packages/session-mcp-server && bun run build-> passcd packages/desktop/packages/session-mcp-server && bun run tsc --noEmit-> 因既有../session-tools-core/src/**.tsimport extension 配置错误失败;新增 local import 改成 extensionless 之后,本 PR 改动的 session MCP 文件没有报错Ubuntu 24.04.2 LTS、Linux 6.8.0-53-generic、Node v22.23.1、Bun 1.3.14):cd packages/desktop && ELECTRON_SKIP_BINARY_DOWNLOAD=1 bun install-> passcd packages/desktop && bun test packages/session-mcp-server/src/credential-cache.test.ts packages/shared/src/config/__tests__/source-slug-validation.test.ts packages/shared/src/agent/__tests__/permissions-config-source-slug.test.ts packages/server-core/src/handlers/rpc/sources.test.ts-> 8 pass, 0 failcd packages/desktop && bun test packages/shared/src/sources/__tests__/credential-manager-renew.test.ts packages/shared/src/sources/__tests__/storage-source-slug.test.ts-> 14 pass, 0 failcd packages/desktop/packages/shared && bun run tsc --noEmit-> passcd packages/desktop/packages/server-core && bun run tsc --noEmit-> passcd packages/desktop/packages/session-mcp-server && bun run build-> passcd packages/desktop/packages/session-mcp-server && bun run tsc --noEmit-> 仅复现既有../session-tools-core/src/**.tsimport extension 配置错误;未报告本 PR 改动的 session MCP 文件错误Evidence (Before & After)
无 UI 证据;这是非 UI 的 path handling、diagnostics 和 test isolation 行为,已由聚焦回归测试覆盖。
Tested on
Environment (optional)
Windows PowerShell, Bun 1.3.12, Node v22.22.3. JD-Cloud Ubuntu 24.04.2 LTS(
Linux 6.8.0-53-generic)、Bun 1.3.14、Node v22.23.1。Risk & Scope
null,这与 missing / unusable credential cache 的可观察结果一致,是有意保持的行为。Linked Issues
Closes #5909