dsh-isolation-gate 0.3.2 → 1.0.0

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/CHANGELOG.md CHANGED
@@ -1,5 +1,38 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.0.0 — 2026-10-23
4
+
5
+ **1.0 = 功能与格式冻结,不是「完美版」。** 承诺范围见 README 的「1.0 的稳定性承诺」:状态文件 v3、CLI 子命令与退出码、HTTP 路径、四条判定语义 —— 只增不破。
6
+
7
+ 这一版**没有功能变更、没有格式变更**,只做发版前的对齐:
8
+
9
+ - **文档纠错**:状态文件章节标到 v3(原文写 v2,实际 `STATE_VERSION = 3`)。
10
+ - **测试数字纠错**:改为实跑结果 **6 个套件 · 396 条断言**(原文「4 个套件 · 325 断言」是旧数;英文 README 的 309 同样过期)。
11
+ - **定位改写**:从「别人做规则、我们做管理面」改为「别人按工具/参数立规则,我们按目录立对象」—— 前者已被同类追上,后者才是实际差异。
12
+ - **SECURITY.md**:支持范围改 `1.x`;新增「已知边界」五条(同权限域可自改、档位模式无 realpath、黑名单只判抽到的目标、审计可改、管不到 DSH 之外)。
13
+ - **新增**:README(中/英)各加一节「1.0 的稳定性承诺」。
14
+
15
+ ## 0.4.0 — 2026-10-23
16
+
17
+ - **改(重要)**:组类型(严格 / 宽松)以前只在按钮上写一句小字,实际是**两套相反语义** —— 严格组是白名单(没列进组的全拒),宽松组是黑名单(没列进组的照常)。实测踩坑:把目录加进严格组当「只禁这个」,结果全盘封死。现在模板卡片和编辑区都挂一条醒目的**模式横幅**(白名单模式 / 黑名单模式 + 「没列进组的怎么算」),编辑区还给一键「换成宽松组 / 严格组」(不动清单,只换清单外怎么算)。
18
+ - 模板卡片显示**挂在哪几个会话**(会话 id 前 8 位),不再只有一个数字。
19
+ - 模板编辑区原来那句「不在组里的目录不受影响」是**错的**(那是宽松组才成立),已按组类型分写。
20
+ - 会话头部选择器里每组也补上「没点名的全拒 / 照常」。
21
+ - **测试**:客户端 94 → 97 条(锁住两种组类型的横幅文案与会话头部提示),`npm test` 6/6 通过。
22
+
23
+ - **新**:会话头部小按钮(`conversation.session.header.utilities`,垃圾桶那一排右侧)—— 打开就能给**当前会话**挂一个「特别门禁」组,或者选「不额外限制」取消。单选:一个会话最多挂一个组,组本身可以被多个会话共用。
24
+ - 按钮直接显示当前挂的组名;没挂时显示「门禁」并保持低调(虚线灰),挂上后变强调色 —— 一眼看出这个会话是不是被额外限制着。
25
+ - 数据源与「特别门禁」页是同一份:`GET /profiles?sessionId=` 读、`POST /session-profile` 写,不另存副本。
26
+ - 弹出的清单里每组带一行说明(严格组 / 宽松组 · 多少条),并固定一句边界提示「组只会更严,不会放宽全局名单」。
27
+ - 弹层包在渲染错误边界里:它坏了也不会拖垮整个会话头部。
28
+ - **测试**:客户端 93 → 94 条(3 个座位 + 新座位 id),`npm test` 6/6 通过。
29
+
30
+ ## 0.3.3 — 2026-10-22
31
+
32
+ - **改名**:「策略档」→「隔离强度」,三档一律用白话名 —— 宽松(normal)/ 严格(strict)/ 全封(sealed),每档一句「这档会发生什么」
33
+ - **修**:页内三个标签点了不切换。标签改用原生 `<button type="button">` + `data-gate-tab`,并把当前页记在模块级变量里(宿主若重建组件实例,回来还是刚才那页);顶栏右侧新增「当前页说明」可肉眼确认
34
+ - **测试**:客户端自测新增 11 条标签回归(3 个标签、白话名、`type=button`、点击回传 list/profiles/audit、首屏渲染、三档白话名),82 → 93
35
+
3
36
  ## 0.2.3 — 2026-10-21
4
37
 
5
38
  - 新增 `npm run release:check`:一条命令核对「本地版本 / npm latest / git tag / GitHub Release / 元数据回链 / 发现性关键词」
@@ -49,16 +82,16 @@
49
82
  - 坏配置 / 坏状态文件 ⇒ 按 `sealed` 处理(fail-closed)
50
83
  - 联网软禁:`web_search` / `web_fetch` 等工具 + `curl` / `npm install` / `git clone` 等命令;`npm test`、`git status` 不误判
51
84
  - `sealed` 下认不出路径的 shell 命令一律拒
52
- - 自测:新增 44 + 12 条断言,套件 6/6 通过
85
+ - 自测:新增 44 + 12 条断言,套件 6/6 通过
53
86
 
54
87
  ### 面板拆成三页(同日)
55
88
 
56
89
  - 侧栏三项:`门禁`(对象授权)/ `门禁 · 策略`(档位与目录绑定,只读)/ `门禁 · 审计`(磁盘差异 + 审计账本)
57
90
  - 新增宿主路由 `GET /__dsh-isolation-gate/profiles`
58
- - 原单页保留为第 1 页,内容未删
91
+ - 原单页保留为第 1 页,内容未删
59
92
 
60
- - 修正:不做三个侧栏项,改回**一个「门禁」+ 页内标签**(对象与分组 / 策略档 / 审计与诊断),与「技能」面板同款交互
93
+ - 修正:不做三个侧栏项,改回**一个「门禁」+ 页内标签**(对象与分组 / 策略档 / 审计与诊断),与「技能」面板同款交互
61
94
 
62
- - 审计页新增「清空审计」:清空前整份备份到 `D:\回收站\isolation-gate-audit\access-log-<时间>.jsonl`,再截断原文件;新增 `POST /__dsh-isolation-gate/audit/clear`
95
+ - 审计页新增「清空审计」:清空前整份备份到 `D:\回收站\isolation-gate-audit\access-log-<时间>.jsonl`,再截断原文件;新增 `POST /__dsh-isolation-gate/audit/clear`
63
96
 
64
- - 修:`POST /audit/clear` 缺 `clearAudit` 导入(运行时 ReferenceError → 400);前端改用 `post()` 正确序列化 body
97
+ - 修:`POST /audit/clear` 缺 `clearAudit` 导入(运行时 ReferenceError → 400);前端改用 `post()` 正确序列化 body
package/README.en.md CHANGED
@@ -91,22 +91,24 @@ dsh plugin --profile desktop add dsh-isolation-gate
91
91
 
92
92
  ---
93
93
 
94
- ## State file (v2)
94
+ ## State file (v3)
95
95
 
96
96
  ```jsonc
97
97
  {
98
- "version": 2,
98
+ "version": 3,
99
99
  "defaultLevel": "deny",
100
100
  "groups": { "harness isolation": { "note": "", "updatedAt": "…" } },
101
101
  "objects": {
102
102
  "Zcode": { "level": "deny", "note": "", "group": "harness isolation", "updatedAt": "…" },
103
103
  "E:\\data\\shared\\project": { "level": "read", "note": "", "group": "", "updatedAt": "…" }
104
- }
104
+ },
105
+ "profiles": { "strict-delivery": { "mode": "allowlist", "allow": [{ "path": "E:\\data\\shared", "level": "read" }], "network": "deny", "unknownTool": "deny" } },
106
+ "sessionOverrides": { "<sessionId>": "strict-delivery" }
105
107
  }
106
108
  ```
107
109
 
108
- - **v1 is still readable**: legacy `harnesses: { key: { level } }` is migrated on read, **levels are preserved**;
109
- the next write persists v2.
110
+ - **Legacy formats stay readable**: v1 `harnesses: { key: { level } }` and v2 `objects` are normalised to v3 on read
111
+ (**levels are preserved**; `profiles` / `sessionOverrides` default to empty); the next write persists v3.
110
112
  - Missing file = **deny everything**; corrupt JSON = **deny everything and never overwrite it**.
111
113
  - No BOM; writes go through temp-file + rename (atomic).
112
114
  - `state.harnesses` remains a read-only in-memory alias; it is no longer written to disk.
@@ -185,6 +187,19 @@ External `Origin` → 403; wrong method → 405; bad JSON / invalid level → 40
185
187
 
186
188
  ---
187
189
 
190
+ ## The 1.0 stability promise
191
+
192
+ From 1.0 onward, the surfaces below **only grow, never break**. A breaking change would ship as 2.0 with a migration note.
193
+
194
+ | Surface | Promise |
195
+ |---|---|
196
+ | **State file** | On-disk format `v3`; v1 / v2 are read-compatible and upgraded on write, levels preserved. New fields are appended only; existing field meanings do not change. |
197
+ | **CLI** | `bin/gate.mjs` subcommands and exit codes (`0` allowed / `3` denied / `2` usage or environment error) are frozen; new flags are additive. |
198
+ | **HTTP** | Existing `/__dsh-isolation-gate/*` paths and methods are frozen; new routes are additive. |
199
+ | **Decision semantics** | The three levels (deny / read / full), longest-prefix matching, default deny and fail-closed are frozen — changing them would be a different product. |
200
+
201
+ **Not promised**: panel visuals and copy, and audit-log fields, may evolve; bug fixes are not breaking changes.
202
+
188
203
  ## Known boundaries (read this)
189
204
 
190
205
  1. **Raw filesystem access and separate processes are not blocked.** Any program or script that bypasses DSH tool calls can read/write the disk directly.
@@ -198,7 +213,7 @@ External `Origin` → 403; wrong method → 405; bad JSON / invalid level → 40
198
213
  ## Tests
199
214
 
200
215
  ```bash
201
- node test/run-all.mjs # 4 suites · 309 assertions · fully offline
216
+ node test/run-all.mjs # 6 suites · 396 assertions · fully offline
202
217
  node --check lib/*.js
203
218
  ```
204
219
 
package/README.md CHANGED
@@ -28,8 +28,10 @@ dsh plugin --profile desktop add dsh-isolation-gate
28
28
 
29
29
  它不是又一个 allow/deny 规则引擎,也不是沙箱本身。
30
30
 
31
- - **别人做的是「怎么让 agent 多访问几个目录」**(multi-root / allowlist / write-protect)或「怎么用规则拦住工具」(permission-rules / safety-net);
32
- - **我们做的是「这台机器上哪些目录属于禁区、现在给什么级别、谁改的、agent 自己怎么查」**。
31
+ - **别人按「工具 / 命令 / 参数」立规则**(`dsh-permission-rules`、`dsh-permissions` 这类:allow / deny / ask + glob / 正则);
32
+ - **我们按「目录」立对象**:这台机器上哪些目录属于禁区、现在给什么级别、谁改的、agent 自己怎么查。
33
+
34
+ 规则引擎回答的是「**这次调用准不准**」,门禁回答的是「**这个目录开不开**」。两者可以叠着用,不冲突。
33
35
 
34
36
  所以它的核心是**管理面**:
35
37
 
@@ -105,23 +107,23 @@ dsh plugin --profile desktop add dsh-isolation-gate
105
107
 
106
108
  ---
107
109
 
108
- ## 状态文件(v2)
110
+ ## 状态文件(v3)
109
111
 
110
112
  ```jsonc
111
113
  {
112
- "version": 2,
114
+ "version": 3,
113
115
  "defaultLevel": "deny",
114
- "groups": {
115
- "harness 隔离": { "note": "", "updatedAt": "…" }
116
- },
116
+ "groups": { "harness 隔离": { "note": "", "updatedAt": "…" } },
117
117
  "objects": {
118
118
  "Zcode": { "level": "deny", "note": "", "group": "harness 隔离", "updatedAt": "…" },
119
119
  "E:\\资料\\合作档案\\某项目": { "level": "read", "note": "", "group": "", "updatedAt": "…" }
120
- }
120
+ },
121
+ "profiles": { "严格交付": { "mode": "allowlist", "allow": [{ "path": "E:\\资料\\库", "level": "read" }], "network": "deny", "unknownTool": "deny" } },
122
+ "sessionOverrides": { "<sessionId>": "严格交付" }
121
123
  }
122
124
  ```
123
125
 
124
- - **v1 仍可读**:旧的 `harnesses: { key: { level } }` 读入时自动迁移成 `objects`,**级别不丢**;下一次写盘才落成 v2。
126
+ - **旧格式仍可读**:v1 的 `harnesses: { key: { level } }` 与 v2 的 `objects` 读入时自动收敛成 v3(**级别不丢**,`profiles` / `sessionOverrides` 缺省为空);下一次写盘才落成 v3。
125
127
  - 文件缺失 = **全禁**;坏 JSON = **全禁且不覆盖原文件**。
126
128
  - 无 BOM;写盘走临时文件 + rename(原子替换)。
127
129
  - `state.harnesses` 在内存里保留为只读别名,磁盘上不再写它。
@@ -202,6 +204,19 @@ node bin/gate.mjs log --limit 20
202
204
 
203
205
  ---
204
206
 
207
+ ## 1.0 的稳定性承诺
208
+
209
+ 从 1.0 起,下面这些**只增不破**;要破坏会走 2.0 并附迁移说明:
210
+
211
+ | 面 | 承诺 |
212
+ |---|---|
213
+ | **状态文件** | 磁盘格式 `v3`;v1 / v2 一律**读时兼容、写时升级**,级别不丢。新增字段只追加,不改已有字段含义。 |
214
+ | **CLI** | `bin/gate.mjs` 的子命令与退出码(`0` 放行 / `3` 拒绝 / `2` 用错或环境错误)不改;新参数只增加。 |
215
+ | **HTTP** | `/__dsh-isolation-gate/*` 现有路径与方法不改;新路由只增加。 |
216
+ | **判定语义** | 三级(deny / read / full)、最长前缀命中、默认禁、fail-closed —— 这四项冻结;改它们等于换产品。 |
217
+
218
+ **不承诺**:面板的视觉与文案、审计日志的字段可以随版本演进;修 bug 不算破坏性变更。
219
+
205
220
  ## 已知边界(必须明说)
206
221
 
207
222
  1. **raw fs 与独立进程拦不住。** 任何程序、脚本、别的 agent 不经过 DSH 工具调用就直接读写磁盘,本插件看不到。
@@ -217,16 +232,18 @@ node bin/gate.mjs log --limit 20
217
232
  ## 自测
218
233
 
219
234
  ```bash
220
- node test/run-all.mjs # 4 个套件 · 325 断言 · 全离线(不需要 GUI/dsh)
235
+ node test/run-all.mjs # 6 个套件 · 396 断言 · 全离线(不需要 GUI/dsh)
221
236
  node --check lib/*.js
222
237
  ```
223
238
 
224
239
  | 套件 | 覆盖 |
225
240
  |---|---|
226
- | `run-data-layer.mjs` | 状态 v1→v2 迁移 / 无 BOM / 坏 JSON 不覆盖 / 路径键 / 名单备份 / 分组 / 孤儿清理 |
227
- | `run-guard.mjs` | 三级×四操作矩阵 / 最长前缀 / 同前缀不误伤 / fail-closed / 真实 I/O 闭环 |
228
- | `run-routes.mjs` | 11 条路由 / 组级切 / 工具门挂载与干跑 / 选择器缺省 / 级别落盘 / 403/405/400 |
229
- | `run-client.mjs` | 两座位注册 / 摘要条 / 分组卡片 / 分段控件 / `⋯` 菜单 / 空态 / 交互接线 |
241
+ | `run-data-layer.mjs`(85) | 状态 v1/v2→v3 迁移 / 无 BOM / 坏 JSON 不覆盖 / 路径键 / 名单备份 / 分组 / 孤儿清理 |
242
+ | `run-guard.mjs`(74) | 三级×四操作矩阵 / 最长前缀 / 同前缀不误伤 / fail-closed / 真实 I/O 闭环 |
243
+ | `run-profiles.mjs`(45) | 隔离强度三档 / 白名单与黑名单语义 / 会话绑定只能加严 / 联网软禁 |
244
+ | `run-profiles-runtime.mjs`(11) | 档位文件读取 / 目录绑定 / 坏配置 fail-closed |
245
+ | `run-routes.mjs`(84) | 11 条路由 / 组级切 / 工具门挂载与干跑 / 选择器缺省 / 级别落盘 / 403/405/400 |
246
+ | `run-client.mjs`(97) | 两座位注册 / 摘要条 / 分组卡片 / 分段控件 / `⋯` 菜单 / 空态 / 交互接线 |
230
247
 
231
248
  ---
232
249
 
@@ -247,7 +264,9 @@ MIT · 见 [LICENSE](./LICENSE)。
247
264
 
248
265
  **已在做 / 下一步**
249
266
 
250
- - [ ] 分组卡片截图与 30 秒演示 GIF(欢迎贡献录屏)
267
+ - [x] 截图四张:总览 / 组卡 / 审计与诊断 / 特别组草稿(见 [docs/screenshots](./docs/screenshots))
268
+ - [ ] 30 秒演示 GIF(欢迎贡献:录你自己机器上的演示;我们不录,整屏会带出无关窗口)
269
+ - [ ] **认知帷幕**:受管路径的读取不直接回原文,只回字段摘要或打码内容(群友提的方向;比目录权限更接近「少看一点」)
251
270
  - [ ] `gate export --csv`:把审计日志导出成表格
252
271
  - [ ] 临时授权:给某对象一个到期时间,到点自动回到 `deny`
253
272
  - [ ] 状态文件并发锁(多窗口同时改不互相覆盖)
@@ -0,0 +1,56 @@
1
+ # v0.4.0 — 组类型说清楚,会话头就能挂组
2
+
3
+ > 这一版解决一个真实误解:**在「严格组」里把 E:\ 标成「禁」,不等于"只禁 E",而是"除了 E 全放、其它一律拒"** —— 正好相反。
4
+ > 0.4.0 把这件事写在界面上,并且给了退路。
5
+
6
+ ## 新增
7
+
8
+ **1. 组类型横幅:白名单模式 / 黑名单模式**
9
+
10
+ - 「白名单模式」(严格组):**只放清单里的;没列进组的,一律拒** —— 黄色
11
+ - 「黑名单模式」(宽松组):**只限清单里的;没列进组的,照常放行** —— 绿色
12
+ - 编辑区和每张模板卡片上都有,不用记、不用猜
13
+
14
+ **2. 一键互换类型**
15
+
16
+ 编辑区有「改成宽松组(只禁我选的,其余照常)」/「改成严格组(只放我选的,其余一律拒)」。
17
+ **只换「清单外怎么算」,不动清单本身。**
18
+
19
+ **3. 模板卡片写明谁在用它**
20
+
21
+ 每张模板卡片多一行:`成员 3 个 · 挂在 2 个会话:a1b2c3d4、e5f6g7h8`;没人挂时写「还没有会话挂它」。
22
+ ——一个组可以被多个会话共用,这件事以前只能在脑子里记。
23
+
24
+ **4. 会话头部直接挂组**
25
+
26
+ 会话标题栏那个「门禁」小按钮,点开就是当前会话的特别组列表,选中即生效、摘掉即恢复。
27
+ **不用重启**:挂/摘都走 `sessionOverrides`,每次工具调用现读状态。
28
+
29
+ **5. 空清单警示**
30
+
31
+ 严格组清单为空时,界面直接写「**清单为空 ⇒ 这档什么都拒(等同全封)**」——这是最容易一脚踩空的地方。
32
+
33
+ ## 修正
34
+
35
+ - 删掉一句错话:原文案「不在组里的目录不受影响」——这句只对**宽松组**成立,对严格组是反的。现在按组类型分写。
36
+ - 弹层底色:`--dsw-alias-bg-overlay` 是遮罩色不是卡片面,改用 `--dsw-alias-bg-layer-1` + 更深投影;行文字改主色。
37
+
38
+ ## 质量
39
+
40
+ - 客户端自测 **97/97**;六套件全绿(数据层 / 受管辖通道 / 策略档 / 策略档运行时 / 宿主路由 / 客户端)
41
+ - `node --check` 全部通过;`npm pack` 26 个文件、1.0 MB
42
+ - 四张真机截图进 `docs/screenshots/`
43
+
44
+ ## 装
45
+
46
+ ```bash
47
+ dsh plugin --profile desktop add dsh-isolation-gate
48
+ ```
49
+
50
+ 或 profile `link:` / `file:` 指向本地路径(见 README)。
51
+
52
+ ## 边界(不重复但要看清)
53
+
54
+ 它拦的是**经过 dsh 的工具调用**与**走 `bin/gate.mjs` 的通道**。
55
+ `pwsh` 里套一层脚本、别的进程直接读写磁盘、已经注入的上下文——都不在它的射程内。
56
+ 要更强,得上内核沙箱(Windows ACL / Seatbelt / bwrap),那是另一层的事。
Binary file
Binary file
@@ -2,8 +2,16 @@
2
2
 
3
3
  | 文件 | 内容 | 来源 |
4
4
  |---|---|---|
5
- | `01-overview.png` | 新界面总览:摘要条(受管 3 / 分组 0 / 工具门已开 / 名字 2·路径 1)+ v1 自动迁移提示 + 三个受管对象 + 分段控件 + 未入名单目录 | 户主真机截图(2026-10-21 19:0x,重启后) |
6
- | `02-group.png` | 「harness 隔离」组卡片:2 个成员、组头分段控件、组内统一「禁」 | **待补**(需在门禁页刷新后截图;户主已确认界面有此组) |
7
- | `03-picker-fallback.png` | 目录选择器不可用时的退路:未入名单一层目录 + 路径输入 | **待补**(当前宿主 `picker=false`) |
5
+ | `01-overview.png` | 新界面总览:摘要条(受管 3 / 分组 0 / 工具门已开 / 名字 2·路径 1)+ v1 自动迁移提示 + 三个受管对象 + 分段控件 + 未入名单目录 | 户主真机截图(2026-10-21 19:0x) |
6
+ | `02-group.png` | 「harness 隔离」组卡片:3 个成员(Zcode / opencode / D:\仓库\合作档案)、组头三档控件、组内统一「禁」,每行仍可单独切档 | 真机截图(2026-10-23 16:1x) |
7
+ | `04-audit.png` | 「审计与诊断」页:磁盘差异五项计数 + 审计账本(只记判定,不记内容) | 真机截图(2026-10-23 16:0x) |
8
+ | `05-special-profile.png` | 「特别门禁」编辑模板草稿:**白名单模式横幅**(只放清单里的;没列进组的,一律拒)+ 级别说明 + 一键「改成宽松组」+ 空清单警示(等同全封) | 真机截图(2026-10-23 17:0x) |
8
9
 
9
10
  > 截图只用于文档;不含任何受管目录内部内容。
11
+
12
+ ## 两份不做的图,及原因
13
+
14
+ | 文件 | 为什么不做 |
15
+ |---|---|
16
+ | `03-picker-fallback.png` | 当前宿主 `picker=true`,系统的目录选择器可用 ⇒ 退路界面根本不会出现。硬凑就是假图。 |
17
+ | `demo.gif` | 30 秒整屏录屏会带出会话列表与其它窗口(隐私),为一张 GIF 不值。**欢迎贡献**:录你自己机器上的演示,说明用哪几个动作即可。 |