dsh-conversation-recycle-bin 0.3.15 → 0.3.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AUDIT-2026-10-19-desktop-web-recheck.md +301 -0
- package/AUDIT-2026-10-19-review.md +129 -0
- package/AUDIT-2026-10-20-last3-versions.md +248 -0
- package/AUDIT-2026-10-21-desktop-web.md +194 -0
- package/AUDIT-2026-10-21-optimization.md +228 -0
- package/AUDIT-2026-10-22-desktop-web-compat.md +330 -0
- package/AUDIT-2026-10-24-desktop-web-rc3.md +277 -0
- package/AUDIT-2026-10-29-desktop-web.md +207 -0
- package/CHANGELOG.md +59 -0
- package/README.md +10 -4
- package/client.js +28 -8
- package/cordis.patch.yml +6 -0
- package/lib/core/config.js +15 -4
- package/lib/core/memory/json-index-adapter.js +32 -2
- package/lib/core/policy.js +184 -126
- package/lib/core/trash.js +245 -245
- package/lib/host/api.js +13 -5
- package/lib/host/catalog.js +8 -0
- package/lib/host/gateway.js +393 -368
- package/lib/host/paths.js +27 -7
- package/lib/version.js +1 -1
- package/package.json +5 -5
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
# dsh-conversation-recycle-bin · 桌面端 × 网页端 Bug 排查报告(0.3.2 在线版 vs 0.3.3 workspace,含「全部修复」后复验)
|
|
2
|
+
|
|
3
|
+
- 审计日期:2026-10-19(首次采样 20:11;执行「全部修复」并在两端复验后于 21:2x 最终更新)
|
|
4
|
+
- 审计对象:`dsh-conversation-recycle-bin`
|
|
5
|
+
- 审计范围:桌面端(DSH Desktop / Electron)与网页端(DSH Web Host + 浏览器)双端缺陷排查
|
|
6
|
+
- 基线:`AUDIT-2026-10-17-desktop-web.md`(v0.2.0 审计,修复于 0.2.2)、`AUDIT-2026-10-19-review.md`(S1–S3/P1–P5/R1–R4,修复于 0.3.3)
|
|
7
|
+
- 结论可信度约定:每条结论标注证据类型(实机探测 / 最小用例实证 / 代码走读 / 测试运行);无法复现者列入「待观察」,不作推测
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 0. 结论速览
|
|
12
|
+
|
|
13
|
+
> 本节为**修复后**的最终结论;修复执行与两端复验证据见 §10。§1–§7 保留修复前的排查过程与原始证据(§7.3 为修复前状态)。
|
|
14
|
+
|
|
15
|
+
1. **W-01 两端版本漂移已消除**:workspace 0.3.3 已提交(`a0b5317 release: v0.3.3`);网页端 profile 由 registry 版 0.3.2 改为 `link:` → workspace,桌面端 junction 同样指向该工作区 → **两端现运行同一份 0.3.3 源码**,且网页端已在**运行中实例**上实测 `version: "0.3.3"`。
|
|
16
|
+
2. **S1 / S2 / S3 三个缺陷**:先在 0.3.2 安装副本上以最小用例实证存在,0.3.3 已修;修复后**在运行中的网页端 Host 上以真实 HTTP 复验全部通过**(`403 E_FORBIDDEN_ORIGIN` / `404 E_NOT_FOUND`,跨站与非回环请求被拦)。
|
|
17
|
+
3. **本轮未发现新的插件代码缺陷**:workspace 0.3.3 全量测试 128/128 通过;修复后两端在线路由均正常挂载并受连接鉴权门禁保护(未认证请求 401,实证)。
|
|
18
|
+
4. **桌面端已装载最终代码**:21:12:30 重启(Host 端口 1525 → 随机 4978);21:16:19 又一次应用启动,**该次宿主在 startup 阶段崩溃**(`DesktopHostFatalError: cannot create effect on inactive context`,栈全在 DSH 桌面宿主/cordis,**无本插件帧** → O-4),21:16:27 重新启动成功(Host 端口 **14998**)。新实例插件路由已挂载(插件格式 401,21:20 实测),且其启动时间晚于最后一次代码写入(18:17:30)、junction 磁盘哈希与已审计 0.3.3 全 MATCH → **加载最终 0.3.3**;但因桌面宿主 token 不可得,运行版本仍属**推断**(O-1)。
|
|
19
|
+
5. **0.3.3 已发布并已推送**(发布前是唯一未完成项):经用户确认后执行 `npm publish` → registry `latest` 现为 **0.3.3**(`dist-tags.latest=0.3.3`、`versions` 末项 0.3.3;版本元数据 `gitHead=a0b5317`、`shasum c215fc95aef8299c8355bada637c404e4eab3dfb`、35 文件、解包 437,450 B)。GitHub `origin/main` = `a0b5317`,注释标签 `v0.3.3` 已推送(`git ls-remote --tags origin` 复核:`refs/tags/v0.3.3` → `a0b5317`)。其他机器/其他 profile 现已可安装到含 S1–S3 修复的 0.3.3。
|
|
20
|
+
6. 测试环境 `test/.tmp` 的 ACL 缺陷曾阻断 `npm test`(12/12 EPERM),已修复且**排除为插件缺陷**(W-07)。
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## 1. 目标确认与假定清单
|
|
25
|
+
|
|
26
|
+
用户仅明确「插件 = `dsh-conversation-recycle-bin`(即本 workspace)」与技术栈「自己判断」,其余未答。按用户要求以主流组合假定并显式列出:
|
|
27
|
+
|
|
28
|
+
| 项目 | 取值 | 来源 |
|
|
29
|
+
| --- | --- | --- |
|
|
30
|
+
| 插件名称 | `dsh-conversation-recycle-bin` | 用户确认 |
|
|
31
|
+
| 技术栈 | Node.js ≥ 20 · ESM · DSH Cordis 插件;宿主侧 `lib/{core,host,memory}`;客户端 `client.js` 手写 `React.createElement`、无构建步骤、以 `window.__ModuleLoader__.load` 装载 | 代码判定(package.json / client.js / cordis.patch.yml) |
|
|
32
|
+
| 插件版本 | ① workspace 0.3.3(开发版,未提交)② web profile 安装版 0.3.2 ③ desktop profile `link:` → workspace 0.3.3 | 实机核对 |
|
|
33
|
+
| 目标桌面端 | DeepSeek Harness 桌面应用 0.2.1-rc.2(`@deepseek-ai/dsh-desktop 0.2.1-rc.2`);运行时 **Electron 44.7.0 / Node 24.21.0** | 实机:桌面宿主崩溃日志自报 `electron: 44.7.0`,与 `resources\version` = 44.7.0 一致。**更正**:早前由 exe 字符串扫描得出的「Electron 39.8.5 / Chromium 152.0.7977.130」与应用自报值不符,已撤回该行数据;**Chromium 具体内核版本未确证** |
|
|
34
|
+
| 操作系统 | Windows 11 家庭版 中文版 build 26200 | 实机 `Win32_OperatingSystem` |
|
|
35
|
+
| 目标浏览器/内核 | Microsoft Edge 155.0.4283.62(Chromium 155);本机无 Chrome | 实机探测;**假定** Chromium 系 ≥120 为主流基线(未覆盖 Firefox/Safari) |
|
|
36
|
+
| 宿主运行时 | DSH 0.2.1-rc.2(CLI 与桌面同版),Node v22.23.2,`DSH_HOME=C:\Users\34332\.dsh`,`DSH_PROFILE=web` | 实机探测 |
|
|
37
|
+
| 运行/调试方式 | `npm test`(8 文件串行)、`npm run check`、profile 直载(web / desktop 现均为 junction → 工作区)、在线 HTTP 探测(网页端固定 `127.0.0.1:8080`;**桌面端端口每次启动随机**:1525 → 4978 → 14998)、双版本并载最小用例 | 实机可行;**bsk 浏览器自动化不可用**(`the bsk CLI ("bsk") was not found`) |
|
|
38
|
+
|
|
39
|
+
**假定声明**:以上「主流假定」项(OS/浏览器/桌面端组合)均以本机实际安装组合为准,未做跨机器验证;若目标用户群体使用其他 Electron/Chromium 大版本或非 Chromium 浏览器,需补充矩阵验证。
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## 2. 跨端环境矩阵(每端 ≥2 组合)
|
|
44
|
+
|
|
45
|
+
### 网页端
|
|
46
|
+
|
|
47
|
+
| 编号 | 宿主 | 插件版本 | 浏览器/内核 | 用途 |
|
|
48
|
+
| --- | --- | --- | --- | --- |
|
|
49
|
+
| W-1 | 实机 Web Host `127.0.0.1:8080`(node pid 46468,17:44:49 启动,DSH 0.2.1-rc.2) | 安装拷贝 **0.3.2**(pnpm-lock 锁定) | Edge 155.0.4283.62(Chromium 155) | 生产实态在线探测 |
|
|
50
|
+
| W-2 | 同机 Node v22.23.2 直载路由 handler(同 fixture,`file://` 并载双版本) | **0.3.2** vs **0.3.3** 对照 | 不涉及渲染层 | 最小用例实证与跨版本对照 |
|
|
51
|
+
|
|
52
|
+
### 桌面端
|
|
53
|
+
|
|
54
|
+
| 编号 | 宿主 | 插件版本 | 内核 | 用途 |
|
|
55
|
+
| --- | --- | --- | --- | --- |
|
|
56
|
+
| D-1 | 实机桌面 Host(`DeepSeek Harness`,Electron 44.7.0):① 17:09:32 实例(宿主 pid 12276,端口 1525,已关闭)② 21:12:30 实例(宿主 pid 43068,端口 4978,21:16 退出)③ **21:16:27 当前实例**(宿主 pid 53284,端口 **14998**) | junction → workspace **0.3.3**(当前实例启动 21:16:27 晚于 `lib/host/api.js` 末次写入 18:17:30 → 加载最终代码;磁盘 SHA-256 MATCH) | Chromium 版本**未确证**(见 §1 更正) | 桌面实态在线探测 |
|
|
57
|
+
| D-2 | 同机 Node v22.23.2 直载(DSH CLI 运行时;`process.versions.electron` 分支不成立) | workspace **0.3.3** | — | 桌面/网页共用宿主代码路径对照 |
|
|
58
|
+
|
|
59
|
+
**两端特有分支(代码走读)**:
|
|
60
|
+
- `lib/host/api.js:329`:`auditSource = process.versions?.electron !== undefined ? 'desktop-ui' : 'web-ui'` —— 仅审计来源标签不同。
|
|
61
|
+
- `lib/host/api.js:147-155, 882`:Electron 壳 `dsh-app://app` origin 被信任(桌面壳从该 origin 提供应用,`out/lib/main.js` 拒绝非该 origin);无网页端等价分支。
|
|
62
|
+
- 客户端 `client.js` 无平台分支,仅 `navigator.language`(403-404)与 `navigator.userAgent`(912-913)。
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## 3. 逐项核对清单(用户指定的七类)
|
|
67
|
+
|
|
68
|
+
| 类别 | 结论 | 证据 |
|
|
69
|
+
| --- | --- | --- |
|
|
70
|
+
| API 可用性与前缀差异 | **无缺陷**。客户端仅用 `navigator.language` / `navigator.userAgent`;全文件无 `-webkit-/-moz-/-ms-` 前缀、无 `clipboard`/`localStorage`/`sessionStorage`/`WebSocket`/`Notification` 依赖;宿主侧唯一平台分支见上(auditSource、dsh-app origin) | 代码走读 + grep(client.js 命中 4 行全部为 navigator 读取) |
|
|
71
|
+
| DOM 与渲染差异 | 客户端为手写 `createElement`、无构建步骤,两端共用同一 Web UI 装载路径;0.2.0 审计的 z-index 遮挡(P0-3,设置弹层 z-index 1000 + `createPortal(body)` 覆盖批量删除确认框)已于 0.2.2 修复 | 代码走读 + `client-module.test.mjs` 19 项通过;**真实浏览器 UI 未直接观察 → O-2** |
|
|
72
|
+
| 生命周期与初始化时序 | **正常**。`dsh.manifest`: `client.platform="web"` + `immediately:true`,两端同一装载路径;实测两端 Host 上插件路由均已挂载(返回插件自身错误格式) | 实机探测(8080/1525 均返回插件 `E_UNAUTHORIZED` 格式);桌面进程快照问题见 W-06 |
|
|
73
|
+
| 文件与剪贴板等系统权限 | 插件不涉及剪贴板;文件操作全在宿主侧 `DSH_HOME` 下 trash 目录(`<dshHome>/delete-conversation/trash`,`lib/host/paths.js:42-53`),两端共享 `DSH_HOME`。唯一权限故障为测试环境 ACL(W-07,非插件缺陷) | 代码走读 + 实机 ACL 诊断 |
|
|
74
|
+
| 网络与跨域策略 | `requireSameOrigin=true` / `allowNonLoopback=false` 默认生效;0.2.2 已修「桌面 `forwardWebRequest` 剥离 origin/sec-fetch-site 导致绕过」(P1-1)。本轮两端未认证请求均被 401 拒绝(**门禁在线生效**);0.3.2 的 S1 端口比较缺陷见 W-02 | 实机探测 + 最小用例实证 |
|
|
75
|
+
| 打包与资源路径 | 存在**版本漂移**:web=实体拷贝 0.3.2,desktop=junction→workspace 0.3.3(W-01)。插件 bundle row 来自插件自身 `cordis.patch.yml`(`- insert: - id: conversation-recycle-bin`),两端 profile 级 `cordis.patch.yml` 均无该行;`exports` 含 `./client`、`./cordis.patch.yml`;trash 路径沿用历史命名 `pluginId='delete-conversation'`(改名需迁移) | 实机核对 + 代码走读 |
|
|
76
|
+
| 构建产物差异 | **无构建产物差异**:无打包步骤,`files` 直接发布 `lib/`、`client.js`、`cordis.patch.yml`、`config.schema.json` 等;`npm run check` = `scripts/check-syntax.mjs` + manifest 测试(0.3.3 起覆盖范围扩大,M3) | 代码走读 + 实机核对 |
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## 4. 问题清单
|
|
81
|
+
|
|
82
|
+
> 下表「复现步骤/根因」为修复前状态描述;「是否已修复」列为 2026-10-19 21:1x 执行「全部修复」后的最终状态(证据见 §10)。
|
|
83
|
+
|
|
84
|
+
| 编号 | 端 | 环境 | 复现步骤 | 严重级别 | 根因 | 是否已修复 |
|
|
85
|
+
| --- | --- | --- | --- | --- | --- | --- |
|
|
86
|
+
| W-01 | 双端(部署) | web profile / desktop profile | 比对 `profiles/web/package.json`+`pnpm-lock.yaml` 与 `profiles/desktop/package.json`:原 web=0.3.2 实体拷贝,desktop=`link:`→workspace 0.3.3 | **高** | 发布/升级流程:web profile 锁定 0.3.2,0.3.3 未发布 | **已修复**:提交 `a0b5317 release: v0.3.3`;web profile 经 `plugin_manager.install_bundle` 改为 `link:` → 工作区;运行中网页端实测 `version:"0.3.3"` |
|
|
87
|
+
| W-02 | 网页端(在线) | W-1(0.3.2) | `requestOriginVerdict({host:'127.0.0.1', origin:'http://127.0.0.1:9999'}, {requireSameOrigin:true, allowNonLoopback:false})` → 0.3.2 `ALLOW`(应 `BLOCK`) | **中**(实际可利用性低:连接鉴权门禁前置) | 插件代码:无端口 `Host` 时端口比较恒真 | **已修复**:0.3.3 `lib/host/api.js:154-168`;修复后在线复验 `403 E_FORBIDDEN_ORIGIN` |
|
|
88
|
+
| W-03 | 网页端(在线) | W-1(0.3.2) | `GET /conversation-recycle-bin/api/<未知路径>` → 0.3.2 `405 E_BAD_REQUEST`(应 `404 E_NOT_FOUND`) | 低 | 插件代码:`method !== 'POST'` 守卫先于 404 兜底 | **已修复**:在线复验 `404 E_NOT_FOUND` |
|
|
89
|
+
| W-04 | 网页端(在线) | W-1(0.3.2) | `trash/<id>/manifest.json` 写入 `{ broken json` 后 `GET /trash` → 0.3.2 该条目**完全隐身**(0.3.3 返回 `status:'unreadable'`) | **中** | 插件代码:损坏条目无 `unreadable` 兜底,无法清理 | **已修复**:0.3.3 `scanUnreadable`;由 `test/api.test.mjs:918-948` 覆盖。**未在生产回收站目录复用该写入用例**(避免污染真实数据) |
|
|
90
|
+
| W-05 | 网页端(在线) | W-1(0.3.2) | 0.3.2 缺失 P1–P5 / R1–R4(性能与健壮性):live drain 全量重建、`/trash` 无缓存、`/audit` 全量读 5MB、跨进程锁 30s < 批量最坏 900s、blocked 仍全量记忆计数、超限 body 仍读完、反馈失败仍扣限流、`/sessions` 固定 soft 裁决、失败项占限流额度 | 中 | 插件代码(版本差异) | **已修复**:随 0.3.3 上线(两端现均为 0.3.3) |
|
|
91
|
+
| W-06 | 桌面端 | D-1 | 桌面 Host 原进程启动 17:09:32,早于 workspace `lib/host/api.js` 末次写入 18:17:30 → 运行中内存快照早于最终 0.3.3 | 低 | 宿主环境特性:junction 开发链接**无热加载**;桌面 Host 端口每次启动随机 | **已处置**:21:12:30 重启(端口 1525 → 4978)→ 21:16:19 又一次启动时宿主 startup 崩溃(`INACTIVE_EFFECT`,O-4)→ 21:16:27 当前实例(端口 14998)装载 workspace 0.3.3 并挂载插件路由;运行版本仍为推断(O-1) |
|
|
92
|
+
| W-07 | 测试环境(双端共用) | `test/.tmp` | `npm test` → memory 用例 12/12 `EPERM mkdir ...\test\.tmp\multi-*`;`pwsh New-Item` 同样拒绝 | 中(阻断验证) | 环境:目录 ACL 缺 `WRITE_OWNER` | **已修复**:为用户 SID 授予 FullControl allow ACE;回滚产物已移出工作区(§9) |
|
|
93
|
+
|
|
94
|
+
### 待观察(无法复现/无法在线验证,不臆测)
|
|
95
|
+
|
|
96
|
+
| 编号 | 事项 | 原因 |
|
|
97
|
+
| --- | --- | --- |
|
|
98
|
+
| O-1 | 桌面 Host 内存中代码的确切版本快照 | 未认证请求在版本差异逻辑**之前**即被 401 短路,无法在线区分;仅能由进程启动时间与文件时间推断 |
|
|
99
|
+
| O-2 | 真实浏览器/桌面壳中的 UI 行为 | bsk 浏览器自动化不可用(CLI 缺失)+ 无浏览器会话 cookie;客户端逻辑仅由 `client-module.test.mjs` 19 项静态覆盖 |
|
|
100
|
+
| O-3 | ~~0.3.3 修复集未提交~~ | **已消除**:2026-10-19 21:0x 提交为 `a0b5317 release: v0.3.3`(19 文件,+906/−120) |
|
|
101
|
+
| O-4 | 桌面宿主 21:16:19 启动阶段崩溃 `DesktopHostFatalError: cannot create effect on inactive context`(code `INACTIVE_EFFECT`) | 仅出现 1 次、无法复现;栈全部位于 DSH 桌面宿主(`dsh-desktop-host/lib/index.js:83`)与 cordis(`lib/index.js:1133 / 1356 / 1622`),**无本插件帧**;应用 21:16:27 重启后正常。不归因于本插件,不据此改动代码 |
|
|
102
|
+
| O-5 | 桌面应用主窗口标题「插件缺失原因询问 — DeepSeek Harness」的来源 | 已排除本地自动化(`automation_list` → `automations: []`);`.dsh\sessions`、`.dsh\storages`、`profiles\desktop`(<2MB、排除 `.zstd`)中均未检索到该文本(会话正文为压缩存储,未解压核对)→ 来源不明,**不作推断** |
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## 5. 根因分析
|
|
107
|
+
|
|
108
|
+
### W-02(S1 · 网页端在线版同源校验绕过)
|
|
109
|
+
0.3.2 `lib/host/api.js:157-161` 的比较表达式:
|
|
110
|
+
|
|
111
|
+
```js
|
|
112
|
+
originPort !== '' && originPort === (requestPort === '' ? originPort : requestPort)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
当请求 `Host` 头**不含端口**(如 `Host: 127.0.0.1`)时 `requestPort === ''`,右侧退化为 `originPort`,等式恒真 → 任意端口的同主机 `Origin`(如 `http://127.0.0.1:9999`)被判定为同源并放行。
|
|
116
|
+
0.3.3 修复(`lib/host/api.js:154-168`):引入 `effectiveRequestPort`,`Host` 缺端口时按 `Origin` 的 scheme 回退默认端口(http→80 / https→443)后再完整比较。
|
|
117
|
+
**可利用性评估**:插件路由已被 P1-1 的连接鉴权门禁前置(未认证请求 401),且默认 `allowNonLoopback=false` 限定回环,故实际可利用性低;该修复属必要的纵深防御。
|
|
118
|
+
|
|
119
|
+
### W-03(S2 · 未知路由响应码语义错误)
|
|
120
|
+
0.3.2 `lib/host/api.js:842-844` 的 `method !== 'POST'` 守卫先于 `api.js:892` 的 `E_NOT_FOUND` 兜底执行,导致任何未知 **GET** 路径返回 `405 E_BAD_REQUEST`,误导客户端与运维诊断(405 表示「方法不允许」,而非「路由不存在」)。0.3.3 统一为 `404 E_NOT_FOUND`。
|
|
121
|
+
|
|
122
|
+
### W-04(S3 · 损坏回收站条目隐身)
|
|
123
|
+
0.3.2 `lib/core/trash.js:94-102` 读取 `manifest.json` 失败即跳过该条目,`GET /trash` 列表中完全不出现 → 用户既看不到也无法清理,成为磁盘上不可见残留。0.3.3 增加 `scanUnreadable`:条目以 `status:'unreadable'` 上列表并可清理;若仍持有 session/ 或记忆则返回 `409 E_TRASH_CONFLICT` 拒绝误删。
|
|
124
|
+
|
|
125
|
+
### W-01(两端版本漂移 · 主因)
|
|
126
|
+
- web profile:`dsh-conversation-recycle-bin: "0.3.2"`,`node_modules` 内为**实体拷贝**(pnpm-lock 确认 specifier/version 均 0.3.2);web Host 进程 17:44:49 启动晚于拷贝 mtime 12:45:18 → **确定在线运行 0.3.2**。
|
|
127
|
+
- desktop profile:`link:D:/Users/34332/AI/dsh-conversation-recycle-bin`,Junction 创建于 10/17 23:17:36 → 解析到 workspace 0.3.3。
|
|
128
|
+
- 结果:网页端缺少 S1–S3 + P1–P5 + R1–R4 全部修复;同时桌面端"生产"直接暴露在开发工作区源码上(任何本地编辑即时影响,且需重启才生效)。
|
|
129
|
+
|
|
130
|
+
### W-06(桌面进程快照落后)
|
|
131
|
+
桌面 Host 于 17:09:32 启动,而 `lib/host/api.js` 末次写入为 18:17:30。DSH 宿主不热加载已装载模块,故运行中的桌面实例持有 17:09 时刻的代码快照;该快照是否已含最终 S1 修复无法在线判定(O-1)。**处置:重启桌面应用后复验**。
|
|
132
|
+
|
|
133
|
+
### W-07(测试环境 ACL,非插件缺陷)
|
|
134
|
+
`test/.tmp` 目录 ACL 缺 `WRITE_OWNER`,导致 `mkdtemp` 触发 EPERM。经诊断工具修复:为用户 SID `S-1-5-21-3420368060-2751838060-2397351331-1001` 在 `test\.tmp` 与 `test` 授予 FullControl allow ACE,均验证通过。**排除为插件缺陷**(同一 workspace 其他目录写入正常,且修复后 128/128 全绿)。
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## 6. 改动点与关键代码片段
|
|
139
|
+
|
|
140
|
+
**本轮审计未新增/修改任何插件代码**(三个在线缺陷的修复均已存在于 workspace 0.3.3,尚未提交)。本轮实际改动仅为环境与验证脚手架:
|
|
141
|
+
|
|
142
|
+
| 改动 | 内容 | 状态 |
|
|
143
|
+
| --- | --- | --- |
|
|
144
|
+
| 环境修复 | `test\.tmp`、`test` 目录 ACL 授予用户 SID FullControl(备份与回滚脚本在 `.acl-repair/`) | 已完成并验证 |
|
|
145
|
+
| 验证脚手架 | 跨版本并载最小用例(临时脚本,已清理) | 已删除 |
|
|
146
|
+
|
|
147
|
+
0.3.3 关键修复点(供发布/评审对照):
|
|
148
|
+
|
|
149
|
+
| 编号 | 文件 | 要点 |
|
|
150
|
+
| --- | --- | --- |
|
|
151
|
+
| S1 | `lib/host/api.js:154-168` | `effectiveRequestPort`:`Host` 缺端口时按 Origin scheme 回退默认端口后完整比较 |
|
|
152
|
+
| S2 | `lib/host/api.js`(404 兜底统一) | 未知路径统一 `404 E_NOT_FOUND` |
|
|
153
|
+
| S3 | `lib/core/trash.js` | `scanUnreadable` 暴露 `status:'unreadable'`;仍占用 session/记忆 → `409 E_TRASH_CONFLICT` |
|
|
154
|
+
| P1 | `lib/host/gateway.js` | `isRunning` 内存探针优先,避免 live drain 全量重建 |
|
|
155
|
+
| P2 | `lib/host/api.js` | `/trash` 增加 1.5s TTL 缓存 |
|
|
156
|
+
| P3 | `lib/host/api.js` | `/audit` 仅读尾部 256KB |
|
|
157
|
+
| P4 | `lib/core/file-lock.js` | 锁预算 `min(900s, 15s + 16s×N + drain)` |
|
|
158
|
+
| P5 | `lib/host/api.js` | blocked 时跳过 `countForSessions` |
|
|
159
|
+
| R1 | `lib/host/api.js` | 超限立即 `finish(413)`,不再读完 body |
|
|
160
|
+
| R2 | `lib/host/api.js` / `lib/core/rate-limit.js` | `takeSlot` 返回 `release()`,失败退还额度 |
|
|
161
|
+
| R3 | `lib/host/api.js` | `/sessions` 按 `config.defaultMode` 裁决 |
|
|
162
|
+
| R4 | `lib/host/api.js` | `limiter.take(1)` 带释放,失败退还 |
|
|
163
|
+
| M2 | `lib/core/ids.js` | `SESSION_ID_PATTERN` 抽出复用 |
|
|
164
|
+
| M3 | `scripts/check-syntax.mjs` | 语法检查覆盖范围扩大 |
|
|
165
|
+
| M4 | `lib/host/api.js` | `/state` 不再输出绝对 `storePath` |
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## 7. 回归验证结果
|
|
170
|
+
|
|
171
|
+
### 7.1 代码级回归(workspace 0.3.3)
|
|
172
|
+
|
|
173
|
+
`npm test`:**8 个测试文件全通过 128/128**
|
|
174
|
+
|
|
175
|
+
| 测试文件 | 通过数 |
|
|
176
|
+
| --- | --- |
|
|
177
|
+
| `test/manifest.test.mjs` | 9 |
|
|
178
|
+
| `test/client-module.test.mjs` | 19 |
|
|
179
|
+
| `test/core.test.mjs` | 14 |
|
|
180
|
+
| `test/cascade.test.mjs` | 15 |
|
|
181
|
+
| `test/memory.test.mjs` | 12 |
|
|
182
|
+
| `test/trash-audit.test.mjs` | 3 |
|
|
183
|
+
| `test/feedback.test.mjs` | 18 |
|
|
184
|
+
| `test/api.test.mjs` | 38 |
|
|
185
|
+
| **合计** | **128** |
|
|
186
|
+
|
|
187
|
+
其中覆盖本轮修复点:origin 全量比较(`test/api.test.mjs:652-718`)、404(`:904-916`)、损坏 manifest(`:918-948`)。
|
|
188
|
+
|
|
189
|
+
### 7.2 跨版本最小用例对照(W-2 环境)
|
|
190
|
+
|
|
191
|
+
| 用例 | 0.3.2(web 在线版) | 0.3.3(workspace) | 判定 |
|
|
192
|
+
| --- | --- | --- | --- |
|
|
193
|
+
| S1 `Host` 无端口 + 异端口 Origin | `ALLOW`(缺陷) | `BLOCK` | 0.3.2 缺陷复现 / 0.3.3 已修 |
|
|
194
|
+
| S2 `GET` 未知路径 | `405 E_BAD_REQUEST` | `404 E_NOT_FOUND` | 同上 |
|
|
195
|
+
| S3 损坏 manifest 条目 | 列表隐身 | `status:'unreadable'` | 同上 |
|
|
196
|
+
|
|
197
|
+
夹具说明:`host: '127.0.0.1:14968'` 为必需头(缺失会因空主机名非回环触发 `403 E_FORBIDDEN_HOST`);trash 目录实为 `<dshHome>/delete-conversation/trash`(`lib/host/paths.js:42-53`),非 `plugins/...` 层级。
|
|
198
|
+
|
|
199
|
+
### 7.3 在线回归(两端实机)
|
|
200
|
+
|
|
201
|
+
| 端 | 请求 | 结果 | 判定 |
|
|
202
|
+
| --- | --- | --- | --- |
|
|
203
|
+
| 网页端 `127.0.0.1:8080` | `GET /`、`POST/GET /conversation-recycle-bin/api/state`(带 `x-conversation-recycle-bin:1`) | `401 {"ok":false,"error":{"code":"E_UNAUTHORIZED","message":"This request is not authenticated with the Host."}}` | 插件路由已挂载 + 连接鉴权门禁生效 |
|
|
204
|
+
| 桌面端 `127.0.0.1:1525` | 同上 | 同上(插件自身错误格式) | 同上 |
|
|
205
|
+
| 桌面端 `1526` / `1527` | 同路径 | DSH 自身 `{"error":{"message":"missing or invalid Authorization bearer"}}` | 非插件路由(属其他宿主服务),已排除误报 |
|
|
206
|
+
|
|
207
|
+
**回归结论**:修复点(S1–S3)与主流程(路由挂载、鉴权门禁)在两端均通过;完整浏览器 UI 端到端回归受 O-2 限制未执行。
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
## 8. 遗留风险
|
|
212
|
+
|
|
213
|
+
1. **发布状态:已完成(2026-10-19 21:34 执行)**:经用户确认后执行 `npm publish` → registry `latest` = **0.3.3**(`dist-tags.latest=0.3.3`、`versions` 末项 0.3.3;版本元数据 `gitHead=a0b5317`、`shasum c215fc95aef8299c8355bada637c404e4eab3dfb`、35 文件、打包 140.1 kB、解包 437,450 B)。GitHub `origin/main` = `a0b5317`,注释标签 `v0.3.3`(tag 对象 `ff69ac3` → `a0b5317`)已推送。**注意**:`npm publish` 返回成功 ≠ registry 立即可见——发布后约 4 分钟内 `npm view dist-tags` 仍显示 0.3.2,之后才变为 0.3.3;判定发布成功应同时看「版本元数据端点 `…/dsh-conversation-recycle-bin/0.3.3` 返回 200」与「packument `dist-tags`」,不要只看发布后立即的 `dist-tags`。
|
|
214
|
+
2. **两端均为 `link:` 开发链接**:本地任何编辑即时进入"生产"两端,且宿主不热加载(需重启宿主进程才生效);缺少版本锁定、灰度与"安装版可复现"能力。若要稳定运行,建议发布后改为 registry 安装 + 语义化升级,并在 CI 中跑 128 项测试。
|
|
215
|
+
3. **桌面端运行版本为推断而非实测(O-1)**:桌面宿主 token 不可得(`%APPDATA%\@deepseek-ai\dsh-desktop` 只有崩溃日志,无宿主 stdout/token 日志;`.dsh` 21:0x 后新文件均无 token;`%TEMP%` 内只有**网页端**的 `dsh-market-restart-2026-10-19T09-44-48.out.log`,桌面侧无对应 out.log),且未认证请求在版本比较**之前**即被 401 短路。加固后的推断链:当前实例 21:16:27 启动 > `lib/host/api.js` 末次写入 18:17:30,且 junction → workspace 磁盘 SHA-256 与已审计 0.3.3 全 MATCH。**实测方法**:桌面 Host 端口**每次启动随机**(1525 → 4978 → **14998**),需先确定端口(`Get-NetTCPConnection -State Listen` 或看宿主进程参数),再从桌面窗口 URL 取 token 后 GET `http://127.0.0.1:<port>/conversation-recycle-bin/api/state`,应见 `"version": "0.3.3"`。
|
|
216
|
+
4. **真实浏览器/桌面壳 UI 端到端未执行(O-2)**:bsk CLI 缺失(`the bsk CLI ("bsk") was not found`),客户端仅由 `client-module.test.mjs` 19 项静态覆盖。建议人工在两端设置页各执行一次「删除会话 → 回收站 → 恢复」。
|
|
217
|
+
5. **S1 修复的实际攻击面有限**(位于连接鉴权之后且限定回环),风险评级中;但不应因此回退该修复。
|
|
218
|
+
6. **非 Chromium 浏览器未覆盖**:Firefox/Safari 未纳入矩阵(按假定非目标环境)。
|
|
219
|
+
7. **ACL 变更的回滚产物已移出工作区**:`C:\Users\34332\.dsh\acl-repair-dsh-conversation-recycle-bin\`(2 份备份 JSON + 回滚 .ps1 + 2 份诊断报告)。该 ACL 修复是 `npm test` 的前置条件,回滚会让 12 项 memory 用例重新 EPERM。
|
|
220
|
+
|
|
221
|
+
### 建议的下一步
|
|
222
|
+
|
|
223
|
+
- ~~发布 0.3.3 到 npm~~ **已完成**(2026-10-19 21:34,见 §8.1);
|
|
224
|
+
- 如需桌面端版本实测:从桌面窗口 URL 取 token 后请求 `/state`,或人工在桌面端设置页执行一次删除→恢复流程;
|
|
225
|
+
- 若要恢复浏览器自动化验证,需先安装 bsk CLI。
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
## 9. 清理记录
|
|
230
|
+
|
|
231
|
+
| 文件 | 处置 |
|
|
232
|
+
| --- | --- |
|
|
233
|
+
| `scripts/.probe-electron-version.mjs`(一次性探测 Electron 版本) | 已删除 |
|
|
234
|
+
| `scripts/.xver-repro.mjs`(跨版本并载最小用例) | 已删除 |
|
|
235
|
+
| `scripts/check-syntax.mjs` | **保留**(`npm run check` 依赖,非临时文件) |
|
|
236
|
+
| `%TEMP%` 下本会话一次性脚本(`.scan-dsh*.mjs`、`.probe-desktop-ports.mjs`、`.diag-desktop.mjs`、`.diag-logs.mjs`、`.diag-m2.mjs` 等) | 随用随删:每个脚本均在同一条命令末尾 `Remove-Item`,已确认无残留 |
|
|
237
|
+
| 桌面崩溃日志、市场 `log.ndjson`、`dsh-market-restart-*.out.log` | **保留不删**(宿主自身产物,仅作只读证据引用,见 §10.4) |
|
|
238
|
+
| `.acl-repair/`(ACL 备份 + 回滚脚本 + 诊断报告) | **移出工作区** → `C:\Users\34332\.dsh\acl-repair-dsh-conversation-recycle-bin\`(保留回滚能力,工作区洁净) |
|
|
239
|
+
|
|
240
|
+
工作区最终状态:`git log -1` = `a0b5317 release: v0.3.3`;远端 `origin/main` = `a0b5317` + 标签 `v0.3.3`;`git status --short` 仅剩本报告的更新(随后以 docs 提交推送,最终为干净工作树)。
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
## 10. 修复执行记录与两端复验(执行用户指令「全部修复」)
|
|
245
|
+
|
|
246
|
+
### 10.1 执行清单
|
|
247
|
+
|
|
248
|
+
| # | 动作 | 结果 | 证据 |
|
|
249
|
+
| --- | --- | --- | --- |
|
|
250
|
+
| 1 | 提交 0.3.3 | `a0b5317 release: v0.3.3`(19 文件,+906/−120) | `git log -1`、`git status --short` |
|
|
251
|
+
| 2 | 网页端升级 | `plugin_manager`:`remove_bundle`(pnpm 退出 0,−28 依赖包)→ `install_bundle(target=工作区)`,依赖变为 `dsh-conversation-recycle-bin <- D:/Users/34332/AI/dsh-conversation-recycle-bin`(junction) | plugin_manager applied + 磁盘核对 |
|
|
252
|
+
| 3 | 磁盘一致性核对 | `api.js`(6AD9DDB07875)/`version.js`(9B8F4D834633)/`client.js`(9E62FD6CA5D4)/`core/ids.js`(924966D48629) 与工作区 SHA-256 全 MATCH;旧 0.3.2 `api.js`(A62DCAF46E7B) 已消失;插件目录文件数 34 → 224 | `Get-FileHash` |
|
|
253
|
+
| 4 | 网页端热装载 | web 宿主日志 `dsh-market-restart-2026-10-19T09-44-48.out.log`:`[conversation-recycle-bin] API registered at /conversation-recycle-bin/api (dshHome=…, mode=soft, memory=on, trash=…)` 出现 **2 次**(第 1 次 = 17:44 启动挂载;第 2 次 = 21:03 插件管理器重载) | 日志行 |
|
|
254
|
+
| 5 | 桌面端重启装载 | ① 21:12:30 重启(旧 5 进程 17:09:32 关闭;新 5 进程 22916/26496/43068/48708/53760;宿主 pid 43068,端口 1525 → **4978**);② 21:16:19 又一次启动(shell pid 47728)在宿主 startup 崩溃(`INACTIVE_EFFECT`,见 §10.4);③ **21:16:27 当前实例**(shell 38032;宿主 pid 53284,端口 **14998/14999/15000**) | `Get-Process` / `Get-NetTCPConnection` / 桌面 `logs\crash-2026-10-19T13-16-23-831Z-main.log` |
|
|
255
|
+
| 6 | 桌面端插件挂载复验 | ① 21:0x:`4978` 上 `/api/state`、`/api/trash` → **插件自身格式 401**;② 21:20:`14998` 上同路径 → **插件自身格式 401**;桌面 profile 插件目录 `version=0.3.3`、`pnpm-lock.yaml` = `link:D:/Users/34332/AI/dsh-conversation-recycle-bin`、磁盘 SHA-256 MATCH | 在线探测 + 磁盘核对 |
|
|
256
|
+
| 7 | 测试环境 ACL | 见 W-07 | 诊断报告 |
|
|
257
|
+
| 8 | npm 发布 | **已执行**(用户确认后):`+ dsh-conversation-recycle-bin@0.3.3`,35 文件、打包 140.1 kB、解包 437,450 B、`shasum c215fc95aef8299c8355bada637c404e4eab3dfb`、`integrity sha512-q1igimuIeQYYP…jiyAKnGP1DrHw==`;registry 复验 `dist-tags.latest=0.3.3`、`gitHead=a0b5317`(CDN 有约 4 分钟传播延迟) | `npm publish` 输出 / `npm view` / `https://registry.npmjs.org/dsh-conversation-recycle-bin/0.3.3` |
|
|
258
|
+
| 9 | GitHub 推送与打标 | `55883b9..a0b5317 HEAD -> main`;注释标签 `v0.3.3` → `a0b5317`(远端复核:`refs/tags/v0.3.3`=`ff69ac3…`、`refs/tags/v0.3.3^{}`=`a0b5317`、`refs/heads/main`=`a0b5317`) | `git push` 输出 / `git ls-remote --tags origin` |
|
|
259
|
+
| 10 | 发布产物复核(「npm 上的包 == 已审计代码」) | 直连下载 `https://registry.npmjs.org/dsh-conversation-recycle-bin/-/dsh-conversation-recycle-bin-0.3.3.tgz`:**sha1 = `c215fc95aef8299c8355bada637c404e4eab3dfb`(与 `npm publish` 输出一致)**、140,063 B、解包 **35 文件**;`lib/host/api.js`、`lib/version.js`、`client.js`、`lib/core/ids.js` 的 SHA-256 与工作区**逐一 MATCH**;包内 `PLUGIN_VERSION = '0.3.3'` | `Invoke-WebRequest` + `tar -xzf` + `Get-FileHash` |
|
|
260
|
+
| 11 | 本次报告更新入库 | `a44d933 docs(audit): 更正 Electron 版本与桌面端重启/崩溃事实,记录 0.3.3 发布与 v0.3.3 标签`(已推送,`git status -sb` = `## main...origin/main`) | `git log --oneline` |
|
|
261
|
+
|
|
262
|
+
> 复核陷阱记录:`npm pack dsh-conversation-recycle-bin@0.3.3` 首次返回 `404 tarball, folder, http url, or git url`——本地 packument 缓存仍是发布前的旧文档;改用 `--prefer-online` 或直连下载绕开。发布后 tarball 端点也晚于元数据端点可用(元数据 200 时 tarball 一度 `{"error":"Not found"}`,约 4–6 分钟后可取)。
|
|
263
|
+
|
|
264
|
+
### 10.2 修复后在线回归(运行中实例、真实 HTTP、仅只读请求)
|
|
265
|
+
|
|
266
|
+
网页端 `127.0.0.1:8080`(带会话 cookie):
|
|
267
|
+
|
|
268
|
+
| 用例 | 结果 | 判定 |
|
|
269
|
+
| --- | --- | --- |
|
|
270
|
+
| `GET /conversation-recycle-bin/api/state` | `200`,`version: "0.3.3"` | **在线实证运行 0.3.3** |
|
|
271
|
+
| S1:`Host: 127.0.0.1` + `Origin: http://127.0.0.1:9999` | `403 E_FORBIDDEN_ORIGIN` | S1 在线生效(0.3.2 曾 `ALLOW`) |
|
|
272
|
+
| S2:未知路径 `GET` | `404 E_NOT_FOUND` | S2 在线生效(0.3.2 曾 `405`) |
|
|
273
|
+
| 跨站:`Origin` + `sec-fetch-site: cross-site` | `403 E_FORBIDDEN_ORIGIN` | 跨站防线正常 |
|
|
274
|
+
| 非回环:`Host: evil.example` | `403 E_FORBIDDEN_ORIGIN` | `allowNonLoopback=false` 生效 |
|
|
275
|
+
| 未认证对照(无 cookie) | `401 E_UNAUTHORIZED` | 连接鉴权门禁生效 |
|
|
276
|
+
| 主流程读接口 | `GET /sessions` 200(45,012B)· `GET /trash` 200(3,424B)· `GET /audit` 200(3,740B) | 主流程无回归 |
|
|
277
|
+
| 路由名核对 | `/sessions` 实为 **GET**(`lib/host/api.js:919`);先前用 POST 得 404 属**探针错误** | 已排除误报 |
|
|
278
|
+
|
|
279
|
+
桌面端 `127.0.0.1:4978`(21:12 实例,21:16 已退出)与 `127.0.0.1:14998`(21:16:27 当前实例)(无会话凭据,仅能验证挂载与门禁):
|
|
280
|
+
|
|
281
|
+
| 用例 | 结果 | 判定 |
|
|
282
|
+
| --- | --- | --- |
|
|
283
|
+
| 插件路由 `/api/state`、`/api/trash` | `401 E_UNAUTHORIZED`(插件格式) | 两次启动后插件路由均重新挂载 + 门禁生效 |
|
|
284
|
+
| `GET /` | `401 dsh web authentication required; reopen the URL printed by dsh web.` | 桌面 Host Web 正常 |
|
|
285
|
+
| 14999/15000 同路径 | DSH `{"error":{"message":"missing or invalid Authorization bearer"}}` | 非插件路由,已排除误报 |
|
|
286
|
+
|
|
287
|
+
### 10.3 修复后仍未实测的项目(不臆测)
|
|
288
|
+
|
|
289
|
+
- **桌面端 `/state.version`**:桌面宿主 token 无法获取,未认证请求在版本比较之前即 401 → 桌面运行版本以推断给出(§8.3 / O-1)。
|
|
290
|
+
- **真实浏览器/桌面壳 UI 交互**:bsk 自动化不可用 → O-2。
|
|
291
|
+
- **桌面端写路径**(删除/恢复/清理):本轮两端**只做只读探测**,未在生产回收站目录制造写操作,以免污染真实数据。
|
|
292
|
+
|
|
293
|
+
### 10.4 排查期间观察到的桌面环境事件(均无与本插件的因果证据)
|
|
294
|
+
|
|
295
|
+
| 时间(本地) | 事件 | 证据 | 归因 |
|
|
296
|
+
| --- | --- | --- | --- |
|
|
297
|
+
| 21:16:23 | 桌面宿主**启动阶段崩溃**:`DesktopHostFatalError: cannot create effect on inactive context`(code `INACTIVE_EFFECT`;栈:`dsh-desktop-host/lib/index.js:83` → `cordis/lib/index.js:1622 / 1356 / 1133` → `app.asar/lib/main.js:3831`);应用 21:16:27 重新启动成功 | `C:\Users\34332\AppData\Roaming\@deepseek-ai\dsh-desktop\logs\crash-2026-10-19T13-16-23-831Z-main.log`(28 行;`app: @deepseek-ai/dsh-desktop 0.2.1-rc.2`、`electron: 44.7.0`、`node: 24.21.0`、`shell pid: 47728`、`phase: startup`) | **无本插件帧**,判为 DSH 宿主/装载器时序问题;仅 1 次、未复现 → **O-4**,不据此改动本插件 |
|
|
298
|
+
| 21:12:37 / 21:16:19 / 21:16:32 | 桌面市场日志在每次应用启动时记录 `dsh-connect-trae: no loader entry matched` | `C:\Users\34332\.dsh\profiles\desktop\.dsh-market\log.ndjson` | 另一个插件(`dsh-connect-trae`,`enabled=false`)缺少 loader 行;与本插件无关。**若在排查「插件缺失」,这是可核查线索** |
|
|
299
|
+
| 21:19:29 | 桌面市场 `update-blocked`:`@liustack/modsearch` 升级被拒(有 agent 会话在跑:`session-84cbb8f8…`、`session-9014534a…`) | 同上 | 与本插件无关(市场为避免打断运行中会话而自我节流) |
|
|
300
|
+
|
|
301
|
+
**观察(不作推断)**:桌面应用主窗口标题为「插件缺失原因询问 — DeepSeek Harness」。已排除本地自动化(`automation_list` → `automations: []`);在 `.dsh\sessions`、`.dsh\storages`、`profiles\desktop`(<2MB、排除 `.zstd`)中未检索到该文本,会话正文为压缩存储、未解压核对 → 列为 **O-5**,来源不明。
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# dsh-conversation-recycle-bin 优化点审查(2026-10-19)
|
|
2
|
+
|
|
3
|
+
- 基线:`npm test` 8 个测试文件全部通过(最后一套 34/34,进程退出码 0);`node --check` 通过。
|
|
4
|
+
- 范围:`lib/` 全部 22 个模块、`client.js`(1988 行)、配置与清单文件。
|
|
5
|
+
- 说明:2026-10-17 审计(AUDIT-2026-10-17-desktop-web.md)中的 P0/P1/P2 问题经核对均已修复并有测试覆盖,本次不重复列出。
|
|
6
|
+
- 标注约定:**[已验证]** = 读代码 + 运行时实测确认;**[代码推断]** = 仅由代码路径推断,未在真实 DSH 环境实测。
|
|
7
|
+
|
|
8
|
+
> **处置状态(2026-10-19,v0.3.3)**:S1–S3、P1–P5、R1–R4、M1–M4 全部修复并补回归测试,`npm test` 128 用例全绿、`npm run check` 通过;明细见 `CHANGELOG.md` [0.3.3]。
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## 1. 安全 / 正确性
|
|
13
|
+
|
|
14
|
+
### S1. Host 头无端口时,同源校验的端口比较被完全跳过(防御纵深缺口)**[已验证]**
|
|
15
|
+
|
|
16
|
+
位置:`lib/host/api.js:157-161`(`requestOriginVerdict`)
|
|
17
|
+
|
|
18
|
+
```js
|
|
19
|
+
const portMatches = originPort !== '' && originPort === (requestPort === '' ? originPort : requestPort)
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
当 `Host` 头不带端口时 `requestPort === ''`,表达式变成 `originPort === originPort`,恒为 true——任意端口的 Origin 都通过。实测:
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
Host: 127.0.0.1, Origin: http://127.0.0.1:9999 => PASS(按文档意图应 BLOCK)
|
|
26
|
+
Host: 127.0.0.1, Origin: https://127.0.0.1 => PASS(scheme 也未比较)
|
|
27
|
+
Host: localhost, Origin: http://localhost:9999 => PASS
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
这与 `config.schema.json` 的描述("a missing port defaults by the Origin's own scheme")和 `test/api.test.mjs:664-676` 的注释意图不符。现有测试只覆盖了 Host **显式带** 443/80 的情形,未覆盖 Host 无端口的情形。
|
|
31
|
+
|
|
32
|
+
- 实际可利用性:**低**。浏览器只有在目标端口是 80/443、或经反向代理剥离端口时,Host 头才不带端口;DSH 默认监听回环高端口,此情形默认不可达。
|
|
33
|
+
- 建议修复(一行,且不破坏现有测试):
|
|
34
|
+
|
|
35
|
+
```js
|
|
36
|
+
const effectiveRequestPort = requestPort === '' ? defaultPortFor(parsed.protocol) : requestPort
|
|
37
|
+
const portMatches = originPort !== '' && originPort === effectiveRequestPort
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### S2. 未知 GET 路由返回 405 + 错误文案 **[已验证]**
|
|
41
|
+
|
|
42
|
+
位置:`lib/host/api.js:842-844`。实测:
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
GET /api/nope => 405 E_BAD_REQUEST "This endpoint accepts POST requests only."
|
|
46
|
+
POST /api/nope => 404 E_NOT_FOUND "Unknown endpoint."
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
GET 未知路径与 POST 未知路径行为不一致,且 405 文案有误导性。建议将 405 仅用于"路径存在但方法错误",未知路径统一返回 404 `E_NOT_FOUND`。
|
|
50
|
+
|
|
51
|
+
### S3. 损坏的 manifest 会让回收站条目永久"隐身"且无法经 UI 清理 **[已验证]**
|
|
52
|
+
|
|
53
|
+
位置:`lib/core/trash.js:94-102`(`read` 解析失败返回 null)、`listAll` 过滤 null、`discardEntry` 再次 `read` 得到 null 后报 `E_TRASH_NOT_FOUND`。
|
|
54
|
+
|
|
55
|
+
manifest.json 因磁盘问题损坏(或被截断)后,该条目目录仍占空间,但 `/trash` 列表不显示、discard 接口拒绝,用户只能手动删目录。建议:`listAll` 把目录名合法但 manifest 不可读的条目作为 `unreadable` 项暴露,允许在二次确认后强制 discard(与现有 `performDiscard` 的磁盘检查一致)。
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## 2. 性能
|
|
60
|
+
|
|
61
|
+
### P1. live 会话 drain 轮询每次都全量重建会话列表 **[代码推断]**
|
|
62
|
+
|
|
63
|
+
位置:`lib/host/lifecycle.js:36-40` → `gateway.invalidate()` + `gateway.inspect()`(`lib/host/gateway.js:198-234`)。
|
|
64
|
+
|
|
65
|
+
每 ≤100ms 一轮、最长 2000ms(最多约 20 轮),每轮在清缓存后执行:`controller.list()`(5s 超时)、**两次** `workspaceRegistry.list()`(`loadList` 与 `workspaceFor` 各一次)、读标题投影、读 `workspace.json`、读单会话投影文件。批量删除多个 live 会话时线性放大。
|
|
66
|
+
|
|
67
|
+
建议:drain 轮询只查询 running 标志(`sessions` 服务或 `controller.stat`),不调用 `invalidate()`;列表缓存在事务结束后由现有的 `gateway.invalidate()` 统一刷新一次即可。
|
|
68
|
+
|
|
69
|
+
### P2. `/trash` 每次全量读取并解析全部 manifest,无缓存无分页 **[已验证]**
|
|
70
|
+
|
|
71
|
+
位置:`lib/core/trash.js:106-117`。回收站条目只能手动 purge(`trashRetentionDays` 在 schema 中明确为"仅展示",属有意设计),长期使用后每次打开设置页产生 N 次文件读取 + JSON.parse。建议加短 TTL 缓存(参照 gateway 的 1.5s),或列表接口只读必要字段。
|
|
72
|
+
|
|
73
|
+
### P3. `/audit` 每次全量读取(上限 5MB)并解析全部行 **[已验证]**
|
|
74
|
+
|
|
75
|
+
位置:`lib/core/audit.js:79-98`,`tail()` 先读整个文件、`JSON.parse` 每一行,再取最后 N 条。设置页每次打开触发。建议从文件尾部按块读取(tail bytes),或缓存解析结果按 mtime 失效。
|
|
76
|
+
|
|
77
|
+
### P4. 跨进程锁等待 30s,明显短于批量最坏耗时 **[已验证]**
|
|
78
|
+
|
|
79
|
+
位置:`lib/host/api.js:322-326`(锁 `timeoutMs: 30_000`)对比 `api.js:517`(`planTimeoutMs` 可达 900s)。两个 Host 共用一个 DSH_HOME 时,大批量在第一个 Host 执行超过 30s,第二个 Host 必然收到 `E_LOCK_BUSY`——即使前者仍在正常工作。建议锁等待时间按批量规模估算(复用 `planTimeoutMs` 逻辑),或在 E_LOCK_BUSY 中带回预计等待时长。
|
|
80
|
+
|
|
81
|
+
### P5. `/preview` 存在 blocked 时仍做全量记忆计数 **[已验证]**
|
|
82
|
+
|
|
83
|
+
位置:`lib/host/api.js:483-509`:blocked 已确定不发确认令牌,记忆计数结果对用户无意义却仍执行(索引文件读取 + 解析)。建议 `blocked.length > 0` 时跳过 `countForSessions`。
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## 3. 健壮性 / 行为细节
|
|
88
|
+
|
|
89
|
+
### R1. 请求体超限后仍继续读完整个 body 才回 413 **[已验证]**
|
|
90
|
+
|
|
91
|
+
位置:`lib/host/api.js:228-256`。超限后只是停止缓存 chunk,未销毁流;慢速超大请求会一直占用连接。回环场景影响极小,可在 overflow 时立即 `req.destroy()` 或先回 413。
|
|
92
|
+
|
|
93
|
+
### R2. 反馈邮件发送失败也消耗限流额度 **[已验证]**
|
|
94
|
+
|
|
95
|
+
位置:`lib/host/feedback.js:407`(`takeSlot()` 在投递之前)。SMTP 临时故障时用户重试 5 次即被锁一小时,失败闭站偏保守。建议仅在投递成功后计数,或传输失败时回退 slot。
|
|
96
|
+
|
|
97
|
+
### R3. `stopLiveSessions:false` 下批量列表把 hard 模式下不可删的 live 行标为可删 **[已验证]**
|
|
98
|
+
|
|
99
|
+
位置:`lib/host/api.js:732-733`,`sessionsForUi` 固定用 soft 模式判定 `deletable`;代码注释称"soft 是最宽判定",但 `stopLiveSessions:false` 时 hard 模式对 live 会话**更严格**(`policy.js:97-107`)。用户在批量列表勾选 live 行、切到 hard 后才被 preview 拦截。边界配置,建议在该行同时给出 hard 模式判定(或在 UI 上按当前 defaultMode 判定)。
|
|
100
|
+
|
|
101
|
+
### R4. 失败的删除项也占用每分钟限流额度 **[已验证]**
|
|
102
|
+
|
|
103
|
+
位置:`lib/host/api.js:611`,执行前一次性 `limiter.take(sessions.length)`,随后失败的条目不返还。属保守设计(防重试风暴),是否合理可斟酌;至少批量全失败时可考虑不扣额度。
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## 4. 可维护性 / 小项(低优先)
|
|
108
|
+
|
|
109
|
+
- **[已验证]** 死 CSS:`.dc-mode-preferred`(`client.js:1822`)全文件无引用。
|
|
110
|
+
- **[已验证]** 会话 ID 正则 `/^[A-Za-z0-9._-]{6,80}$/` 在 `api.js:64` 与 `gateway.js:199` 重复定义,建议抽共享常量。
|
|
111
|
+
- **[已验证]** `npm run check` 只 `node --check` 3 个文件;建议覆盖 `lib/**` 全部文件,或改用 `node --test test/` 统一聚合 8 个测试文件(目前手写在 package.json 串行链中)。
|
|
112
|
+
- **[已验证]** `GET /state` 把每个记忆适配器的**绝对磁盘路径** `storePath` 返回给客户端(`api.js:701-710` + `registry.js:50-56`),客户端只使用 id/label/available/reason。成功响应不经过 `redactDetails`。属数据最小化问题,建议从 state 响应中剔除。
|
|
113
|
+
- **[已验证]** `lib/host/api.js:826` 起 try 块内多缩进一层,纯格式问题。
|
|
114
|
+
- **[已验证]** 中英文案键完全对齐(147/147,脚本校验无缺失),此项无需处理。
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## 5. 已核实为正常、无需改动的方面
|
|
119
|
+
|
|
120
|
+
- 跨进程文件锁(nonce 校验、pid 存活、stale 判定、心跳 unref)、确认令牌单次使用与计划绑定、事务回滚顺序、错误信息路径脱敏、软删除归档路径、记忆 adapter 的 bundle 先写后改与补偿——核心安全机制实现正确。
|
|
121
|
+
- `node_modules/` 仅含可选依赖 nodemailer,已在 `.gitignore` 中,未被 git 跟踪。
|
|
122
|
+
- `trashRetentionDays` 不自动清理是 schema 中明示的有意设计(purging is always explicit),不是缺陷。
|
|
123
|
+
- 2026-10-17 审计的 P0/P1/P2 全部有对应修复与回归测试。
|
|
124
|
+
|
|
125
|
+
## 6. 不确定性说明
|
|
126
|
+
|
|
127
|
+
- P1(drain 轮询开销)的实际卡顿程度**未在真实 DSH 环境测量**,结论来自代码路径推断;`controller.list()` 的真实耗时取决于会话规模。
|
|
128
|
+
- S1 在真实部署中的可达性(Host 是否可能监听 80/443 或经端口剥离代理)**没有部署环境证据**;默认高端口回环配置下不可达,定为低风险防御纵深项。
|
|
129
|
+
- 其余条目均由代码 + 运行时实测确认。
|