makecindy / makecindy/cindy

bug: 插件 webview 分区未按账号隔离,跨账号泄漏插件内存储(localStorage/IndexedDB/Cookie)

Open
#1,411 1 comment 0 reactions 0 assignees View on GitHub
bug
Dominant language
TypeScript
Stars
2.7k
Forks
395
Avg merge
21h 48m
Merged PRs (30d)
776

Description

## 现象

插件面板 / 设置页的 webview 分区名只含 ghostId,且分区随 app 生命周期常驻。
同一台机器上账号 A 与账号 B 装了**同一个插件 id** 时,两者共用同一个分区,
因此共用 `localStorage` / `IndexedDB` / `Cookie`。

从账号 A 切到账号 B 后打开该插件的面板,B 会直接看到 A 遗留的插件内登录态、
表单内容与缓存数据——跨账号数据泄漏。

## 复现

1. 账号 A 登录,安装带 `panel` 的插件 X,在面板内登录插件自己的服务(写入 localStorage/Cookie);
2. 切换到账号 B(B 也装有同 id 的插件 X);
3. 在插件页打开 X 的面板 → 面板内仍是账号 A 的登录态/数据。

> 注:PR #1371 已修掉「面板宿主不随 owner 重挂载」那一层(`ghostPanelOwnerKey` +
> 宿主 key 纳入 owner 代际),所以 webview 元素本身会重建;但**分区(存储)没有隔离**,
> 重建后的 webview 仍挂在同一个分区上,本 issue 说的是这一层。

## 位置

- `apps/desktop/src/shared/ghost.ts:40` — `ghostPartition(id)` 只用 ghostId 构造分区名;
- `apps/desktop/src/shared/ghost.ts:45` — `parseGhostPartition` 反解 ghostId,是 webview 附加闸「验明正身」的依据;
- 消费点:
- `apps/desktop/src/renderer/cindy-brain/ghostPanelBody.tsx:186`(面板 webview)
- `apps/desktop/src/renderer/cindy-brain/GhostSettingsWebview.tsx:214`(设置页 webview)
- `apps/desktop/src/main/cindy-brain/runtime/electronSandboxAdapter.ts:216`(`registerGhostProtocol`)
- `apps/desktop/src/main/cindy-brain/runtime/electronSandboxAdapter.ts:474`(`session.fromPartition`)

## 为什么需要独立处理(三条硬约束)

1. **存量插件兼容红线** —— `docs/dev-rules/plugin-security-and-authoring.md` §5「升级必须无感」,
且明写增量适用原则。分区名一旦纳入 owner,已装插件的存储会整体落到一个**新的空分区**:
用户升级后插件内的登录态与配置凭空消失,等于被迫重新配置。规则要求「做不到无感就必须
自带从旧版数据的自动迁移」。**所以本 issue 的修复必须配套分区数据迁移方案。**
2. **插件基座白名单确认门** —— `AGENTS.md`:改到运行时/沙箱的 PR 一律走白名单确认门,
需放行人明确 Approve,不看 diff 大小、不因「是 bugfix」豁免。partition 正是沙箱面。
3. **Electron 安全边界** —— `parseGhostPartition` 是 main 侧 webview 附加闸反解 ghostId 的
依据,分区格式变更必须同步该校验与协议注册,见
`docs/dev-rules/electron-security-and-process-boundaries.md`。

## 期望方案(待该 PR 讨论)

- **方案 A**:分区名纳入稳定的 owner 身份,并在首次启动时把旧分区数据迁移到新分区
(满足「升级无感」);
- **方案 B**:owner 切换时定向清理分区。实现简单,但会连带丢掉切回原账号时本该保留的
数据,体验未必更好。

倾向 A,但迁移的失败/中断路径需要单独设计。

## 验收标准

- 账号 A 在插件面板内产生的 `localStorage` / `IndexedDB` / `Cookie`,切到账号 B 后不可见;
- 切回账号 A 时,A 自己的数据仍在(不是简单清空);
- 面板重挂载、旧 webview 销毁后同样不泄漏;
- **升级路径无感**:老用户升级后已装插件不需要重新登录 / 重新配置;
- `parseGhostPartition` 的验明正身逻辑与新分区格式一致,webview 附加闸测试通过。

## 出处

PR #1371 review(chatgpt-codex-connector,2026-08-02)。该 PR 是插件页 UI 改版,
经与产品负责人确认后不把沙箱分区改动并入,改由本 issue 独立跟踪。

Contributor guide

Open the contributing guide

Research direction

Start by reading ghostPartition and parseGhostPartition in apps/desktop/src/shared/ghost.ts, then trace their consumers in ghostPanelBody.tsx, GhostSettingsWebview.tsx, and electronSandboxAdapter.ts. Review the plugin security and Electron boundary rules before choosing between per-owner partitions and cleanup. Done means account data is isolated and preserved across switching and upgrades, with the partition validation and webview attachment tests passing.

Written by the indexing model from the issue text.

Assessment

Tech stack
electron, typescript
Domain
desktop, security
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.