Skip to content

fix(vault): 保存凭据跳过未变化的 keychain 条目,修复切换供应商弹窗风暴 (#602)#634

Merged
H-Chris233 merged 1 commit into
betafrom
fix/issue-602-keychain-prompt-storm
Jun 10, 2026
Merged

fix(vault): 保存凭据跳过未变化的 keychain 条目,修复切换供应商弹窗风暴 (#602)#634
H-Chris233 merged 1 commit into
betafrom
fix/issue-602-keychain-prompt-storm

Conversation

@appergb

@appergb appergb commented Jun 10, 2026

Copy link
Copy Markdown
Collaborator

User description

问题 (#602)

macOS 设置中切换供应商会连续弹出多次「OpenLess 想访问钥匙串」密码弹窗(v1.3.6-2 复现)。

根因:切换供应商只改 root.active 一个字段,但 save_credentials 每次都执行读旧 manifest → 重写所有 chunks → 重写 manifest。macOS Keychain 每个条目各自 ACL,签名变化/未点「始终允许」时每次条目访问各弹一窗 → 一次切换 3+ 个弹窗。读路径早有进程缓存(PR #394),写路径没有。

修复

基于已有的进程级凭据缓存做三处收口:

  1. manifest 进程缓存:load/save 成功后回填 CREDENTIALS_MANIFEST_CACHE,后续 save 不再回 keychain 读旧 manifest。冷缓存仍读真实 manifest——UUID 代际旧 chunks 的清理信息不丢。
  2. chunk_skip_mask(纯函数,带单测):用缓存中上次落盘的 root 反推旧 chunk 内容,逐字节一致的 chunk 跳过重写。仅旧 manifest 已是稳定名(generation=None)时启用;任何不确定(冷缓存/序列化顺序差异/UUID 代际)都退回「全部重写」旧行为,只会多写不会漏写。
  3. manifest 跳写:manifest 内容只由 chunks 数决定,数量不变且已是稳定名 → 内容逐字节一致,跳过。

效果:典型单 chunk 凭据库,切换供应商 keychain 访问 3 次 → 1 次;no-op 保存 0 次。保存时新增一行 info 日志(written/total + manifest 状态),方便用户日志回报验证。

安全性

  • partial-write 防护保持:仍是「先 chunks 后 manifest」顺序
  • 跳写只在「目标内容 == 缓存内容」时发生;save 失败不更新缓存,重试会全量重写差异项
  • 不改变存储 schema,无迁移,可直接回滚

验证

  • 新增单测 chunk_skip_mask_skips_unchanged_and_rewrites_changed(4 个场景)
  • cargo test --lib:479 passed, 0 failed

Closes #602


PR Type

Bug fix


Description

  • 添加进程级 manifest 缓存,避免保存前读取钥匙串

  • 实现 chunk_skip_mask 跳过未变化的 chunk

  • 在 chunk 数量和 manifest 稳定时跳过 manifest 写入

  • 添加相应的单元测试


Diagram Walkthrough

flowchart LR
  A["save_credentials"] --> B{"manifest cache hit & stable name?"}
  B -- "yes" --> C["chunk_skip_mask from cached root"]
  C --> D["write only changed chunks"]
  D --> E{"chunks count unchanged?"}
  E -- "yes" --> F["skip manifest write"]
  E -- "no" --> G["write new manifest"]
  B -- "no" --> H["read manifest from keychain"]
  H --> I["write all chunks"]
  I --> G
  F --> J["update caches"]
  G --> J
Loading

File Walkthrough

Relevant files
Bug fix
persistence.rs
添加 manifest 缓存和 chunk 跳过优化                                                             

openless-all/app/src-tauri/src/persistence.rs

  • 新增 CREDENTIALS_MANIFEST_CACHE 静态缓存
  • 新增 chunk_skip_mask 函数计算可跳过的 chunk
  • 修改 save_credentials 使用缓存和跳过逻辑
  • 新增 chunk_skip_mask 单元测试
+113/-11

切换供应商只改 root.active 一个字段,但 save_credentials 会先读旧 manifest、
再重写所有 chunks、再重写 manifest —— macOS 上每个 keychain 条目各自 ACL,
ad-hoc 签名/未永久授权时每次访问各弹一次「访问密码」弹窗,一次切换弹 3+ 次。

三处收口(都基于已有的进程级凭据缓存,行为冷路径不变):
1. manifest 进程缓存(CREDENTIALS_MANIFEST_CACHE):load/save 成功后回填,
   后续 save 不再回 keychain 读旧 manifest(省 1 次 ACL 访问);冷缓存仍读
   真实 manifest,UUID 代际旧 chunks 的清理信息不丢。
2. chunk_skip_mask:用缓存里上次落盘的 root 反推旧 chunk 内容,逐字节一致的
   chunk 跳过重写。仅旧 manifest 已是稳定名(generation=None)时启用;缓存
   序列化顺序差异只会多写(回退旧行为),不会漏写。
3. manifest 内容只由 chunks 数决定:数量不变且已是稳定名 → 跳过重写。

典型单 chunk 凭据库:切换供应商 3 次 keychain 访问 → 1 次。
@github-actions

Copy link
Copy Markdown

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

🎫 Ticket compliance analysis 🔶

602 - Partially compliant

Compliant requirements:

  • 新增 manifest 进程缓存,避免 save_credentials 时重复读取 keychain manifest
  • 使用 chunk_skip_mask 跳过内容未变化的 chunk 写入
  • manifest 内容不变时跳过 manifest 写入
  • 冷启动/不确定时退回全量写入,不丢失清理信息

Non-compliant requirements:

(无)

Requires further human verification:

(无)

394 - Partially compliant

Compliant requirements:

  • 保留了已有的 CREDENTIALS_CACHE 并在 store_credentials_cache 中正确更新
  • 新增的 manifest 缓存(CREDENTIALS_MANIFEST_CACHE)与现有缓存协同工作
  • 错误路径不缓存 manifest,确保下次读取仍从 keychain 重试

Non-compliant requirements:

(无)

Requires further human verification:

(无)

⏱️ Estimated effort to review: 3 🔵🔵🔵⚪⚪
🧪 PR contains tests
🔒 No security concerns identified
⚡ No major issues detected

@H-Chris233 H-Chris233 merged commit f6c0b32 into beta Jun 10, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[macos]设置中切换供应商会多次弹出访问密码的弹窗

2 participants