Skip to content

fix(chat): securely support external Windows history paths#270

Open
End0rph1nww wants to merge 2 commits into
RachelForster:mainfrom
End0rph1nww:fix/windows-external-chat-history-paths
Open

fix(chat): securely support external Windows history paths#270
End0rph1nww wants to merge 2 commits into
RachelForster:mainfrom
End0rph1nww:fix/windows-external-chat-history-paths

Conversation

@End0rph1nww

@End0rph1nww End0rph1nww commented Jul 24, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • add a dedicated chat-history path policy that permits explicit absolute paths outside the selected project root, including other drives, UNC shares, and supported Windows verbatim paths
  • keep relative history paths project-managed while rejecting traversal, drive-relative, root-relative, and device namespace paths
  • normalize Windows paths only for containment comparisons and preserve the native \\?\ spelling for long-path I/O
  • make external history download file-bound and short-lived, and limit history cleanup to files owned by chat storage
  • cover external launch, resume, copy, open, clear, cross-drive, UNC, traversal, and extended-length path behavior

Root cause

The desktop project-root selector canonicalizes the selected directory. On Windows this can produce a \\?\ path, while other callers may still use the regular drive spelling. ntpath.commonpath treats those spellings as different drives.

Chat history also reused the generic project-contained path resolver, even though explicit external chat-history locations are supported. As a result, both same-drive verbatim/plain spelling mismatches and legitimate cross-drive histories could fail with Paths don't have the same drive.

User impact

Users can launch and resume chats whose histories live outside the selected data directory without weakening the containment rules for general project assets and downloads. Existing extended-length paths retain long-path compatibility.

Validation

  • 129 focused Python tests passed with the bundled runtime
  • Python compilation checks passed for all changed runtime modules
  • a real C: project plus D: external-history I/O smoke test passed
  • Tauri resource preparation and Windows x64 resource verification passed
  • repository pre-push checks passed with the bundled project Python

Summary by Ameath

小爱先把本次核对的重点整理出来,方便快速查看。

本 PR 将聊天历史与通用项目内路径策略拆分,支持 Windows 外部盘符、UNC 和受支持的 verbatim 路径,并调整下载、清理和空历史恢复;审查发现失败清理会丢失恢复临时文件,且 UNC 路径可绕过目录归属校验后参与删除。

New Features

  • 支持在启动和恢复聊天时使用项目目录外的聊天历史。

Enhancements

  • 统一普通与 verbatim Windows 路径的包含关系比较,同时保留原生 I/O 路径表示。
  • 历史下载改为短期能力 URL,并将清理限制为保留名称的聊天存储文件。
  • 用空的活动历史快照防止陈旧分支元数据恢复已清空的对话。

Tests

  • 新增 Windows 定向覆盖,包括跨盘、UNC、扩展长度路径、下载能力和清理场景。
Original summary in English

This PR separates chat-history handling from the generic project-contained path policy, adds support for external Windows drives, UNC paths, and supported verbatim paths, and changes download, cleanup, and empty-history recovery. The review found that a failed clear can discard recovery data, and that UNC paths can bypass directory-ownership validation before deletion.

New Features

  • Supports project-external chat histories when launching and resuming chats.

Enhancements

  • Compares regular and verbatim Windows path spellings consistently while preserving native I/O spelling.
  • Moves history downloads to short-lived capability URLs and limits cleanup to reserved chat-storage filenames.
  • Uses an empty active-history snapshot to prevent stale branch metadata from restoring a cleared conversation.

Tests

  • Adds Windows-focused coverage for cross-drive, UNC, extended-length paths, download capabilities, and cleanup.

@End0rph1nww
End0rph1nww marked this pull request as ready for review July 24, 2026 09:28
@ameath-review

ameath-review Bot commented Jul 24, 2026

Copy link
Copy Markdown

Reviewer's Guide by Ameath

小爱把阅读路径顺了一下,可以从这里开始查看。

核查覆盖 history_paths.py 到启动、恢复和清理调用链,以及 HistoryManager.tmp 恢复和新的历史下载端点。两个行级问题都位于本 PR 新增的外部历史/可恢复清理路径:空快照提交失败时会先删除增量恢复数据;UNC 路径则未验证目录归属便可进入破坏性清理。

文件级变更

Change Details Files
新增文件 2 个文件,+490 / -0 frontend_bridge_core/history_paths.py
test/unit/frontend_bridge_core/test_history_path_policy.py
修改文件(1/2) 10 个文件,+252 / -64 core/sprite/chat_branch_storage.py
core/sprite/chat_ui_service.py
frontend_bridge.py
frontend_bridge_core/chat.py
frontend_bridge_core/handler.py
frontend_bridge_core/path_utils.py
frontend_bridge_core/security.py
frontend_bridge_core/state.py
llm/history_manager.py
main.py
修改文件(2/2) 4 个文件,+195 / -4 test/unit/core/test_chat_branch_storage.py
test/unit/core/test_chat_history.py
test/unit/core/test_chat_initial_sprite.py
test/unit/frontend_bridge_core/test_chat_runtime_mode.py

关联 Issue

  • #269 Clarify chat history import and template generation flow:标题语义候选,需审查者确认关联。

Tips and commands
  • 重新审查: @Ame4th review
  • 重新生成摘要: @Ame4th summary
  • 重新生成指南: @Ame4th guide
  • 生成标题: @Ame4th title
  • 解决本机器人线程: @Ame4th resolve
  • 清理本机器人审查: @Ame4th dismiss
  • 从行级评论创建 Issue: 回复 @Ame4th issue
  • 继续讨论: 直接回复任一行级评论。
  • 反馈偏好: 对行级评论点 👍 或 👎。
Original review guide in English

The review traced history_paths.py through launch, resume, and clear callers, plus HistoryManager’s .tmp recovery and the new history-download endpoint. Both inline issues are in the PR’s new external-history/recoverable-clear paths: incremental recovery data is deleted before an empty snapshot is committed, and UNC paths reach destructive cleanup without directory-ownership validation.

@ameath-review ameath-review Bot changed the title fix: support external Windows chat history paths fix(chat): support external Windows history paths securely Jul 24, 2026

@ameath-review ameath-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

小爱把需要留意的位置放在这里,方便逐处确认。

小爱已完成全部变更审查,共发布 2 条行级建议(高风险 0,中风险 1)。

合并前关注点(风险 1 · 建议测试 1)

风险

  • 唯一关联的 #269 在 PR 元数据中标记为 possible,其内容聚焦聊天 UI/文档困惑,并明确称跨盘路径故障尚未确认;目前没有与本次外部 Windows 聊天历史路径变更直接对应的已关联 issue。

建议测试

  • 在 Windows 上补充 \\?\UNC\... 历史路径经 _chat_history_path_launch_chat--history 参数到 main.py 加载/保存的端到端往返测试;现有长路径用例只直接读写 Path,跨盘启动用例替换了 _launch_chat
Prompt for AI Agents

请逐项处理以下审查问题;修改前先核实上下文,修改后运行相关测试。

1. 原生清理可能恢复旧分支历史 / Native clear can resurrect branch history

core/sprite/chat_ui_service.py:129 (RIGHT)
<issue_to_address>此处在 clear_chat_history() 已删除 active.json 后才进行尽力而为的清理。remove_chat_history_storage() 会吞掉 branches.json 的删除错误;若该文件被 Windows 上的其他进程锁定,或进程在两步之间退出,旧分支树会保留。原生模式退出时只写入空的 active.json,下次启动 reconcile_active_branch_state() 会把空 active 当作缺失,并从残留分支树恢复旧的 messages/history,使已清空的聊天重新出现。请持久化可恢复的空分支状态(或清除标记),且不要将元数据删除失败报告为清理成功。

English: active.json is removed before this best-effort cleanup. If deleting branches.json fails or the process exits between the two steps, native shutdown writes only an empty active file; recovery then restores the stale branch payload, so cleared history reappears.</issue_to_address>

2. 离线 UNC 验证仍会阻塞请求 / Offline UNC validation still blocks requests

frontend_bridge_core/history_paths.py:79 (RIGHT)
<issue_to_address>注释说明这里应保持词法处理以避免离线 UNC 共享阻塞,但 Path.exists() 已经会访问该共享;后续的 is_file()is_dir()iterdir() 也一样。用户保存的 UNC 历史在共享断开后,启动、恢复或无实时会话时的快照轮询都可能等待 Windows 网络超时,而不是及时返回“不可访问”。请对 UNC 目标使用不触发 I/O 的验证,或将有界的可访问性检查移出同步请求路径。

English: Despite the lexical-path comment, exists() and the following checks access the UNC share. A disconnected saved share can block launch, resume, or snapshot polling until the Windows network timeout.</issue_to_address>

Comment thread core/sprite/chat_ui_service.py Outdated
)
)
if history_file:
remove_chat_history_storage(history_file)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[MEDIUM · correctness] 原生清理可能恢复旧分支历史 / Native clear can resurrect branch history

此处在 clear_chat_history() 已删除 active.json 后才进行尽力而为的清理。remove_chat_history_storage() 会吞掉 branches.json 的删除错误;若该文件被 Windows 上的其他进程锁定,或进程在两步之间退出,旧分支树会保留。原生模式退出时只写入空的 active.json,下次启动 reconcile_active_branch_state() 会把空 active 当作缺失,并从残留分支树恢复旧的 messages/history,使已清空的聊天重新出现。请持久化可恢复的空分支状态(或清除标记),且不要将元数据删除失败报告为清理成功。

English: active.json is removed before this best-effort cleanup. If deleting branches.json fails or the process exits between the two steps, native shutdown writes only an empty active file; recovery then restores the stale branch payload, so cleared history reappears.

置信度 96% · 可直接回复讨论 · 👍/👎 会影响后续审查

def _validate_history_storage_target(path: Path) -> Path:
"""Reject an existing unrelated file/directory as a history storage root."""

if not path.exists():

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[LOW · performance] 离线 UNC 验证仍会阻塞请求 / Offline UNC validation still blocks requests

注释说明这里应保持词法处理以避免离线 UNC 共享阻塞,但 Path.exists() 已经会访问该共享;后续的 is_file()is_dir()iterdir() 也一样。用户保存的 UNC 历史在共享断开后,启动、恢复或无实时会话时的快照轮询都可能等待 Windows 网络超时,而不是及时返回“不可访问”。请对 UNC 目标使用不触发 I/O 的验证,或将有界的可访问性检查移出同步请求路径。

Original in English

Despite the lexical-path comment, exists() and the following checks access the UNC share. A disconnected saved share can block launch, resume, or snapshot polling until the Windows network timeout.

置信度 93% · 可直接回复讨论 · 👍/👎 会影响后续审查

@RachelForster RachelForster added p0 p1 and removed p1 labels Jul 24, 2026
@End0rph1nww

Copy link
Copy Markdown
Collaborator Author

@Ame4th review

@ameath-review ameath-review Bot changed the title fix(chat): support external Windows history paths securely fix(chat): securely support external Windows history paths Jul 24, 2026

@ameath-review ameath-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

小爱沿着实现细节逐处看过,下面是需要留意的地方。

小爱已完成全部变更审查,共发布 2 条行级建议(高风险 0,中风险 2)。

合并前关注点(风险 1 · 建议测试 0)

风险

  • 贡献约定要求 PR 描述链接一个现有 issue;当前描述未包含链接,元数据仅将 #269 标为可能相关。
Prompt for AI Agents

请逐项处理以下审查问题;修改前先核实上下文,修改后运行相关测试。

1. 保留失败清理的恢复临时文件 / Preserve recovery data on a failed clear

llm/history_manager.py:176 (RIGHT)
<issue_to_address>delete_tmp() 在空活动快照写入成功前执行。若 write_text("[]") 因权限或 Windows 文件占用失败,清理会报错且旧 active.json 仍在,但增量恢复用的 .tmp 已被删除。LLMManager 会把新增消息追加到该文件,因此最后尚未合并的对话无法恢复。应在新的空状态可持久恢复后才丢弃 .tmp,并为提交失败保留恢复路径。

delete_tmp() runs before the empty active snapshot is committed. If write_text("[]") fails because of permissions or a Windows sharing violation, the clear reports an error with the old active.json still present but the incremental recovery .tmp already deleted. LLMManager appends new messages there, so the latest unmerged turns cannot be recovered. Retain the recovery file until the new empty state is durably recoverable, with a failure-recovery path.</issue_to_address>

2. 在删除前验证 UNC 历史目录归属 / Validate UNC history ownership before deletion

frontend_bridge_core/history_paths.py:93 (RIGHT)
<issue_to_address>UNC 路径在此直接返回,因而完全跳过后续目录归属检查。用户在启动时提供 \\server\share\folder(甚至共享根)后,resetHistory 会调用 remove_chat_history_storage(),后续清理也会调用它,并删除该目录中仅因名称匹配的 active.jsonactive.json.tmpbranches.json。这些文件可能属于其他应用,而外部 UNC 正是本 PR 新增支持的范围。应将离线 UNC 的可启动判定与执行破坏性操作前的所有权验证分开。

UNC paths return here directly and therefore skip all later directory-ownership checks. After a user supplies \\server\share\folder (even a share root), resetHistory and later clearing call remove_chat_history_storage(), which deletes name-matched active.json, active.json.tmp, and branches.json from that directory. Those files may belong to another application, and external UNC storage is newly supported by this PR. Separate the offline-UNC launch decision from ownership validation before destructive operations.</issue_to_address>

Comment thread llm/history_manager.py
Comment thread frontend_bridge_core/history_paths.py
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants