fix(desktop): enforce session plans path boundary - #5507
Conversation
991b1e2 to
7d9a0c6
Compare
|
@qwen-code /triage |
|
Thanks for the PR! Template looks good ✓ — all required sections present, bilingual, linked to #5506. On direction: straightforward path-traversal fix in the desktop session plans handler. On approach: the scope is minimal and focused — 4 files, +159/-2 (bulk is tests). Both changes are necessary and correct. However, the maintainer has flagged that this PR and #5513 (workspace image path boundary) are the same class of fix and should be consolidated. Specifically:
The individual fix is sound, but merging it standalone would cement a pattern that #5513 then duplicates. These should land together. Flagging for discussion before this can proceed. 中文说明感谢贡献! 模板完整 ✓ — 所有必填部分齐全,双语,关联了 #5506。 方向:直接的路径穿越修复。 方案:范围最小化且聚焦。但维护者指出本 PR 与 #5513(workspace 图片路径边界修复)属于同类修复,应当合并。具体来说:
单独修复本身是正确的,但独立合并会让 #5513 被迫重复同样的逻辑。这些应当一起落地。 先提出来讨论,再深入。 — Qwen Code · qwen3.7-max |
Code ReviewIndependent proposal: To fix the The PR matches this exactly. Two production changes, both correct:
No correctness issues, no security gaps in the implementation itself. DRY concern (from maintainer): PR #5513 ( TestsThree new tests for
Three new tests for
Test quality is solid: real temp directories, proper cleanup, assertions verify absence of side effects on rejected paths. CI Results (all green)Real-Scenario TestingN/A — this is an internal path-security fix in the desktop package's 中文说明代码审查独立方案: 在 PR 与方案完全一致。 两处改动均正确。无正确性问题、无安全缺口。 DRY 问题(来自维护者): PR #5513 在 测试
CI 结果(全绿)全部通过。 真实场景测试不适用 — desktop 包内部路径安全修复,无 CLI/TUI 行为可验证。 — Qwen Code · qwen3.7-max |
|
The fix itself is clean and correct — But the maintainer's consolidation feedback stands, and it hasn't been addressed:
The right move is to consolidate #5507 and #5513 into one PR that establishes a single shared path-boundary helper used by both handlers. The fix quality here is genuinely good — it just needs to land alongside its sibling so the pattern doesn't fragment. Not approving. Waiting for the consolidation to happen. 中文说明修复本身干净正确 — 如果只有这一个 PR,我会毫不犹豫地批准。 但维护者的合并建议尚未被处理:
正确做法是将 #5507 和 #5513 合并为一个 PR,建立一个共享的路径边界 helper,两个 handler 共用。修复质量确实好 — 但需要与兄弟 PR 一起落地,避免模式碎片化。 不批准。 等待合并处理。 — Qwen Code · qwen3.7-max |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
LGTM — clean security fix, reuses proven path-security infrastructure, well-tested, CI green on all platforms. Ready to ship. ✅
✅ Maintainer verification — security fix confirmed, safe to mergeBuilt and ran the real test suite locally (under tmux) against the PR head Environment
Results — full PR test plan reproduced
A/B proof — both halves of the fix independently guard the bug① Handler guard ( Without the guard, the handler reads & submits both the sibling-prefix file and the symlink-escape file. Restoring → ② Shared helper (
Security assessment
CIAll substantive checks are green (Classify, CodeQL, Lint, Test on ubuntu/macos/windows · Node 22.x), and 🇨🇳 中文版(点击展开)✅ 维护者验证 — 安全修复已确认,可以安全合并已在本地(tmux 下)针对 PR head 环境
结果 — 完整复现 PR test plan
A/B 验证 — 两半修复各自都能拦住 bug① Handler 守卫( ② Shared helper( 安全评估
CI所有实质性检查均已通过(Classify、CodeQL、Lint、ubuntu/macos/windows · Node 22.x 测试),且 |
|
📌 Same maintainer note — full version on #5509. Part of the desktop batch (#5507 / #5511 / #5513) and a path-boundary fix (same class as #5513). Please fold the path-boundary work into one PR and reuse the existing helper — |
|
@qwen-code /triage |
Dismissing prior approval — maintainer consolidation feedback (DRY with #5513) is unaddressed.
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
The fix itself is clean and correct — isPathWithinDirectory from path-security.ts is the right utility, isPathInPlansDir's relative()-based check is the right lexical replacement, tests cover the attack surface well, CI is green across all platforms. If this were the only PR, I'd approve without hesitation.
But the maintainer's consolidation feedback stands, and it hasn't been addressed:
- #5513 creates a duplicate
isPathWithinDirectoryin a different package. Same vulnerability class (startsWithbypass), same solution pattern, shipped as a separate PR. - Merging #5507 alone means accepting that the path-boundary utility lives in two places — the very DRY problem the maintainer called out.
The right move is to consolidate #5507 and #5513 into one PR that establishes a single shared path-boundary helper used by both handlers. The fix quality here is genuinely good — it just needs to land alongside its sibling so the pattern doesn't fragment.
Not approving. Waiting for the consolidation to happen.
中文说明
修复本身干净正确 — 如果只有这一个 PR,我会毫不犹豫地批准。
但维护者的合并建议尚未被处理:
- #5513 在不同包中创建了重复的
isPathWithinDirectory,同一类漏洞、同一类方案,却作为独立 PR 提交。 - 单独合并 #5507 意味着接受路径边界工具函数分散在两处 — 正是维护者指出的 DRY 问题。
正确做法是将 #5507 和 #5513 合并为一个 PR,建立一个共享的路径边界 helper,两个 handler 共用。修复质量确实好 — 但需要与兄弟 PR 一起落地,避免模式碎片化。
不批准。 等待合并处理。
— Qwen Code · qwen3.7-max
|
consolidated this into #5545 so the desktop path-boundary fixes share one helper. closing this one to avoid duplicate review. |
What this PR does
SubmitPlanpaths unless the submitted file is inside the current session's plans directory after path and symlink resolution.isPathInPlansDirhelper to use a real path-boundary check instead of a raw string prefix check.SubmitPlanhandler and the shared helper.Why it's needed
The old checks could treat a sibling path as being inside the plans directory if it shared the same string prefix. For example,
/tmp/workspace/sessions/session-123/plans-other/plan.mdstarts with/tmp/workspace/sessions/session-123/plans, but it is not actually inside the plans directory.SubmitPlanshould not read or submit that file.Reviewer Test Plan
How to verify
handleSubmitPlan: it should reject paths outsidectx.plansFolderPathbefore checking file existence or reading file contents, including symlink escapes from inside the plans directory.isPathInPlansDir: it should allow the plans directory itself and real children, but reject sibling-prefix paths such asplans-other.bun test ./src/handlers/submit-plan.test.tsfrompackages/desktop/packages/session-tools-core.bun test ./src/agent/__tests__/session-scoped-tools-path-boundary.test.tsfrompackages/desktop/packages/shared.bun run typecheckfrompackages/desktop/packages/session-tools-core.bun run typecheck:sharedfrompackages/desktop.npx prettier --check packages/desktop/packages/session-tools-core/src/handlers/submit-plan.ts packages/desktop/packages/session-tools-core/src/handlers/submit-plan.test.ts packages/desktop/packages/shared/src/agent/session-scoped-tools.ts packages/desktop/packages/shared/src/agent/__tests__/session-scoped-tools-path-boundary.test.ts.Evidence (Before & After)
Before: a sibling path like
plans-other/plan.mdcould pass the raw prefix check and be read/submitted as a plan path. After:SubmitPlanuses the existing symlink-aware path-security helper, andisPathInPlansDiruses a path-relative boundary check, so sibling prefixes are rejected and symlink escapes are not read.Tested on
Environment (optional)
Local Bun desktop workspace after
bun install --cwd packages/desktop --frozen-lockfile.Risk & Scope
SubmitPlannow requires the plan file path to be inside the session plans folder before it will read the file, including after symlink resolution.Linked Issues
Fixes #5506
中文说明
What this PR does
SubmitPlan路径。isPathInPlansDirhelper 从裸字符串前缀判断改成真实路径边界判断。SubmitPlanhandler 和 shared helper 增加回归测试。Why it's needed
旧检查会把共享字符串前缀的兄弟路径误认为 plans 目录内部路径。例如
/tmp/workspace/sessions/session-123/plans-other/plan.md以/tmp/workspace/sessions/session-123/plans开头,但它并不在 plans 目录内。SubmitPlan不应该读取或提交这个文件。Reviewer Test Plan
How to verify
handleSubmitPlan:它应该在检查文件是否存在或读取文件内容之前,拒绝ctx.plansFolderPath外部的路径,包括从 plans 目录内部软链逃逸到外部的路径。isPathInPlansDir:它应该允许 plans 目录本身和真实子路径,但拒绝plans-other这样的兄弟前缀路径。packages/desktop/packages/session-tools-core下运行bun test ./src/handlers/submit-plan.test.ts。packages/desktop/packages/shared下运行bun test ./src/agent/__tests__/session-scoped-tools-path-boundary.test.ts。packages/desktop/packages/session-tools-core下运行bun run typecheck。packages/desktop下运行bun run typecheck:shared。Evidence (Before & After)
修复前:
plans-other/plan.md这类兄弟路径可能通过裸前缀检查,并被读取/提交为 plan path。修复后:SubmitPlan复用现有的 symlink-aware path-security helper,isPathInPlansDir使用 path-relative 边界检查,因此兄弟前缀会被拒绝,软链逃逸也不会被读取。Tested on
Environment (optional)
本地 Bun desktop workspace,先运行过
bun install --cwd packages/desktop --frozen-lockfile。Risk & Scope
SubmitPlan现在会要求 plan 文件路径在软链解析后仍必须位于 session plans folder 内,才会读取文件。Linked Issues
Fixes #5506
AI Assistance Disclosure
I used Codex to review the changes, sanity-check the implementation against existing patterns, and help spot potential edge cases.