bug(device-link): 登出后旧账号的在途镜像写入会泄漏进新账号缓存(跨账号数据泄漏)
- Dominant language
- TypeScript
- Stars
- 2.7k
- Forks
- 395
- Avg merge
- 21h 48m
- Merged PRs (30d)
- 776
Description
## 现象
`apps/desktop/src/main/device-link/mirrorCacheStore.ts` 的 `writeMessages` 存在跨账号数据泄漏:**登出账号 A 后,A 的在途远端响应(携带清理前捕获的令牌)到达时,数据会写入新登录账号 B 的缓存目录**,B 会读到 A 的消息。
## 复现(本地稳定复现)
模拟真实生产时序(owner 切换):
```ts
let owner = 'alice';
const store = createMirrorCache(() => path.join(root, owner));
const token = (await store.readMessagesWithInvalidation('dev-1', 'sess-1')).invalidation; // T0 捕获令牌
await store.clearAll(); // T1 登出 alice
owner = 'bob'; // T2 登录 bob
await store.writeMessages('dev-1', 'sess-1', rows, token); // T3 alice 响应到达, 带旧令牌
// 结果: alice 目录文件不存在(正确), 但 bob 目录文件存在 —— alice 的数据进了 bob 的缓存
```
**bob 目录文件被创建(`bob file exists = true`),bob 读到 alice 的消息。**
## 根因
- `expectedInvalidation`(令牌)只含**会话级作废计数**(`readMessagesWithInvalidation` 返回 `numericCounter(after[0])`,会话级 key)
- `clearAll` 只 bump **账号级计数** `CLEARED_ACCOUNT`,**不碰会话级计数**
- `writeMessages` 的 `accountCounterAtStart`(`mirrorCacheStore.ts:983`)在**调用时**才采样,若调用时清理已完成,读到的是新值 → `clearedSince(CLEARED_ACCOUNT, accountCounterAtStart)` 返回 false → 防线放行
- 令牌捕获(T0)与写入调用(T3)之间发生的 `clearAll` 无法被检测:会话级计数没变(令牌匹配通过)、账号级计数采样已错过
## 影响
- **跨账号数据泄漏**:登出后切换账号,旧账号的镜像消息会写进新账号缓存,新账号离线冷启动时 hydrate 出旧账号内容
- 由 #1538 的 Codex review 发现(PR #1781),issue 归属人:@dashhuang(mirrorCacheStore 来自 #1070)
## 建议修复方向(供评估,未实施)
1. **令牌扩展**:让 `expectedInvalidation` 携带账号级计数(或让 renderer 在捕获令牌时也记录账号级计数),writeMessages 提交前比对账号级计数是否变化
2. **root 一致性校验**:writeMessages 提交前检查 `rootAtStart === resolveRoot()`,owner 已切换则丢弃(已验证该检测可行)
3. 修复需覆盖 `writeMessages` / `writeSessionList` 两条路径,并补确定性回归
## 参考
- 相关 issue:#1538(CI flake,测试侧),本 issue 为独立的产品缺陷
- 同族:#1599 / #1598 / #1606
Contributor guide
Research direction
Start in apps/desktop/src/main/device-link/mirrorCacheStore.ts by reading writeMessages, readMessagesWithInvalidation, clearAll, and the account-counter logic. Reproduce the owner-switch sequence from the issue, then inspect the writeSessionList path as well. Done means stale responses from the previous account cannot create cache files for the new account, with deterministic regression coverage for both paths.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- desktop, security
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 48/100