dsh-permission-rules 0.5.4 → 0.5.5

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
@@ -2,6 +2,14 @@
2
2
 
3
3
  All notable changes to dsh-permission-rules are recorded here, newest first.
4
4
 
5
+ ## v0.5.5 — 2026-08-23
6
+
7
+ ### Docs
8
+
9
+ - Document the `network` match dimension in the rule-format references: `docs/rules-format.en.md` and `docs/rules-format.md` now list `network` among the allowed `match` fields, add a `network` dimension row plus a dedicated section (domains / ips / ports / schemes semantics, tool scoping, the mode/boundary cross-reference), and `docs/rules-format.schema.json` gains the `network` property so editor completion covers it. This closes the v0.5.0 gap where the implementation shipped but the vocabulary docs still listed only six `match` fields.
10
+ - The injected network-policy system-prompt paragraph now opens with a short role sentence (Minimal persona style), as the engineering standard requires.
11
+ - Refresh the five-language README Development section test count to the current `236 tests, 20 files` (was `139 tests, 9 suites`).
12
+
5
13
  ## v0.5.4 — 2026-08-22
6
14
 
7
15
  ### DeepSeek Harness rc.2 compatibility
package/README.es.md CHANGED
@@ -193,7 +193,7 @@ node scripts/repair-session-logs.mjs repair [--home DIR] [--dry-run]
193
193
  pnpm install # node ^22.19 || >=24
194
194
  pnpm run typecheck # tsc, src + tests
195
195
  pnpm run lint # eslint, src + tests + scripts
196
- pnpm test # vitest: 139 tests, 9 suites
196
+ pnpm test # vitest: 236 tests, 20 files
197
197
  pnpm run test:coverage # coverage gate (90/80/90/90)
198
198
  pnpm run build # tsc declarations + tsdown bundles (lib/)
199
199
  pnpm run pack:check # build + pack (the published artifact)
package/README.hi.md CHANGED
@@ -193,7 +193,7 @@ node scripts/repair-session-logs.mjs repair [--home DIR] [--dry-run]
193
193
  pnpm install # node ^22.19 || >=24
194
194
  pnpm run typecheck # tsc, src + tests
195
195
  pnpm run lint # eslint, src + tests + scripts
196
- pnpm test # vitest: 139 tests, 9 suites
196
+ pnpm test # vitest: 236 tests, 20 files
197
197
  pnpm run test:coverage # coverage gate (90/80/90/90)
198
198
  pnpm run build # tsc declarations + tsdown bundles (lib/)
199
199
  pnpm run pack:check # build + pack (the published artifact)
package/README.md CHANGED
@@ -194,7 +194,7 @@ node scripts/repair-session-logs.mjs repair [--home DIR] [--dry-run]
194
194
  pnpm install # node ^22.19 || >=24
195
195
  pnpm run typecheck # tsc, src + tests
196
196
  pnpm run lint # eslint, src + tests + scripts
197
- pnpm test # vitest: 139 tests, 9 suites
197
+ pnpm test # vitest: 236 tests, 20 files
198
198
  pnpm run test:coverage # coverage gate (90/80/90/90)
199
199
  pnpm run build # tsc declarations + tsdown bundles (lib/)
200
200
  pnpm run pack:check # build + pack (the published artifact)
package/README.pt.md CHANGED
@@ -193,7 +193,7 @@ node scripts/repair-session-logs.mjs repair [--home DIR] [--dry-run]
193
193
  pnpm install # node ^22.19 || >=24
194
194
  pnpm run typecheck # tsc, src + tests
195
195
  pnpm run lint # eslint, src + tests + scripts
196
- pnpm test # vitest: 139 tests, 9 suites
196
+ pnpm test # vitest: 236 tests, 20 files
197
197
  pnpm run test:coverage # coverage gate (90/80/90/90)
198
198
  pnpm run build # tsc declarations + tsdown bundles (lib/)
199
199
  pnpm run pack:check # build + pack (the published artifact)
package/README.zh.md CHANGED
@@ -193,7 +193,7 @@ node scripts/repair-session-logs.mjs repair [--home DIR] [--dry-run]
193
193
  pnpm install # node ^22.19 || >=24
194
194
  pnpm run typecheck # tsc, src + tests
195
195
  pnpm run lint # eslint, src + tests + scripts
196
- pnpm test # vitest: 139 tests, 9 suites
196
+ pnpm test # vitest: 236 tests, 20 files
197
197
  pnpm run test:coverage # coverage gate (90/80/90/90)
198
198
  pnpm run build # tsc declarations + tsdown bundles (lib/)
199
199
  pnpm run pack:check # build + pack (the published artifact)
@@ -17,7 +17,7 @@ rules: # rule list, evaluated in written order; may be omitted or empty
17
17
  reason: ...
18
18
  ```
19
19
 
20
- Only `rules` is allowed at the top level; each rule allows only `match`, `action`, `reason`, `enabled`, `description`, `tags`; each `match` allows only `tools`, `agents`, `params`, `paths`, `absent`, `when`. Any unknown field fails the load loudly — never silently ignored.
20
+ Only `rules` is allowed at the top level; each rule allows only `match`, `action`, `reason`, `enabled`, `description`, `tags`; each `match` allows only `tools`, `agents`, `params`, `paths`, `absent`, `when`, `network`. Any unknown field fails the load loudly — never silently ignored.
21
21
 
22
22
  ## match dimensions (AND)
23
23
 
@@ -29,9 +29,36 @@ Only `rules` is allowed at the top level; each rule allows only `match`, `action
29
29
  | `paths` | `string[]` | Workspace-relative path patterns; any candidate matching any pattern satisfies the dimension. Candidates come from argument values under these keys at ANY nesting depth (depth-capped): `path` `paths` `file` `files` `file_path` `dir` `directory` `directories` `cwd` `workspace` `root` `target` `targets` `output`. Absolute candidates outside the workspace are dropped; relative `../` forms are kept so explicit out-of-root globs still work. In path patterns `*` does not cross `/`; `**` crosses any depth (including zero). With `caseInsensitivePaths` on (Windows default) the root comparison and the patterns ignore ASCII case. |
30
30
  | `absent` | `string[]` | Argument keys that must be ABSENT; **every listed key must be missing** (non-object arguments satisfy this trivially). |
31
31
  | `when` | `{ env, platform }` | Host conditions: `env` is env-var name → pattern(s), **every listed var must be present AND match**; `platform` is any-of a closed list (`aix` `android` `darwin` `freebsd` `linux` `openbsd` `sunos` `win32`). |
32
+ | `network` | `{ domains, ips, ports, schemes }` | Network scope (a network rule): the call must carry a URL candidate satisfying every listed dimension. `domains` (subdomain-inclusive unless wildcarded), `ips` (literals/globs/CIDRs), `ports` (`*`, one port, or a range), `schemes` (`http`/`https`). See the next section. |
32
33
 
33
34
  All dimensions are ANDed: a rule matches only when **every non-empty dimension matches**. Rules evaluate in order — **the first match wins**; no match = passthrough.
34
35
 
36
+ ## network dimension
37
+
38
+ A rule whose `match` carries a `network` block is a **network rule**. It matches a tool call only when the call carries a URL candidate — a web-tool URL argument (`url`, `endpoint`, `webhook`, …) or a URL embedded in `bash`/`pwsh` command text — that satisfies every listed dimension (AND); at the proxy layer it matches the shell subprocess connection target directly. A rule WITHOUT `network` keeps its file/command behavior exactly as before.
39
+
40
+ | Field | Semantics |
41
+ |---|---|
42
+ | `domains` | Domain patterns, lowercased with a trailing dot stripped. A pattern without wildcards is subdomain-inclusive — `example.com` also matches `api.example.com`; `*.example.com` matches subdomains only; `*`/`**` compile as ordinary globs. ANY pattern matching the target host satisfies the dimension. |
43
+ | `ips` | Exact IPv4/IPv6 literal, a glob (`10.0.*.*`), or an IPv4 CIDR (`10.0.0.0/8`). A literal IP in the URL is always a candidate; the proxy additionally resolves hostnames and tests their addresses, while `tools/pre-execute` matches literal IPs only (no DNS in the hot path). ANY match satisfies the dimension. |
44
+ | `ports` | `*`, one port (`443`), or an inclusive range (`8000-9000`); numeric YAML ports are accepted. Evaluated against the effective port (URL port, else 80/443 by scheme). ANY match satisfies the dimension. |
45
+ | `schemes` | `http` and/or `https`; the target scheme must be one of them. |
46
+
47
+ A network block must name at least one of the four fields. Scope a network rule by tool with `tools: [bash, pwsh]` (shell traffic), `tools: [web_fetch, web_search]` (web tools), or leave `tools` empty to cover both. The three policy modes that decide unlisted targets (`deny-all` / `whitelist` / `allow-all`, with an `auto` mode mapped onto the sandbox presets) are plugin config, not rule syntax — see the README "Network policy" section.
48
+
49
+ ```yaml
50
+ rules:
51
+ # block one host for web tools; the shell proxy enforces the same for curl
52
+ - match: { tools: [web_fetch, web_search], network: { domains: [blocked.example] } }
53
+ action: deny
54
+ reason: "blocked.example is off-limits"
55
+
56
+ # allow only pinned package registries over https (whitelist-friendly rule)
57
+ - match: { tools: [bash, pwsh], network: { domains: ["registry.npmjs.org", "proxy.golang.org"], schemes: [https] } }
58
+ action: allow
59
+ reason: "pinned package registries"
60
+ ```
61
+
35
62
  ## action, reason, and metadata
36
63
 
37
64
  - `action: allow | deny | ask` (required).
@@ -17,7 +17,7 @@ rules: # 规则列表,按书写顺序求值;可以省略或为空
17
17
  reason: ...
18
18
  ```
19
19
 
20
- 顶层只允许 `rules` 一个键;每条规则只允许 `match`、`action`、`reason`、`enabled`、`description`、`tags` 六个键;`match` 只允许 `tools`、`agents`、`params`、`paths`、`absent`、`when` 六个键。出现任何未知键都在加载期报错(响亮失败),绝不静默忽略。
20
+ 顶层只允许 `rules` 一个键;每条规则只允许 `match`、`action`、`reason`、`enabled`、`description`、`tags` 六个键;`match` 只允许 `tools`、`agents`、`params`、`paths`、`absent`、`when`、`network` 七个键。出现任何未知键都在加载期报错(响亮失败),绝不静默忽略。
21
21
 
22
22
  ## match 维度(全部 AND)
23
23
 
@@ -29,9 +29,36 @@ rules: # 规则列表,按书写顺序求值;可以省略或为空
29
29
  | `paths` | `string[]` | 工作区相对路径模式,任一候选命中任一模式即可。候选来自参数中下列键的值(**任意嵌套深度**、深度上限):`path` `paths` `file` `files` `file_path` `dir` `directory` `directories` `cwd` `workspace` `root` `target` `targets` `output`。工作区外的绝对候选被丢弃;相对 `../` 形式保留,可显式匹配工作区外。路径模式中 `*` 不跨 `/`,`**` 跨任意深度(含零层)。`caseInsensitivePaths` 开启时(Windows 默认)根比较与模式匹配忽略 ASCII 大小写。 |
30
30
  | `absent` | `string[]` | 必须**缺席**的参数键;**每个列出的键都必须缺失**(非对象参数天然满足)。 |
31
31
  | `when` | `{ env, platform }` | 宿主条件:`env` 为环境变量名 → 模式(**每个列出的变量都必须存在且命中**);`platform` 为任一命中即可的封闭列表(`aix` `android` `darwin` `freebsd` `linux` `openbsd` `sunos` `win32`)。 |
32
+ | `network` | `{ domains, ips, ports, schemes }` | 网络作用域(网络规则):调用必须携带命中每个列出维度的 URL 候选。`domains`(无通配符时包含子域)、`ips`(字面量/glob/CIDR)、`ports`(`*`、单端口或区间)、`schemes`(`http`/`https`)。详见下一节。 |
32
33
 
33
34
  所有维度为 AND:一条规则只有在它的**每个非空维度都命中**时才命中。规则按顺序求值,**首条命中生效**;无命中 = 透传。
34
35
 
36
+ ## network 维度
37
+
38
+ `match` 带 `network` 块的规则是**网络规则**。它只有在调用携带命中每个列出维度(AND)的 URL 候选时才命中——候选来自 web 工具的 URL 参数(`url`、`endpoint`、`webhook` 等)或 `bash`/`pwsh` 命令文本中嵌入的 URL;在代理层则直接对 shell 子进程的连接目标求值。不带 `network` 的规则保持原有文件/命令行为不变。
39
+
40
+ | 字段 | 语义 |
41
+ |---|---|
42
+ | `domains` | 域名模式,小写化并去掉末尾点。无通配符的模式包含子域——`example.com` 同时命中 `api.example.com`;`*.example.com` 只匹配子域;`*`/`**` 按普通 glob 编译。任一模式命中目标主机即满足该维度。 |
43
+ | `ips` | 精确 IPv4/IPv6 字面量、glob(`10.0.*.*`)或 IPv4 CIDR(`10.0.0.0/8`)。URL 中的字面量 IP 始终是候选;代理额外解析主机名并测试其地址,而 `tools/pre-execute` 只匹配字面量 IP(热路径不做 DNS)。任一命中即满足。 |
44
+ | `ports` | `*`、单端口(`443`)或闭区间(`8000-9000`);接受数值型 YAML 端口。按有效端口求值(URL 端口,否则按 scheme 取 80/443)。任一命中即满足。 |
45
+ | `schemes` | `http` 和/或 `https`;目标 scheme 必须是其中之一。 |
46
+
47
+ 网络块必须至少命名四个字段之一。用工具作用域限定网络规则:`tools: [bash, pwsh]`(shell 流量)、`tools: [web_fetch, web_search]`(web 工具),或留空 `tools` 同时覆盖两者。决定未列出目标的三档策略模式(`deny-all` / `whitelist` / `allow-all`,以及映射到沙箱预设的 `auto` 模式)是插件配置而非规则语法——见 README「Network policy」一节。
48
+
49
+ ```yaml
50
+ rules:
51
+ # 对 web 工具封锁一个主机;shell 代理对 curl 同样生效
52
+ - match: { tools: [web_fetch, web_search], network: { domains: [blocked.example] } }
53
+ action: deny
54
+ reason: "blocked.example 禁止访问"
55
+
56
+ # 仅放行固定的包镜像(https,白名单友好规则)
57
+ - match: { tools: [bash, pwsh], network: { domains: ["registry.npmjs.org", "proxy.golang.org"], schemes: [https] } }
58
+ action: allow
59
+ reason: "固定包镜像"
60
+ ```
61
+
35
62
  ## action、reason 与元数据
36
63
 
37
64
  - `action: allow | deny | ask`(必填)。
@@ -108,6 +108,38 @@
108
108
  }
109
109
  }
110
110
  }
111
+ },
112
+ "network": {
113
+ "description": "Network scope (a network rule): the call must carry a URL candidate (a web-tool URL argument, or a URL embedded in bash/pwsh command text) — or, at the proxy layer, a connection target — satisfying every listed dimension. domains are subdomain-inclusive unless wildcarded; ips accept IPv4/IPv6 literals, globs, and IPv4 CIDRs; ports accept \"*\", one port, or an inclusive low-high range (numeric YAML ports accepted); schemes are http/https. A network block must name at least one dimension.",
114
+ "type": "object",
115
+ "additionalProperties": false,
116
+ "properties": {
117
+ "domains": {
118
+ "description": "Domain patterns; ANY matching the target host satisfies the dimension.",
119
+ "type": "array",
120
+ "items": { "type": "string", "minLength": 1 }
121
+ },
122
+ "ips": {
123
+ "description": "IP literals, globs, or IPv4 CIDRs; ANY matching the target address satisfies the dimension.",
124
+ "type": "array",
125
+ "items": { "type": "string", "minLength": 1 }
126
+ },
127
+ "ports": {
128
+ "description": "\"*\", one port, or an inclusive low-high range; ANY matching the effective port satisfies the dimension.",
129
+ "type": "array",
130
+ "items": {
131
+ "oneOf": [
132
+ { "type": "string", "minLength": 1 },
133
+ { "type": "integer", "minimum": 0, "maximum": 65535 }
134
+ ]
135
+ }
136
+ },
137
+ "schemes": {
138
+ "description": "Target scheme must be one of these.",
139
+ "type": "array",
140
+ "items": { "enum": ["http", "https"] }
141
+ }
142
+ }
111
143
  }
112
144
  }
113
145
  },
package/lib/index.js CHANGED
@@ -3267,7 +3267,7 @@ async function apply(ctx, config) {
3267
3267
  systemPrompt.context({
3268
3268
  name: "network:policy",
3269
3269
  order: 115,
3270
- text: "Network policy (permission-rules): shell commands reach the network only through a local policy proxy, and web tools are gated the same way — every target is allowed or blocked per the active rules and sandbox mode, and a blocked connection fails with a [network: …] message. Follow the denial messages and do not attempt to bypass the proxy."
3270
+ text: "You are a helpful assistant. Network policy (permission-rules): shell commands reach the network only through a local policy proxy, and web tools are gated the same way — every target is allowed or blocked per the active rules and sandbox mode, and a blocked connection fails with a [network: …] message. Follow the denial messages and do not attempt to bypass the proxy."
3271
3271
  });
3272
3272
  });
3273
3273
  await ctx.plugin(PermissionRulesRemoteService, { runtime });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-permission-rules",
3
- "version": "0.5.4",
3
+ "version": "0.5.5",
4
4
  "description": "Declarative Claude Code-style permission rules plus a Codex-style process-level network policy for DeepSeek Harness: ordered allow/deny/ask rules with tool-name, argument (glob/regex), workspace-path, and network-target (domain/ip/port/scheme) matching on the tools/pre-execute waterfall, a built-in local HTTP/CONNECT proxy governing shell subprocess traffic, three network modes mapped onto the official sandbox presets, full session-log audit, HMR rule reload, and a settings page with a rule editor, block counters, and recent interception lists.",
5
5
  "private": false,
6
6
  "type": "module",
@@ -160,7 +160,7 @@
160
160
  "@eslint/js": "^10.0.1",
161
161
  "@types/node": "^22.19.0",
162
162
  "@vitest/coverage-v8": "^3.2.0",
163
- "dsh-auto-review": "file:vendor/dsh-auto-review-0.5.4.tgz",
163
+ "dsh-auto-review": "file:vendor/dsh-auto-review-0.6.0.tgz",
164
164
  "eslint": "^10.8.1",
165
165
  "globals": "^17.11.0",
166
166
  "react": "18.3.1",
package/src/runtime.ts CHANGED
@@ -1159,7 +1159,7 @@ export async function apply(ctx: Context, config: Config): Promise<void> {
1159
1159
  systemPrompt.context({
1160
1160
  name: 'network:policy',
1161
1161
  order: 115,
1162
- text: 'Network policy (permission-rules): shell commands reach the network only through a local policy proxy, and web tools are gated the same way — every target is allowed or blocked per the active rules and sandbox mode, and a blocked connection fails with a [network: …] message. Follow the denial messages and do not attempt to bypass the proxy.',
1162
+ text: 'You are a helpful assistant. Network policy (permission-rules): shell commands reach the network only through a local policy proxy, and web tools are gated the same way — every target is allowed or blocked per the active rules and sandbox mode, and a blocked connection fails with a [network: …] message. Follow the denial messages and do not attempt to bypass the proxy.',
1163
1163
  })
1164
1164
  })
1165
1165
  await ctx.plugin(PermissionRulesRemoteService, { runtime })