dsh-perm-gate 2.1.0 → 2.6.1
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.ja.md +108 -0
- package/CHANGELOG.ko.md +104 -0
- package/CHANGELOG.md +347 -0
- package/INSTALL.ja.md +84 -8
- package/INSTALL.ko.md +80 -9
- package/INSTALL.md +83 -8
- package/INSTALL.zh.md +70 -7
- package/README.ja.md +130 -16
- package/README.ko.md +128 -17
- package/README.md +179 -25
- package/README.zh.md +121 -12
- package/assets/allow1.png +0 -0
- package/assets/allow2.png +0 -0
- package/assets/gateoff.png +0 -0
- package/assets/llms.png +0 -0
- package/assets/permlist.png +0 -0
- package/assets/setting-head.png +0 -0
- package/assets/setting-rules.png +0 -0
- package/cordis.patch.yml +37 -3
- package/docs/baseline-guard.md +56 -0
- package/docs/plan-rules-to-settings.md +217 -0
- package/docs/rules-format.en.md +294 -0
- package/docs/rules-format.md +279 -0
- package/docs/ui-gap-analysis-and-branch-permissions.md +338 -0
- package/dsh.plugin.json +22 -0
- package/examples/permissions.example.yaml +16 -1
- package/lib/agent-identity.d.ts +43 -0
- package/lib/agent-identity.js +95 -0
- package/lib/allowlist.d.ts +27 -0
- package/lib/allowlist.js +103 -1
- package/lib/cli.js +11 -14
- package/lib/client.js +598 -6
- package/lib/client.js.map +1 -1
- package/lib/command-dispatcher.d.ts +22 -0
- package/lib/command-dispatcher.js +97 -0
- package/lib/command-semantics.d.ts +62 -0
- package/lib/command-semantics.js +6 -0
- package/lib/compiler.d.ts +50 -0
- package/lib/compiler.js +122 -0
- package/lib/config.d.ts +161 -7
- package/lib/config.js +78 -11
- package/lib/dry-run.d.ts +87 -0
- package/lib/dry-run.js +110 -0
- package/lib/engine.js +61 -4
- package/lib/evaluate.d.ts +9 -0
- package/lib/evaluate.js +264 -1
- package/lib/events.d.ts +69 -2
- package/lib/events.js +123 -0
- package/lib/index.d.ts +1 -1
- package/lib/index.js +320 -11
- package/lib/network-lifecycle.d.ts +107 -0
- package/lib/network-lifecycle.js +180 -0
- package/lib/network.d.ts +109 -0
- package/lib/network.js +196 -0
- package/lib/parsers/git.d.ts +43 -0
- package/lib/parsers/git.js +404 -0
- package/lib/parsers/shell-cmds.d.ts +32 -0
- package/lib/parsers/shell-cmds.js +293 -0
- package/lib/preset.d.ts +13 -0
- package/lib/preset.js +17 -0
- package/lib/proxy.d.ts +160 -0
- package/lib/proxy.js +529 -0
- package/lib/risk.d.ts +17 -3
- package/lib/risk.js +19 -6
- package/lib/rule-chain.d.ts +39 -0
- package/lib/rule-chain.js +213 -0
- package/lib/rule-dims.d.ts +174 -0
- package/lib/rule-dims.js +230 -0
- package/lib/rule.d.ts +60 -0
- package/lib/rule.js +69 -7
- package/lib/rules-view.d.ts +52 -0
- package/lib/rules-view.js +126 -0
- package/lib/runtime.d.ts +208 -7
- package/lib/runtime.js +433 -27
- package/lib/session-sweep.d.ts +45 -0
- package/lib/session-sweep.js +153 -0
- package/lib/shadow.d.ts +34 -0
- package/lib/shadow.js +143 -0
- package/lib/shell.d.ts +14 -0
- package/lib/shell.js +17 -0
- package/lib/watch.d.ts +42 -0
- package/lib/watch.js +170 -0
- package/package.json +26 -3
- package/scripts/patch-permission-glyph.mjs +392 -0
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
# 规则文件格式规范(`permissions` YAML)
|
|
2
|
+
|
|
3
|
+
> **权威性**:本文件的维度清单与 `src/rule.ts:181` 的 `VALID_KEYS` **逐字一致**。
|
|
4
|
+
> 两者任一变更必须同步;`test/rule.spec.ts` 与 `test/rule-dims.spec.ts` 是运行时护栏。
|
|
5
|
+
> 适用版本:`dsh-perm-gate` **2.4.1**(DSH ≥ 0.1.2-alpha.1)。
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 1. 文件位置与加载
|
|
10
|
+
|
|
11
|
+
| 项 | 说明 |
|
|
12
|
+
|---|---|
|
|
13
|
+
| 默认路径 | `<dataDir>/rules.yml`(`dataDir` 由 `config.resolveDataDir` 解析,默认 `$DSH_HOME/perm-gate/`) |
|
|
14
|
+
| 显式指定 | `config.rulesFile` |
|
|
15
|
+
| 规则链 | 支持多文件链(`rulesChain` / `searchUp` / `fallbackPath` / `badFilePolicy` / `maxRules`),见 `src/rule-chain.ts` |
|
|
16
|
+
| 热重载 | 由 `src/watch.ts`(chokidar)监听生效规则文件,防抖后链级重编译 |
|
|
17
|
+
| 解析失败 | **fail loud**——规则非法直接报错,绝不静默停用门控 |
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 2. 顶层结构
|
|
22
|
+
|
|
23
|
+
```yaml
|
|
24
|
+
defaultAction: ask # 可选;无任何规则命中时的动作。缺省 = ask
|
|
25
|
+
deny: # 动作分区(数组)
|
|
26
|
+
- <规则条目>
|
|
27
|
+
allow:
|
|
28
|
+
- <规则条目>
|
|
29
|
+
ask:
|
|
30
|
+
- <规则条目>
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
**动作即分区**:条目本身不带 action 字段(写了必须与所在分区一致)。求值顺序固定为
|
|
34
|
+
**deny → allow → ask → defaultAction**,首次匹配胜出(deny wins over allow)。
|
|
35
|
+
黑名单关键词层排在 deny 之前。
|
|
36
|
+
|
|
37
|
+
`defaultAction` 取值:`allow` | `ask` | `deny`(缺省 `ask`)。
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## 3. 规则条目可用字段(`VALID_KEYS`)
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
tools · command · args · paths · params · absent · agents · when · argv · network · branch
|
|
45
|
+
action · reason · enabled
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
| 字段 | 类型 | 作用 |
|
|
49
|
+
|---|---|---|
|
|
50
|
+
| `tools` | 字符串 / 字符串数组 | 工具名 glob |
|
|
51
|
+
| `command` | 字符串 / 字符串数组 | 命令词,支持 `word#recursive` / `word#force` 后缀 |
|
|
52
|
+
| `args` | 字符串 / 字符串数组 | 参数 token 扫描 |
|
|
53
|
+
| `paths` | 字符串 / 字符串数组 | 工作区相对路径 glob |
|
|
54
|
+
| `params` | 映射 | 参数键 → 值 glob 列表 |
|
|
55
|
+
| `absent` | 字符串 / 字符串数组 | 必须**不存在**的参数键 |
|
|
56
|
+
| `agents` | 字符串 / 字符串数组 | `main` / `subagent` / `preset:<name>` |
|
|
57
|
+
| `when` | 映射 | 环境与平台条件 |
|
|
58
|
+
| `argv` | 映射 | 额外 argv 模式(`pipeline`) |
|
|
59
|
+
| `network` | 映射 | 域名 / IP / 端口 / scheme |
|
|
60
|
+
| `branch` | 映射 | git 分支 / 远程 / 保护分支(仅对 git 命令生效) |
|
|
61
|
+
| `action` | `allow`\|`ask`\|`deny` | 可选;写了必须与所在分区一致 |
|
|
62
|
+
| `reason` | 字符串 | 人类可读理由(进入事件与 UI) |
|
|
63
|
+
| `enabled` | 布尔 | 设 `false` 则跳过该条目 |
|
|
64
|
+
|
|
65
|
+
**跨维度逻辑 = AND**:一条规则内出现的**多个维度必须全部满足**;
|
|
66
|
+
**维度内 = OR**:某个维度的列表里任一模式命中即可(`params`/`when`/`network` 的子键为 AND,见各节)。
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## 4. 逐维语义
|
|
71
|
+
|
|
72
|
+
### 4.1 `tools` — 工具名
|
|
73
|
+
```yaml
|
|
74
|
+
- tools: [shell, pwsh, bash]
|
|
75
|
+
```
|
|
76
|
+
Glob 匹配工具名。列表内 OR。
|
|
77
|
+
|
|
78
|
+
### 4.2 `command` — 命令词
|
|
79
|
+
```yaml
|
|
80
|
+
- command: [rm#recursive, rm#force]
|
|
81
|
+
```
|
|
82
|
+
命令词来自 argv 分解(`src/shell.ts`)。后缀 `#recursive` / `#force` 分别要求递归 / 强制语义。
|
|
83
|
+
列表内 OR。
|
|
84
|
+
|
|
85
|
+
### 4.3 `args` — 参数 token
|
|
86
|
+
```yaml
|
|
87
|
+
- args: ["--force", "-rf"]
|
|
88
|
+
```
|
|
89
|
+
对分解后的 token 集合做扫描。**⚠ 语义为 OR**:任意一个 token 匹配任意一个模式即命中。
|
|
90
|
+
它**无法**要求「`push` 与 `--force` 同时存在」,也无法区分
|
|
91
|
+
`git push --force origin main` 与 `git checkout --force main`——需要这种区分时用 `params` 或(未来的)`branch` 维度。
|
|
92
|
+
|
|
93
|
+
### 4.4 `paths` — 路径
|
|
94
|
+
```yaml
|
|
95
|
+
- paths: ["src/**", "!src/vendor/**"]
|
|
96
|
+
```
|
|
97
|
+
工作区相对路径 glob;`!` 前缀取反。列表内 OR。
|
|
98
|
+
|
|
99
|
+
### 4.5 `params` — 键定向匹配(**AND over keys**)
|
|
100
|
+
```yaml
|
|
101
|
+
- params:
|
|
102
|
+
command: ["*--force*", "!*--dry-run*"]
|
|
103
|
+
flags.mode: ["production"]
|
|
104
|
+
```
|
|
105
|
+
- 每个键的**实际值**必须匹配该键模式列表中的**至少一个**(列表内 OR)
|
|
106
|
+
- **所有列出的键都必须满足**(键之间 AND)
|
|
107
|
+
- 单个 `!` 前缀模式 = 取反(值必须**不**匹配其余部分)
|
|
108
|
+
- 空模式列表 = 该键只需存在(任意值)
|
|
109
|
+
|
|
110
|
+
这是表达复合条件的主要手段(`args` 做不到的 AND 需求在这里做)。
|
|
111
|
+
|
|
112
|
+
### 4.6 `absent` — 键必须缺失(AND)
|
|
113
|
+
```yaml
|
|
114
|
+
- absent: [dry_run, force]
|
|
115
|
+
```
|
|
116
|
+
所列参数键必须**全部不存在**。
|
|
117
|
+
|
|
118
|
+
### 4.7 `agents` — 代理身份
|
|
119
|
+
```yaml
|
|
120
|
+
- agents: ["main", "preset:reviewer"]
|
|
121
|
+
```
|
|
122
|
+
合法取值:`main`、`subagent`、`preset:<name>`(大小写不敏感)。非法值解析期报错。
|
|
123
|
+
候选集由 `src/agent-identity.ts` 从会话 header 提取。列表内 OR。
|
|
124
|
+
|
|
125
|
+
### 4.8 `when` — 环境与平台(AND)
|
|
126
|
+
```yaml
|
|
127
|
+
- when:
|
|
128
|
+
env:
|
|
129
|
+
NODE_ENV: ["production"]
|
|
130
|
+
CI: ["true", "1"]
|
|
131
|
+
platform: [linux, win32]
|
|
132
|
+
```
|
|
133
|
+
- `env`:键 → 允许值列表(**列表内 OR**,**键之间 AND**)
|
|
134
|
+
- `platform`:Node.js `process.platform` 取值
|
|
135
|
+
- `nodeVersion`:保留字段(尚未参与判定)
|
|
136
|
+
|
|
137
|
+
### 4.9 `argv` — 额外 argv 模式
|
|
138
|
+
```yaml
|
|
139
|
+
- argv:
|
|
140
|
+
pipeline: ["curl*|sh", "wget*|bash"]
|
|
141
|
+
```
|
|
142
|
+
`pipeline` 匹配**整条管道字符串**:各简单命令的**完整 argv**(命令词 + 参数 + 重定向目标)以 `|` 连接,
|
|
143
|
+
例如 `curl https://x.sh | sh` 的匹配串是 `curl https://x.sh|sh`。
|
|
144
|
+
|
|
145
|
+
**⚠ 模式里的 `|` 是字面量**,所以 `curl|sh` 只匹配参数恰好为空的紧邻形式;要覆盖带参数的常见写法必须写
|
|
146
|
+
`curl*|sh`。(历史缺陷:匹配串曾只取命令词,`curl|sh` 因此对上的是无害的 `curl|sh`,而真正危险的
|
|
147
|
+
`curl https://x.sh | sh` 反而匹配不到。)
|
|
148
|
+
|
|
149
|
+
### 4.10 `network` — 网络目标(子维度 AND)
|
|
150
|
+
```yaml
|
|
151
|
+
- network:
|
|
152
|
+
domains: ["*.internal.corp", "github.com"]
|
|
153
|
+
ips: ["10.0.0.0/8", "192.168.1.1"]
|
|
154
|
+
ports: ["443", "8000-9000"]
|
|
155
|
+
schemes: [https, http]
|
|
156
|
+
```
|
|
157
|
+
- 出现的**每个子维度都必须匹配**(子维度之间 AND);每个子维度内部 OR
|
|
158
|
+
- `domains` 支持 glob;`ips` 支持 CIDR;`ports` 支持区间;`schemes` 为协议名
|
|
159
|
+
|
|
160
|
+
网络维度有两个生效面:`tools/pre-execute` 的静态 URL 候选检查,以及回环代理拦截真实出站流量
|
|
161
|
+
(`network.enabled` **默认 false**,opt-in)。
|
|
162
|
+
|
|
163
|
+
### 4.11 `branch` — git 分支 / 远程 / 保护分支(子维度 AND)
|
|
164
|
+
|
|
165
|
+
```yaml
|
|
166
|
+
- command: [git]
|
|
167
|
+
args: [push]
|
|
168
|
+
branch:
|
|
169
|
+
target: [main, master, "release*"]
|
|
170
|
+
remote: [origin]
|
|
171
|
+
shared: true
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
| 子字段 | 语义 |
|
|
175
|
+
|---|---|
|
|
176
|
+
| `target` | 分支名 glob(`*` **跨** `/`,所以 `release*` 能匹配 `release/1.0`) |
|
|
177
|
+
| `remote` | 远程名 glob |
|
|
178
|
+
| `shared` | 要求目标分支是**保护分支** |
|
|
179
|
+
|
|
180
|
+
**为什么需要这个维度**:`args` 是 OR 语义(§4.3),它能问「`--force` 出现了吗」,但永远问不出
|
|
181
|
+
「`--force` 是否用在**推送到保护分支**上」。决定性用例是区分两条 token 完全相同的命令:
|
|
182
|
+
|
|
183
|
+
- `git push --force origin main` → 危险,应命中
|
|
184
|
+
- `git checkout --force main` → 日常操作,**不**应命中
|
|
185
|
+
|
|
186
|
+
候选来自统一的命令分类器(`src/command-dispatcher.ts` + `src/parsers/git.ts`),因此
|
|
187
|
+
refspec 形式(`HEAD:main`)已被拆解、远程名不会被误当成分支名、标志位也已归一。
|
|
188
|
+
|
|
189
|
+
**三条必须知道的边界**:
|
|
190
|
+
|
|
191
|
+
1. **只对 git 命令生效**。非 git 命令(`rm -rf`、`npm install`…)不满足任何 `branch` 子维度,
|
|
192
|
+
因此带 `branch` 的规则对它们**不匹配**(不会抛错)——这类命令由写路径层与黑名单层负责。
|
|
193
|
+
2. **`shared` 是静态判定**,来自解析器的保护分支清单
|
|
194
|
+
(`src/parsers/git.ts` 的 `PROTECTED_BRANCHES`:`main` / `master` / `production` / `release` / `stable`)。
|
|
195
|
+
它**不会**执行 `git branch -r --contains` 之类查询——那会把子进程放上门控决策路径。
|
|
196
|
+
注意 `shared` 与 `target` 是**并列且各自独立**的子维度:写了 `target: ["release*"]` 时
|
|
197
|
+
`release/1.0` 满足 `target`,但不满足 `shared`(保护清单是精确匹配)。
|
|
198
|
+
3. **子维度之间 AND,各自内部 OR**;多个简单命令(管道/`&&`)任一满足即可。
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
## 5. 完整示例
|
|
203
|
+
|
|
204
|
+
```yaml
|
|
205
|
+
defaultAction: ask
|
|
206
|
+
|
|
207
|
+
deny:
|
|
208
|
+
# 凭据材料外泄(P0 层另有硬拒绝兜底)
|
|
209
|
+
- tools: [shell, pwsh]
|
|
210
|
+
params:
|
|
211
|
+
command: ["*AWS_SECRET*", "*PRIVATE KEY*"]
|
|
212
|
+
reason: "credential material in command"
|
|
213
|
+
|
|
214
|
+
# 管道下载即执行
|
|
215
|
+
- argv:
|
|
216
|
+
pipeline: ["curl*|sh", "wget*|bash"]
|
|
217
|
+
reason: "piped download-and-execute"
|
|
218
|
+
|
|
219
|
+
# 推送到保护分支(args 做不到的区分:同一批 token 的 checkout 不命中)
|
|
220
|
+
- command: [git]
|
|
221
|
+
args: [push]
|
|
222
|
+
branch:
|
|
223
|
+
target: [main, master, "release*"]
|
|
224
|
+
shared: true
|
|
225
|
+
reason: "push to a protected branch"
|
|
226
|
+
|
|
227
|
+
# 生产环境下的强制删除
|
|
228
|
+
- command: [rm#recursive, rm#force]
|
|
229
|
+
when:
|
|
230
|
+
env:
|
|
231
|
+
NODE_ENV: ["production"]
|
|
232
|
+
reason: "recursive force delete in production"
|
|
233
|
+
|
|
234
|
+
allow:
|
|
235
|
+
# 常见只读查询
|
|
236
|
+
- tools: [read, grep, glob]
|
|
237
|
+
reason: "read-only workspace queries"
|
|
238
|
+
|
|
239
|
+
# 常见开发域名
|
|
240
|
+
- network:
|
|
241
|
+
domains: [github.com, "*.githubusercontent.com"]
|
|
242
|
+
schemes: [https]
|
|
243
|
+
reason: "common dev hosts"
|
|
244
|
+
|
|
245
|
+
ask:
|
|
246
|
+
# 推送需确认(注意:args 是 OR,这里只要求出现 push)
|
|
247
|
+
- command: [git]
|
|
248
|
+
args: ["push"]
|
|
249
|
+
reason: "git push requires confirmation"
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
同一份示例也随包分发:`examples/permissions.example.yaml`。
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
## 6. 与 CLI dry-run 配合
|
|
257
|
+
|
|
258
|
+
改完规则**先干跑再上线**:
|
|
259
|
+
|
|
260
|
+
```powershell
|
|
261
|
+
# 列出已编译规则(默认 ask 分区、规则条数、permissive 策略快照)
|
|
262
|
+
dsh-perm-gate --rules permissions.yaml --list
|
|
263
|
+
|
|
264
|
+
# 判定一次调用(--args 是 JSON;参数较短时直接内联)
|
|
265
|
+
dsh-perm-gate --rules permissions.yaml --tool read --args "{\"file_path\":\"src/index.ts\"}"
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
输出为 JSON:`decision`(`allow` / `ask` / `deny`)、`reason`、`audited`。
|
|
269
|
+
|
|
270
|
+
**两个必须知道的坑**:
|
|
271
|
+
|
|
272
|
+
1. **黑名单先于规则命中**。`deny-keyword` 层跑在 `deny:` 分区之前,
|
|
273
|
+
`rm -rf` / `push --force` 这类预设词会在你的规则之前就把调用拦下——
|
|
274
|
+
dry-run 输出里的 `deny-keyword:` 前缀就是它在起作用,不是你的规则。
|
|
275
|
+
2. **带引号的长参数在 PowerShell 下会被二次解析**(可能报 `--args must be valid JSON`)。
|
|
276
|
+
稳妥做法:写进临时文件后用 `--args (Get-Content -Raw args.json)`,
|
|
277
|
+
或直接依赖 `test/rule-dims.spec.ts` 与 `test/evaluate.spec.ts` 的维度覆盖。
|
|
278
|
+
|
|
279
|
+
CLI 与运行时共用同一判定引擎(`evaluate.ts` + `engine.ts`),输出即真实裁决。
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
# dsh-perm-gate UI 缺口分析与分支级权限可行性
|
|
2
|
+
|
|
3
|
+
> **日期**: 2026-09-16
|
|
4
|
+
> **状态**: 草案
|
|
5
|
+
> **相关讨论**: PerryLink dsh-auto-review 对比分析
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 一、现状:后端能力完整,前端 UI 缺失
|
|
10
|
+
|
|
11
|
+
### 后端已支持的完整 YAML 规则格式
|
|
12
|
+
|
|
13
|
+
`rules.yml` 支持 10 个维度的分层权限配置:
|
|
14
|
+
|
|
15
|
+
```yaml
|
|
16
|
+
permissions:
|
|
17
|
+
deny:
|
|
18
|
+
- tools: [shell, pwsh]
|
|
19
|
+
command: [rm#recursive, rm#force]
|
|
20
|
+
reason: "destructive system command"
|
|
21
|
+
|
|
22
|
+
- args: ["--force"]
|
|
23
|
+
level: deny
|
|
24
|
+
reason: "Force flag detected"
|
|
25
|
+
|
|
26
|
+
allow:
|
|
27
|
+
- command: [grep, glob, read]
|
|
28
|
+
reason: "permissive allowlist"
|
|
29
|
+
|
|
30
|
+
- network:
|
|
31
|
+
domains: [github.com, api.github.com]
|
|
32
|
+
reason: "common dev hosts"
|
|
33
|
+
|
|
34
|
+
ask:
|
|
35
|
+
- command: [git]
|
|
36
|
+
args: ["push"]
|
|
37
|
+
reason: "Git push requires confirmation"
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**VALID_KEYS**: `tools`, `command`, `args`, `paths`, `params`, `absent`, `agents`, `when`, `argv`, `network`, `action`, `reason`, `enabled`
|
|
41
|
+
|
|
42
|
+
### 前端 UI 面板实际暴露的内容
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
┌─────────────────────────────────────────────┐
|
|
46
|
+
│ 自动审查 (permissive) 开关 │
|
|
47
|
+
├─────────────────────────────────────────────┤
|
|
48
|
+
│ 策略选项 │
|
|
49
|
+
│ ☑ trustAutoAllow │
|
|
50
|
+
│ ☐ alwaysConfirm │
|
|
51
|
+
│ ☐ llmAssist │
|
|
52
|
+
├─────────────────────────────────────────────┤
|
|
53
|
+
│ 允许列表 (allowlist) │
|
|
54
|
+
│ [简单字符串列表,支持批量 textarea 编辑] │
|
|
55
|
+
│ ⚠️ 只支持前缀匹配,不支持 YAML 分层规则 │
|
|
56
|
+
├─────────────────────────────────────────────┤
|
|
57
|
+
│ 拒绝关键词 (denyKeywords) │
|
|
58
|
+
│ [同样只支持简单字符串列表] │
|
|
59
|
+
└─────────────────────────────────────────────┘
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### 缺失的 UI 能力
|
|
63
|
+
|
|
64
|
+
| 功能 | 后端支持 | UI 支持 | 状态 |
|
|
65
|
+
|------|---------|---------|------|
|
|
66
|
+
| YAML 规则编辑器 | ✅ | ❌ | 需开发 |
|
|
67
|
+
| 维度选择器 (tools/command/args/network 等) | ✅ | ❌ | 需开发 |
|
|
68
|
+
| 规则级别选择 (deny/allow/ask) | ✅ | ❌ | 需开发 |
|
|
69
|
+
| 复合条件编辑 (AND/OR 逻辑) | ✅ | ❌ | 需开发 |
|
|
70
|
+
| 规则测试/预览 | ✅ (CLI dry-run) | ❌ | 需开发 |
|
|
71
|
+
| 规则导入/导出 | ❌ | ❌ | 需开发 |
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## 二、UI 补全计划草案
|
|
76
|
+
|
|
77
|
+
### Phase 1: 最小可行方案 (MVP)
|
|
78
|
+
|
|
79
|
+
**目标**: 在 UI 中暴露完整的 YAML 规则编辑能力
|
|
80
|
+
|
|
81
|
+
#### 1.1 YAML 编辑器集成
|
|
82
|
+
|
|
83
|
+
- 集成 Monaco Editor 或 CodeMirror 支持 YAML 语法高亮
|
|
84
|
+
- 支持 `rules.yml` 文件的实时编辑和保存
|
|
85
|
+
- 添加 YAML 语法校验和错误提示
|
|
86
|
+
- 添加规则格式校验(维度名称、值类型等)
|
|
87
|
+
|
|
88
|
+
#### 1.2 规则模板系统
|
|
89
|
+
|
|
90
|
+
- 提供常用规则模板(git push、文件删除、网络访问等)
|
|
91
|
+
- 支持从模板快速创建规则
|
|
92
|
+
- 模板库可扩展
|
|
93
|
+
|
|
94
|
+
#### 1.3 规则测试功能
|
|
95
|
+
|
|
96
|
+
- 添加"测试规则"按钮
|
|
97
|
+
- 输入命令字符串,预览规则匹配结果
|
|
98
|
+
- 显示哪些维度匹配、哪些不匹配
|
|
99
|
+
- 复用 CLI dry-run 逻辑
|
|
100
|
+
|
|
101
|
+
### Phase 2: 可视化规则编辑器
|
|
102
|
+
|
|
103
|
+
**目标**: 提供图形化的规则创建界面,降低 YAML 编写门槛
|
|
104
|
+
|
|
105
|
+
#### 2.1 维度化规则编辑器
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
┌─────────────────────────────────────────────┐
|
|
109
|
+
│ 新建规则 │
|
|
110
|
+
├─────────────────────────────────────────────┤
|
|
111
|
+
│ 规则级别: [▼ deny] │
|
|
112
|
+
│ 原因: [输入框] │
|
|
113
|
+
├─────────────────────────────────────────────┤
|
|
114
|
+
│ 匹配条件 (AND 逻辑): │
|
|
115
|
+
│ [+ 添加条件] │
|
|
116
|
+
│ │
|
|
117
|
+
│ ┌─────────────────────────────────────┐ │
|
|
118
|
+
│ │ 维度: [▼ command] 模式: [git] │ │
|
|
119
|
+
│ │ 删除 │ │
|
|
120
|
+
│ └─────────────────────────────────────┘ │
|
|
121
|
+
│ │
|
|
122
|
+
│ ┌─────────────────────────────────────┐ │
|
|
123
|
+
│ │ 维度: [▼ args] 模式: [--force] │ │
|
|
124
|
+
│ │ 删除 │ │
|
|
125
|
+
│ └─────────────────────────────────────┘ │
|
|
126
|
+
│ │
|
|
127
|
+
│ ┌─────────────────────────────────────┐ │
|
|
128
|
+
│ │ 维度: [▼ network] 域名: [example.*] │ │
|
|
129
|
+
│ │ 删除 │ │
|
|
130
|
+
│ └─────────────────────────────────────┘ │
|
|
131
|
+
├─────────────────────────────────────────────┤
|
|
132
|
+
│ [预览 YAML] [保存规则] │
|
|
133
|
+
└─────────────────────────────────────────────┘
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
#### 2.2 规则分组管理
|
|
137
|
+
|
|
138
|
+
- 支持按用途分组(安全规则、工作流规则等)
|
|
139
|
+
- 支持规则启用/禁用开关
|
|
140
|
+
- 支持规则排序(优先级)
|
|
141
|
+
|
|
142
|
+
#### 2.3 规则冲突检测
|
|
143
|
+
|
|
144
|
+
- 检测规则间的冲突(deny vs allow)
|
|
145
|
+
- 提示规则冗余(被更宽泛的规则覆盖)
|
|
146
|
+
- 建议规则优化
|
|
147
|
+
|
|
148
|
+
### Phase 3: 高级功能
|
|
149
|
+
|
|
150
|
+
**目标**: 提供生产级规则管理能力
|
|
151
|
+
|
|
152
|
+
#### 3.1 规则版本控制
|
|
153
|
+
|
|
154
|
+
- 规则变更历史
|
|
155
|
+
- 支持回滚到历史版本
|
|
156
|
+
- 与 Git 集成(rules.yml 版本追踪)
|
|
157
|
+
|
|
158
|
+
#### 3.2 规则导入/导出
|
|
159
|
+
|
|
160
|
+
- 支持从其他 perm-gate 实例导入规则
|
|
161
|
+
- 支持导出规则包
|
|
162
|
+
- 支持从模板库导入
|
|
163
|
+
|
|
164
|
+
#### 3.3 规则审计日志
|
|
165
|
+
|
|
166
|
+
- 记录规则创建/修改/删除操作
|
|
167
|
+
- 记录规则匹配历史
|
|
168
|
+
- 支持审计日志查询
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## 三、分支级权限可行性分析
|
|
173
|
+
|
|
174
|
+
### 当前 YAML 能否实现 git push 分支级权限?
|
|
175
|
+
|
|
176
|
+
#### 3.1 args 维度的匹配逻辑
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
// evaluate.ts 第 76-81 行
|
|
180
|
+
// args dimension: any token matches any args glob.
|
|
181
|
+
if (rule.args.length > 0) {
|
|
182
|
+
const tokens: string[] = []
|
|
183
|
+
for (const cmd of commands) tokens.push(...cmd.args, ...cmd.redirects)
|
|
184
|
+
tokens.push(...pathCands, ...urlCands)
|
|
185
|
+
if (!tokens.some((tok) => rule.args.some((g) => g.re.test(tok)))) return false
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
**关键发现:args 是 OR 逻辑,不是 AND 逻辑。**
|
|
190
|
+
|
|
191
|
+
对于 `git push --force origin main`:
|
|
192
|
+
- tokens = [`push`, `--force`, `origin`, `main`]
|
|
193
|
+
- args 匹配规则:只要**任意一个 token** 匹配**任意一个 glob 模式**,规则就生效
|
|
194
|
+
|
|
195
|
+
#### 3.2 能做什么
|
|
196
|
+
|
|
197
|
+
```yaml
|
|
198
|
+
# ✅ 拦截所有带 --force 的命令(不管是不是 push)
|
|
199
|
+
- args: ["--force"]
|
|
200
|
+
level: deny
|
|
201
|
+
reason: "Force flag detected"
|
|
202
|
+
|
|
203
|
+
# ✅ 拦截所有涉及 main 分支的命令
|
|
204
|
+
- args: ["main"]
|
|
205
|
+
level: deny
|
|
206
|
+
reason: "Main branch target detected"
|
|
207
|
+
|
|
208
|
+
# ✅ 拦截所有 git push 命令
|
|
209
|
+
- command: [git]
|
|
210
|
+
args: ["push"]
|
|
211
|
+
level: confirm
|
|
212
|
+
reason: "Git push requires confirmation"
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
#### 3.3 不能做什么
|
|
216
|
+
|
|
217
|
+
```yaml
|
|
218
|
+
# ❌ 精确匹配 "git push --force origin main" 这个复合操作
|
|
219
|
+
# 因为 args 是 OR 逻辑,无法要求多个 token 同时存在
|
|
220
|
+
|
|
221
|
+
# ❌ 区分 "git push --force origin main" 和 "git checkout --force main"
|
|
222
|
+
# 两者都有 --force 和 main,无法区分
|
|
223
|
+
|
|
224
|
+
# ❌ 动态判断分支是"个人"还是"共享"
|
|
225
|
+
# YAML 是静态 pattern,没有 git 查询能力
|
|
226
|
+
|
|
227
|
+
# ❌ 匹配分支模式(如 release/*、feature/*)
|
|
228
|
+
# args glob 不支持路径段匹配(segments=false)
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
#### 3.4 结论
|
|
232
|
+
|
|
233
|
+
| 能力 | 状态 | 说明 |
|
|
234
|
+
|------|------|------|
|
|
235
|
+
| 拦截 force push | ⚠️ 部分可行 | 只能匹配 `--force` token,无法限定是 push 操作 |
|
|
236
|
+
| 拦截特定分支 | ⚠️ 部分可行 | 只能硬编码分支名,无法判断共享属性 |
|
|
237
|
+
| 复合条件匹配 | ❌ 不可行 | args 是 OR 逻辑,无法要求多个 token 同时存在 |
|
|
238
|
+
| 运行时 git 查询 | ❌ 不可行 | YAML 是静态 pattern,无法调用 git 命令 |
|
|
239
|
+
| 分支模式匹配 | ⚠️ 部分可行 | 可用 `release*` 匹配,但无法匹配路径段 |
|
|
240
|
+
|
|
241
|
+
### 3.5 要实现真正的分支级权限,需要
|
|
242
|
+
|
|
243
|
+
#### 方案 A: 新增 `branch` 维度
|
|
244
|
+
|
|
245
|
+
```yaml
|
|
246
|
+
permissions:
|
|
247
|
+
deny:
|
|
248
|
+
- branch:
|
|
249
|
+
target: "main|master|develop|release/*"
|
|
250
|
+
shared: true # 动态判断是否共享
|
|
251
|
+
reason: "Force push to shared branch"
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
**实现要求**:
|
|
255
|
+
- 运行时调用 `git branch -r --contains <branch>` 查询分支属性
|
|
256
|
+
- 查询 `git log --format='%ae' <branch>` 获取提交者信息
|
|
257
|
+
- 查询 `git config branch.<name>.remote` 获取远程仓库
|
|
258
|
+
- 缓存查询结果(避免重复调用)
|
|
259
|
+
- 添加超时机制(防止 git 命令卡住)
|
|
260
|
+
|
|
261
|
+
#### 方案 B: 改造 args 为 AND 逻辑
|
|
262
|
+
|
|
263
|
+
```yaml
|
|
264
|
+
permissions:
|
|
265
|
+
deny:
|
|
266
|
+
- args_match: AND # 新语法:要求所有条件同时满足
|
|
267
|
+
- "push"
|
|
268
|
+
- "--force"
|
|
269
|
+
- "origin"
|
|
270
|
+
- "main|master|develop"
|
|
271
|
+
reason: "Force push to shared branch"
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
**实现要求**:
|
|
275
|
+
- 修改 args 维度的匹配逻辑(从 OR 改为 AND)
|
|
276
|
+
- 添加新语法支持(`args_match: AND`)
|
|
277
|
+
- 保持向后兼容(旧的 `args` 保持 OR 逻辑)
|
|
278
|
+
|
|
279
|
+
#### 方案 C: 新增 `shared-branches` 配置
|
|
280
|
+
|
|
281
|
+
```yaml
|
|
282
|
+
# rules.yml 顶部配置
|
|
283
|
+
shared_branches:
|
|
284
|
+
- main
|
|
285
|
+
- master
|
|
286
|
+
- develop
|
|
287
|
+
- "release/*"
|
|
288
|
+
|
|
289
|
+
permissions:
|
|
290
|
+
deny:
|
|
291
|
+
- command: [git]
|
|
292
|
+
args: ["push", "--force"]
|
|
293
|
+
branch:
|
|
294
|
+
in: shared_branches # 引用预定义列表
|
|
295
|
+
reason: "Force push to shared branch"
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
**实现要求**:
|
|
299
|
+
- 新增 `shared_branches` 配置项
|
|
300
|
+
- 新增 `branch` 维度,支持 `in` 操作符引用列表
|
|
301
|
+
- 运行时解析分支名,匹配列表中的模式
|
|
302
|
+
|
|
303
|
+
---
|
|
304
|
+
|
|
305
|
+
## 四、建议的实施路径
|
|
306
|
+
|
|
307
|
+
### 短期 (1-2 周)
|
|
308
|
+
|
|
309
|
+
1. **Phase 1 UI 补全**: 集成 YAML 编辑器,暴露完整的规则编辑能力
|
|
310
|
+
2. **文档更新**: 明确说明 YAML 规则的完整能力边界
|
|
311
|
+
3. **临时方案**: 在文档中提供手动配置分支级权限的方法(硬编码分支名)
|
|
312
|
+
|
|
313
|
+
### 中期 (1-2 月)
|
|
314
|
+
|
|
315
|
+
1. **Phase 2 可视化编辑器**: 提供图形化规则创建界面
|
|
316
|
+
2. **args AND 逻辑**: 改造 args 维度,支持复合条件匹配
|
|
317
|
+
3. **规则测试功能**: 支持在 UI 中测试规则匹配结果
|
|
318
|
+
|
|
319
|
+
### 长期 (3-6 月)
|
|
320
|
+
|
|
321
|
+
1. **branch 维度**: 实现运行时 git 查询,支持动态分支判断
|
|
322
|
+
2. **规则版本控制**: 支持规则变更历史和回滚
|
|
323
|
+
3. **规则审计日志**: 记录规则匹配历史,支持审计
|
|
324
|
+
|
|
325
|
+
---
|
|
326
|
+
|
|
327
|
+
## 五、与 PerryLink dsh-auto-review 的对比总结
|
|
328
|
+
|
|
329
|
+
| 维度 | dsh-perm-gate | dsh-auto-review | 差距分析 |
|
|
330
|
+
|------|--------------|-----------------|----------|
|
|
331
|
+
| 规则格式 | YAML 静态规则 | cordis patch 配置 | 格式不同,无法直接兼容 |
|
|
332
|
+
| 匹配逻辑 | 模式匹配 (pattern) | AI 语义理解 | 各有优势:模式匹配快,语义理解准 |
|
|
333
|
+
| 分支权限 | 不支持动态判断 | 不支持(无分支维度) | 两者都需要新增能力 |
|
|
334
|
+
| UI 面板 | 简化接口,缺失 YAML 编辑器 | 完整配置面板 | 我们需要补全 UI |
|
|
335
|
+
| 配置入口 | settings.yaml | cordis.patch.yml | 入口不同,无法直接融合 |
|
|
336
|
+
| 执行速度 | < 10ms (静态匹配) | 3-60s (AI 审查) | 我们的速度优势明显 |
|
|
337
|
+
|
|
338
|
+
**核心结论**: 两者是互补关系,不是替代关系。perm-gate 的静态规则适合确定性拦截(速度优先),auto-review 的 AI 审查适合模糊场景(精度优先)。分支级权限是两者都缺失的能力,需要各自实现。
|
package/dsh.plugin.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "dsh-perm-gate",
|
|
3
|
+
"name": "dsh-perm-gate",
|
|
4
|
+
"version": "2.6.1",
|
|
5
|
+
"description": {
|
|
6
|
+
"en": "Deterministic-first, fail-closed permission gate for DeepSeek Harness 0.1.2+: a P0 hard-deny -> P1 precise session grant -> P2 static allow/deny rule chain -> P3 optional LLM classifier -> P4 ask pipeline over the tools/pre-execute waterfall, with a standalone CLI dry-run evaluator and an auditable decision log. It only vetoes with deny/ask and always delegates otherwise, and unknown or malformed rules fail loud instead of silently standing the gate down.",
|
|
7
|
+
"zh": "面向 DeepSeek Harness 0.1.2+ 的确定性优先、失败即拒的权限闸门:在 tools/pre-execute 瀑布上串起 P0 硬拒绝 → P1 精确会话授权 → P2 静态 allow/deny 规则链 → P3 可选 LLM 语义分类 → P4 ask,并附带独立 CLI 干跑评估器与可审计的决策日志。只以 deny/ask 否决、其余一律放行,规则未知或非法时直接报错,绝不静默失效。"
|
|
8
|
+
},
|
|
9
|
+
"homepage": "https://github.com/drscrewdriver/dsh-perm-gate",
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "https://github.com/drscrewdriver/dsh-perm-gate.git"
|
|
13
|
+
},
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"engines": {
|
|
16
|
+
"dsh": ">=0.1.2-alpha.1 <0.2.0-0"
|
|
17
|
+
},
|
|
18
|
+
"components": {
|
|
19
|
+
"host": "lib/index.js",
|
|
20
|
+
"client": "lib/client.js"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -9,8 +9,23 @@ permissions:
|
|
|
9
9
|
reason: no direct remote shell
|
|
10
10
|
- paths: [.dsh/**]
|
|
11
11
|
reason: protect harness metadata
|
|
12
|
+
# `command` matches the decomposed command WORD only (`git`), so the
|
|
13
|
+
# subcommand belongs in `args`. A multi-word entry such as `git pull` can
|
|
14
|
+
# never match — see docs/rules-format.md §4.2.
|
|
15
|
+
- command: [git]
|
|
16
|
+
args: [push]
|
|
17
|
+
branch:
|
|
18
|
+
target: [main, master, "release*"]
|
|
19
|
+
shared: true
|
|
20
|
+
reason: no push to a protected branch
|
|
12
21
|
allow:
|
|
13
|
-
- command: [
|
|
22
|
+
- command: [pnpm, node]
|
|
23
|
+
reason: routine project development
|
|
24
|
+
- command: [git]
|
|
25
|
+
args: [pull]
|
|
26
|
+
reason: routine project development
|
|
27
|
+
- command: [npm]
|
|
28
|
+
args: [run]
|
|
14
29
|
reason: routine project development
|
|
15
30
|
- command: [curl, wget]
|
|
16
31
|
args: ["https://*.example.com/*"]
|