dsh-permission-rules 0.5.6 → 0.6.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 +12 -0
- package/README.es.md +4 -1
- package/README.hi.md +4 -1
- package/README.md +4 -1
- package/README.pt.md +4 -1
- package/README.zh.md +4 -1
- package/docs/rules-format.en.md +37 -0
- package/docs/rules-format.md +38 -1
- package/docs/rules-format.schema.json +29 -0
- package/lib/index.js +404 -29
- package/lib/types/builtin-rules.d.ts +26 -0
- package/lib/types/builtin-rules.d.ts.map +1 -0
- package/lib/types/config.d.ts +16 -0
- package/lib/types/config.d.ts.map +1 -1
- package/lib/types/index.d.ts +2 -0
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/prose.d.ts +1 -0
- package/lib/types/prose.d.ts.map +1 -1
- package/lib/types/rules.d.ts +62 -0
- package/lib/types/rules.d.ts.map +1 -1
- package/lib/types/runtime.d.ts +31 -6
- package/lib/types/runtime.d.ts.map +1 -1
- package/lib/types/shell.d.ts +38 -0
- package/lib/types/shell.d.ts.map +1 -0
- package/package.json +2 -1
- package/rules/builtin-high-risk.yaml +155 -0
- package/src/builtin-rules.ts +34 -0
- package/src/config.ts +32 -0
- package/src/index.ts +2 -0
- package/src/prose.ts +6 -5
- package/src/rules.ts +172 -17
- package/src/runtime.ts +85 -12
- package/src/shell.ts +187 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to dsh-permission-rules are recorded here, newest first.
|
|
4
4
|
|
|
5
|
+
## v0.6.0 — 2026-08-26
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- **Built-in high-risk baseline**: a shipped `rules/builtin-high-risk.yaml` (deny/ask rules for destructive commands — `rm -rf /`, `mkfs`, `dd of=/dev/*`, `chmod -R 777`, `chown -R` — privilege escalation — setuid/setgid, `shutdown`/`reboot` — download-and-execute — `curl|sh`/`wget|sh` — history rewrites — `git push --force`, `git reset --hard` — the fork bomb, and sensitive paths — `~/.ssh`, `.env`, credentials, `/etc/shadow`, `~/.aws`). Enabled by default and appended AFTER user rules (first-match lets a nearer user rule override it); toggle with `builtin.enabled`, swap with `builtin.path`.
|
|
10
|
+
- **`argv` match dimension**: lexically decompose shell `command`/`cmd`/`script` arguments (quotes/escapes/pipes/control-operators/redirects aware) into command words, argument tokens, redirect targets, and a pipeline signature, then match them token-precisely (`command`, `args` AND, `anyArg` OR, `pipeline`) without changing existing rule syntax.
|
|
11
|
+
- **Rule compile cache**: compiled rules are cached per source path + content hash, so the shared built-in baseline and any shared fallback/absolute `rulesFile` are parsed and compiled once across workspaces instead of once per cwd.
|
|
12
|
+
|
|
13
|
+
### Docs
|
|
14
|
+
|
|
15
|
+
- Document the `argv` dimension and the built-in baseline in `docs/rules-format.md`/`.en.md` and `docs/rules-format.schema.json`; add the `builtin.*` config keys to the five-language READMEs.
|
|
16
|
+
|
|
5
17
|
## v0.5.6 — 2026-08-25
|
|
6
18
|
|
|
7
19
|
### Fixed
|
package/README.es.md
CHANGED
|
@@ -39,7 +39,8 @@
|
|
|
39
39
|
|
|
40
40
|
Cada acierto **y** cada paso directo se registra como un evento de sesión `permissionRules/decision` (solo registro — nada extra se inyecta en el contexto del modelo).
|
|
41
41
|
|
|
42
|
-
- **Emparejamiento rico** — globs de nombre de herramienta (incl. `mcp__*`), selectores de identidad de agente (`main` / `subagent` / `preset:*`), globs **o** regexes de clave/valor de argumentos (con negación `!pattern` y una dimensión de clave `absent`), globs de ruta relativos al workspace a **cualquier profundidad de anidamiento**,
|
|
42
|
+
- **Emparejamiento rico** — globs de nombre de herramienta (incl. `mcp__*`), selectores de identidad de agente (`main` / `subagent` / `preset:*`), globs **o** regexes de clave/valor de argumentos (con negación `!pattern` y una dimensión de clave `absent`), globs de ruta relativos al workspace a **cualquier profundidad de anidamiento**, condiciones de host `when` (variables de entorno, plataforma), y **descomposición de comandos de shell** (`argv`: palabra de comando, tokens de argumento, firma de pipeline) para emparejamiento preciso a nivel de token.
|
|
43
|
+
- **Línea base de alto riesgo integrada** — un conjunto deny/ask embarcado (comandos destructivos, escalada de privilegios, descarga-y-ejecución, rutas sensibles) habilitado por defecto y añadido después de las reglas de usuario (una regla de usuario más cercana puede sobrescribirlo); se alterna con `builtin.enabled`.
|
|
43
44
|
- **Archivos de reglas jerárquicos** — `searchUp` opcional fusiona cada `.dsh/rules.yaml` desde el cwd de la sesión hasta la raíz del sistema de archivos, el más cercano primero.
|
|
44
45
|
- **Despliegue en dry-run** — `enforce: false` audita lo que la política *haría* mientras deja pasar cada llamada.
|
|
45
46
|
- **Recarga en caliente** — vigilancia Chokidar con debounce; una edición rota conserva las reglas anteriores, nunca falla.
|
|
@@ -127,6 +128,8 @@ Todos los parámetros son campos Schemastery `Config` (modificables desde cordis
|
|
|
127
128
|
| `network.loopback` | `allow` | Destinos de loopback: `allow` (paridad Codex) o `policy` |
|
|
128
129
|
| `network.injectEnv` | `true` | Si se inyectan variables de entorno del proxy para subprocesos |
|
|
129
130
|
| `network.noProxy` | `clear` | Manejo de NO_PROXY en subprocesos: `clear` aplica la política o `preserve` |
|
|
131
|
+
| `builtin.enabled` | `true` | Línea base de alto riesgo integrada: `false` deshabilita por completo el conjunto deny/ask embarcado |
|
|
132
|
+
| `builtin.path` | *(embarcado)* | Archivo de línea base de reemplazo (absoluto, o relativo a `process.cwd()`); validado al montar |
|
|
130
133
|
|
|
131
134
|
## Tools & surfaces
|
|
132
135
|
|
package/README.hi.md
CHANGED
|
@@ -39,7 +39,8 @@
|
|
|
39
39
|
|
|
40
40
|
हर हिट **और** हर पास-थ्रू `permissionRules/decision` सत्र घटना के रूप में लॉग होता है (केवल-लॉग — मॉडल संदर्भ में कुछ भी अतिरिक्त इंजेक्ट नहीं होता)।
|
|
41
41
|
|
|
42
|
-
- **समृद्ध मिलान** — टूल-नाम globs (जिनमें `mcp__*` शामिल), एजेंट-पहचान चयनकर्ता (`main` / `subagent` / `preset:*`), तर्क कुंजी/मान globs **या** regex (जिनमें `!pattern` निषेध और `absent` कुंजी आयाम), **किसी भी नेस्टिंग गहराई** पर कार्यक्षेत्र-सापेक्ष पथ globs,
|
|
42
|
+
- **समृद्ध मिलान** — टूल-नाम globs (जिनमें `mcp__*` शामिल), एजेंट-पहचान चयनकर्ता (`main` / `subagent` / `preset:*`), तर्क कुंजी/मान globs **या** regex (जिनमें `!pattern` निषेध और `absent` कुंजी आयाम), **किसी भी नेस्टिंग गहराई** पर कार्यक्षेत्र-सापेक्ष पथ globs, `when` होस्ट शर्तें (env चर, प्लेटफ़ॉर्म), और **शेल कमांड विघटन** (`argv`: कमांड शब्द, तर्क टोकन, पाइपलाइन हस्ताक्षर) टोकन-स्तर सटीक मिलान के लिए।
|
|
43
|
+
- **अंतर्निहित उच्च-जोखिम आधाररेखा** — एक साथ भेजा गया deny/ask नियम-समूह (विनाशकारी कमांड, विशेषाधिकार वृद्धि, डाउनलोड-और-निष्पादन, संवेदनशील पथ) डिफ़ॉल्ट रूप से सक्षम और उपयोगकर्ता नियमों के बाद जोड़ा गया (निकटतम उपयोगकर्ता नियम इसे अधिरोहित कर सकता है); `builtin.enabled` से टॉगल करें।
|
|
43
44
|
- **पदानुक्रमित नियम फ़ाइलें** — वैकल्पिक `searchUp` सत्र cwd से फ़ाइल-सिस्टम रूट तक हर `.dsh/rules.yaml` को मर्ज करता है, निकटतम पहले।
|
|
44
45
|
- **dry-run रोलआउट** — `enforce: false` ऑडिट करता है कि नीति *क्या* करती, हर कॉल को पास करते हुए।
|
|
45
46
|
- **हॉट रीलोड** — debounce सहित Chokidar निगरानी; टूटा संपादन पिछले नियम रखता है, कभी क्रैश नहीं।
|
|
@@ -127,6 +128,8 @@ dsh --profile web --dump-config | grep -A4 'id: permission-rules'
|
|
|
127
128
|
| `network.loopback` | `allow` | लूपबैक लक्ष्य: `allow` (Codex समता) या `policy` |
|
|
128
129
|
| `network.injectEnv` | `true` | क्या उप-प्रक्रियाओं के लिए प्रॉक्सी पर्यावरण चर इंजेक्ट हों |
|
|
129
130
|
| `network.noProxy` | `clear` | उप-प्रक्रिया NO_PROXY व्यवहार: `clear` नीति लागू करता है या `preserve` |
|
|
131
|
+
| `builtin.enabled` | `true` | अंतर्निहित उच्च-जोखिम आधाररेखा: `false` साथ भेजे गए deny/ask नियम-समूह को पूरी तरह अक्षम करता है |
|
|
132
|
+
| `builtin.path` | *(साथ भेजा)* | प्रतिस्थापन आधाररेखा फ़ाइल (निरपेक्ष, या `process.cwd()` के सापेक्ष); माउंट पर मान्य |
|
|
130
133
|
|
|
131
134
|
## Tools & surfaces
|
|
132
135
|
|
package/README.md
CHANGED
|
@@ -40,7 +40,8 @@
|
|
|
40
40
|
|
|
41
41
|
Every hit **and** every passthrough is audit-logged as a `permissionRules/decision` session event (log-only — nothing extra is injected into the model context).
|
|
42
42
|
|
|
43
|
-
- **Rich matching** — tool-name globs (including `mcp__*`), agent-identity selectors (`main` / `subagent` / `preset:*`), argument key/value globs **or** regexes (with `!pattern` negation and an `absent` key dimension), workspace-relative path globs at **any nesting depth**,
|
|
43
|
+
- **Rich matching** — tool-name globs (including `mcp__*`), agent-identity selectors (`main` / `subagent` / `preset:*`), argument key/value globs **or** regexes (with `!pattern` negation and an `absent` key dimension), workspace-relative path globs at **any nesting depth**, `when` host conditions (env vars, platform), and **shell command decomposition** (`argv`: command word, argument tokens, pipeline signature) for token-precise command matching.
|
|
44
|
+
- **Built-in high-risk baseline** — a shipped deny/ask ruleset (destructive commands, privilege escalation, download-and-execute, sensitive paths) enabled by default and appended after user rules so a nearer user rule can override it; toggle with `builtin.enabled`.
|
|
44
45
|
- **Hierarchical rule files** — optional `searchUp` merges every `.dsh/rules.yaml` from the session cwd to the filesystem root, nearest first.
|
|
45
46
|
- **Dry-run rollout** — `enforce: false` audits what the policy *would* do while passing every call through.
|
|
46
47
|
- **Hot reload** — Chokidar watch with debounce; a broken edit keeps the previous rules, never crashes.
|
|
@@ -128,6 +129,8 @@ All tunables are Schemastery `Config` fields (changeable from cordis.yml). An id
|
|
|
128
129
|
| `network.loopback` | `allow` | Loopback targets: `allow` (Codex parity) or `policy` |
|
|
129
130
|
| `network.injectEnv` | `true` | Whether proxy environment variables are injected for subprocesses |
|
|
130
131
|
| `network.noProxy` | `clear` | Subprocess NO_PROXY handling: `clear` enforces the policy or `preserve` |
|
|
132
|
+
| `builtin.enabled` | `true` | Built-in high-risk baseline: `false` disables the shipped deny/ask ruleset entirely |
|
|
133
|
+
| `builtin.path` | *(shipped)* | Replacement baseline file (absolute, or relative to `process.cwd()`); validated at mount |
|
|
131
134
|
|
|
132
135
|
## Tools & surfaces
|
|
133
136
|
|
package/README.pt.md
CHANGED
|
@@ -39,7 +39,8 @@ O `dsh-permission-rules` antepõe uma lista ordenada de regras **`allow` / `deny
|
|
|
39
39
|
|
|
40
40
|
Cada acerto **e** cada passagem direta é registrada como um evento de sessão `permissionRules/decision` (somente log — nada extra é injetado no contexto do modelo).
|
|
41
41
|
|
|
42
|
-
- **Correspondência rica** — globs de nome de ferramenta (incl. `mcp__*`), seletores de identidade de agente (`main` / `subagent` / `preset:*`), globs **ou** regexes de chave/valor de argumentos (com negação `!pattern` e dimensão de chave `absent`), globs de caminho relativos ao workspace em **qualquer profundidade de aninhamento**,
|
|
42
|
+
- **Correspondência rica** — globs de nome de ferramenta (incl. `mcp__*`), seletores de identidade de agente (`main` / `subagent` / `preset:*`), globs **ou** regexes de chave/valor de argumentos (com negação `!pattern` e dimensão de chave `absent`), globs de caminho relativos ao workspace em **qualquer profundidade de aninhamento**, condições de host `when` (variáveis de ambiente, plataforma), e **decomposição de comandos de shell** (`argv`: palavra de comando, tokens de argumento, assinatura de pipeline) para correspondência precisa por token.
|
|
43
|
+
- **Linha de base de alto risco integrada** — um conjunto deny/ask embarcado (comandos destrutivos, escalada de privilégios, baixar-e-executar, caminhos sensíveis) habilitado por padrão e anexado após as regras de usuário (uma regra de usuário mais próxima pode sobrescrevê-lo); alterna com `builtin.enabled`.
|
|
43
44
|
- **Arquivos de regras hierárquicos** — `searchUp` opcional mescla cada `.dsh/rules.yaml` do cwd da sessão até a raiz do sistema de arquivos, o mais próximo primeiro.
|
|
44
45
|
- **Implantação em dry-run** — `enforce: false` audita o que a política *faria* enquanto deixa cada chamada passar.
|
|
45
46
|
- **Recarga a quente** — vigilância Chokidar com debounce; uma edição quebrada mantém as regras anteriores, nunca falha.
|
|
@@ -127,6 +128,8 @@ Todos os parâmetros são campos Schemastery `Config` (alteráveis pelo cordis.y
|
|
|
127
128
|
| `network.loopback` | `allow` | Destinos de loopback: `allow` (paridade Codex) ou `policy` |
|
|
128
129
|
| `network.injectEnv` | `true` | Se variáveis de ambiente do proxy são injetadas para subprocessos |
|
|
129
130
|
| `network.noProxy` | `clear` | Manejo de NO_PROXY em subprocessos: `clear` aplica a política ou `preserve` |
|
|
131
|
+
| `builtin.enabled` | `true` | Linha de base de alto risco integrada: `false` desabilita por completo o conjunto deny/ask embarcado |
|
|
132
|
+
| `builtin.path` | *(embarcado)* | Arquivo de linha de base de substituição (absoluto, ou relativo a `process.cwd()`); validado ao montar |
|
|
130
133
|
|
|
131
134
|
## Tools & surfaces
|
|
132
135
|
|
package/README.zh.md
CHANGED
|
@@ -39,7 +39,8 @@
|
|
|
39
39
|
|
|
40
40
|
每次命中**和**每次透传都作为 `permissionRules/decision` 会话事件审计落盘(仅日志——不会向模型上下文额外注入任何内容)。
|
|
41
41
|
|
|
42
|
-
- **丰富匹配** —— 工具名 glob(含 `mcp__*`)、agent 身份选择器(`main` / `subagent` / `preset:*`)、参数键/值 glob **或** 正则(含 `!pattern` 取反与 `absent` 键维度)、**任意嵌套深度**的工作区相对路径 glob
|
|
42
|
+
- **丰富匹配** —— 工具名 glob(含 `mcp__*`)、agent 身份选择器(`main` / `subagent` / `preset:*`)、参数键/值 glob **或** 正则(含 `!pattern` 取反与 `absent` 键维度)、**任意嵌套深度**的工作区相对路径 glob、`when` 宿主条件(环境变量、平台),以及 **shell 命令分解**(`argv`:命令词、参数 token、管道签名)用于 token 级精确命令匹配。
|
|
43
|
+
- **内置高危基线** —— 随附的 deny/ask 规则集(破坏性命令、权限提升、下载即执行、敏感路径),默认开启并追加在用户规则之后(更近的用户规则可覆盖);用 `builtin.enabled` 开关。
|
|
43
44
|
- **分层规则文件** —— 可选 `searchUp` 从会话 cwd 到文件系统根合并每个 `.dsh/rules.yaml`,就近优先。
|
|
44
45
|
- **试运行上线** —— `enforce: false` 审计策略*会*做什么,同时放行每个调用。
|
|
45
46
|
- **热重载** —— Chokidar 监听带去抖;损坏的编辑保留旧规则、绝不崩溃。
|
|
@@ -127,6 +128,8 @@ dsh --profile web --dump-config | grep -A4 'id: permission-rules'
|
|
|
127
128
|
| `network.loopback` | `allow` | 回环目标:`allow`(Codex 对齐)或 `policy` |
|
|
128
129
|
| `network.injectEnv` | `true` | 是否为子进程注入代理环境变量 |
|
|
129
130
|
| `network.noProxy` | `clear` | 子进程 NO_PROXY 处理:`clear` 强制策略或 `preserve` |
|
|
131
|
+
| `builtin.enabled` | `true` | 内置高危基线:`false` 完全禁用随附的 deny/ask 规则集 |
|
|
132
|
+
| `builtin.path` | *(随附)* | 替换基线文件(绝对路径,或相对 `process.cwd()`);挂载时校验 |
|
|
130
133
|
|
|
131
134
|
## Tools & surfaces
|
|
132
135
|
|
package/docs/rules-format.en.md
CHANGED
|
@@ -29,6 +29,7 @@ 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
|
+
| `argv` | `{ command, args, anyArg, pipeline }` | Shell command decomposition scope: matches a command-string argument (`command`/`cmd`/`script`/`command_line`/`commandLine`) whose lexical decomposition satisfies every listed field. `command` matches any simple-command word; `pipeline` matches the command words joined by `|`; `args` requires every pattern to match some token; `anyArg` requires some token to match a pattern (both support `!` negation). Argument tokens include redirect targets. See the next section. |
|
|
32
33
|
| `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. |
|
|
33
34
|
|
|
34
35
|
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.
|
|
@@ -59,6 +60,32 @@ rules:
|
|
|
59
60
|
reason: "pinned package registries"
|
|
60
61
|
```
|
|
61
62
|
|
|
63
|
+
## argv dimension (shell command decomposition)
|
|
64
|
+
|
|
65
|
+
A rule whose `match` carries an `argv` block matches a command-string argument — the `command`/`cmd`/`script`/`command_line`/`commandLine` values, at any nesting depth — after lexically decomposing it into simple commands. The decomposition is quote/escape/pipe/control-operator/redirect aware and produces, per simple command, a command word, argument tokens, and redirect targets. This gives token-precise matching that the raw `params.command` substring globs cannot express (e.g. `rm -rf /tmp` is NOT `rm -rf /`).
|
|
66
|
+
|
|
67
|
+
| Field | Semantics |
|
|
68
|
+
|---|---|
|
|
69
|
+
| `command` | Command-word globs. ANY simple-command word matching ANY pattern satisfies the field. |
|
|
70
|
+
| `pipeline` | Pipeline-signature globs: matched against the command words joined by `|` (e.g. `curl http://x \| sh` → `curl\|sh`). ANY pattern matching the signature satisfies the field. |
|
|
71
|
+
| `args` | Argument-token patterns; EVERY pattern must match at least one token (AND across patterns, any-of across tokens). A `!`-prefixed pattern negates (no token may match it). |
|
|
72
|
+
| `anyArg` | Argument-token patterns; ANY token matching ANY pattern satisfies the field (OR). A `!`-prefixed pattern negates. |
|
|
73
|
+
|
|
74
|
+
All four fields AND together; a block must name at least one. Argument tokens include redirect targets (`echo x > /etc/passwd` feeds `/etc/passwd` to `args`/`anyArg`). `command` and `pipeline` are always globs; `args` and `anyArg` follow `patternMode`. Rules are typically scoped with `tools: [bash, pwsh]`, but the dimension itself is tool-agnostic (any tool carrying a command string feeds it).
|
|
75
|
+
|
|
76
|
+
```yaml
|
|
77
|
+
rules:
|
|
78
|
+
# token-precise: rm -rf / but NOT rm -rf /tmp
|
|
79
|
+
- match: { tools: [bash, pwsh], argv: { command: "rm", args: ["-rf", "/"] } }
|
|
80
|
+
action: deny
|
|
81
|
+
reason: "rm -rf / wipes the filesystem root"
|
|
82
|
+
|
|
83
|
+
# download-and-execute: any pipeline whose stages are curl/wget then sh/bash
|
|
84
|
+
- match: { tools: [bash, pwsh], argv: { pipeline: ["curl*|*sh", "wget*|*bash"] } }
|
|
85
|
+
action: deny
|
|
86
|
+
reason: "piping a remote fetch into a shell executes remote code"
|
|
87
|
+
```
|
|
88
|
+
|
|
62
89
|
## action, reason, and metadata
|
|
63
90
|
|
|
64
91
|
- `action: allow | deny | ask` (required).
|
|
@@ -121,6 +148,16 @@ rules:
|
|
|
121
148
|
reason: "Write operations need confirmation"
|
|
122
149
|
```
|
|
123
150
|
|
|
151
|
+
## Built-in high-risk baseline
|
|
152
|
+
|
|
153
|
+
The plugin ships a built-in high-risk baseline at `rules/builtin-high-risk.yaml` (deny/ask rules for destructive commands, privilege escalation, download-and-execute, and sensitive paths). It is **enabled by default** and appended AFTER every user rule file (project chain → `searchUp` → `fallback`), so first-match semantics let any nearer user rule override it; with no user rules it applies alone. `/rules` attributes the baseline rules to their shipped source file.
|
|
154
|
+
|
|
155
|
+
- Disable entirely with `builtin.enabled: false`.
|
|
156
|
+
- Swap the file with `builtin.path` (absolute, or relative to `process.cwd()`).
|
|
157
|
+
- The baseline is a deployed, read-only file: it is validated at mount (missing/invalid fails loud), it is never watched, and the settings-page editor refuses to write it.
|
|
158
|
+
|
|
159
|
+
The shipped baseline is an example, not an exhaustive policy — extend it with your own project rules.
|
|
160
|
+
|
|
124
161
|
## Division of labor with dsh-auto-review
|
|
125
162
|
|
|
126
163
|
This plugin only produces `ask` decisions; the `ask` goes to the official `ctx.approval` approval seam. With `dsh-auto-review` mounted, its answerer claims matching requests and delivers a second-model verdict; otherwise a human answers; with neither, the official seam fails closed (`unavailable` → denied). This plugin **never** starts a reviewer subagent.
|
package/docs/rules-format.md
CHANGED
|
@@ -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`、`network`
|
|
20
|
+
顶层只允许 `rules` 一个键;每条规则只允许 `match`、`action`、`reason`、`enabled`、`description`、`tags` 六个键;`match` 只允许 `tools`、`agents`、`params`、`paths`、`absent`、`when`、`argv`、`network` 八个键。出现任何未知键都在加载期报错(响亮失败),绝不静默忽略。
|
|
21
21
|
|
|
22
22
|
## match 维度(全部 AND)
|
|
23
23
|
|
|
@@ -29,6 +29,7 @@ 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
|
+
| `argv` | `{ command, args, anyArg, pipeline }` | shell 命令分解作用域:命中携带命令字符串参数(`command`/`cmd`/`script`/`command_line`/`commandLine`)且其词法分解命中每个列出字段的调用。`command` 命中任一简单命令的命令词;`pipeline` 命中用 `|` 连接的命令词序列;`args` 要求每个模式都命中某 token;`anyArg` 要求某 token 命中某模式(两者均支持 `!` 否定)。参数 token 含重定向目标。详见下一节。 |
|
|
32
33
|
| `network` | `{ domains, ips, ports, schemes }` | 网络作用域(网络规则):调用必须携带命中每个列出维度的 URL 候选。`domains`(无通配符时包含子域)、`ips`(字面量/glob/CIDR)、`ports`(`*`、单端口或区间)、`schemes`(`http`/`https`)。详见下一节。 |
|
|
33
34
|
|
|
34
35
|
所有维度为 AND:一条规则只有在它的**每个非空维度都命中**时才命中。规则按顺序求值,**首条命中生效**;无命中 = 透传。
|
|
@@ -59,6 +60,32 @@ rules:
|
|
|
59
60
|
reason: "固定包镜像"
|
|
60
61
|
```
|
|
61
62
|
|
|
63
|
+
## argv 维度(shell 命令分解)
|
|
64
|
+
|
|
65
|
+
`match` 带 `argv` 块的规则会命中命令字符串参数——即 `command`/`cmd`/`script`/`command_line`/`commandLine` 的值(任意嵌套深度)——先做词法分解再匹配。分解对引号/转义/管道/控制运算符/重定向有感知,得到每个简单命令的命令词、参数 token 与重定向目标,从而获得 `params.command` 子串 glob 无法表达的「token 级精确」匹配(例如 `rm -rf /tmp` 不是 `rm -rf /`)。
|
|
66
|
+
|
|
67
|
+
| 字段 | 语义 |
|
|
68
|
+
|---|---|
|
|
69
|
+
| `command` | 命令词 glob;**任一**简单命令的命令词命中**任一**模式即满足该字段。 |
|
|
70
|
+
| `pipeline` | 管道签名 glob:用 `|` 连接的命令词序列(如 `curl http://x \| sh` → `curl\|sh`)上匹配;**任一**模式命中签名即满足该字段。 |
|
|
71
|
+
| `args` | 参数 token 模式;**每个**模式都必须命中至少一个 token(模式间 AND、token 间任一命中)。`!` 前缀为否定(任何 token 都不得命中)。 |
|
|
72
|
+
| `anyArg` | 参数 token 模式;**任一** token 命中**任一**模式即满足该字段(OR)。`!` 前缀为否定。 |
|
|
73
|
+
|
|
74
|
+
四个字段之间 AND;块必须至少命名一个字段。参数 token 包含重定向目标(`echo x > /etc/passwd` 会把 `/etc/passwd` 交给 `args`/`anyArg`)。`command`、`pipeline` 始终按 glob 解释;`args`、`anyArg` 遵循 `patternMode`。通常用 `tools: [bash, pwsh]` 限定作用域,但该维度本身与工具无关(任何携带命令字符串的工具都会喂给它)。
|
|
75
|
+
|
|
76
|
+
```yaml
|
|
77
|
+
rules:
|
|
78
|
+
# token 级精确:rm -rf / 命中,rm -rf /tmp 不命中
|
|
79
|
+
- match: { tools: [bash, pwsh], argv: { command: "rm", args: ["-rf", "/"] } }
|
|
80
|
+
action: deny
|
|
81
|
+
reason: "rm -rf / 会清空根文件系统"
|
|
82
|
+
|
|
83
|
+
# 下载即执行:任何「curl/wget 管道到 sh/bash」的管道
|
|
84
|
+
- match: { tools: [bash, pwsh], argv: { pipeline: ["curl*|*sh", "wget*|*bash"] } }
|
|
85
|
+
action: deny
|
|
86
|
+
reason: "把远程抓取直接管道给 shell 会执行远程代码"
|
|
87
|
+
```
|
|
88
|
+
|
|
62
89
|
## action、reason 与元数据
|
|
63
90
|
|
|
64
91
|
- `action: allow | deny | ask`(必填)。
|
|
@@ -119,6 +146,16 @@ rules:
|
|
|
119
146
|
reason: "写操作需要确认"
|
|
120
147
|
```
|
|
121
148
|
|
|
149
|
+
## 内置高危基线
|
|
150
|
+
|
|
151
|
+
插件随附一份内置高危基线 `rules/builtin-high-risk.yaml`(针对破坏性命令、权限提升、下载即执行与敏感路径的 deny/ask 规则)。它**默认开启**,追加在每条用户规则文件(项目链 → `searchUp` → `fallback`)**之后**,因此首匹配语义让更近的用户规则可以覆盖它;没有用户规则时它单独生效。`/rules` 会把基线规则归属到其随附源文件。
|
|
152
|
+
|
|
153
|
+
- 用 `builtin.enabled: false` 整体关闭。
|
|
154
|
+
- 用 `builtin.path`(绝对路径,或相对 `process.cwd()`)替换文件。
|
|
155
|
+
- 基线是部署级只读文件:挂载时校验(缺失/非法即响亮失败)、不监听、设置页编辑器拒绝写入。
|
|
156
|
+
|
|
157
|
+
随附基线只是示例而非穷尽策略——请用自己的项目规则扩展它。
|
|
158
|
+
|
|
122
159
|
## 与 dsh-auto-review 的分工
|
|
123
160
|
|
|
124
161
|
本插件只产出 `ask` 决定;`ask` 交给官方 `ctx.approval` 审批 seam。挂载 `dsh-auto-review` 后,其 answerer 会认领符合策略的请求并给出第二模型裁决,否则交给人类审批;两者都没有时官方 fail-closed(`unavailable` → 拒绝)。本插件**不**自己启动 reviewer 子代理。
|
|
@@ -109,6 +109,35 @@
|
|
|
109
109
|
}
|
|
110
110
|
}
|
|
111
111
|
},
|
|
112
|
+
"argv": {
|
|
113
|
+
"description": "Shell command decomposition scope: the call must carry a command-string argument whose lexical decomposition (command words, argument tokens, redirect targets, pipeline signature) satisfies every listed field. command/pipeline are globs; args/anyArg follow patternMode. args requires EVERY pattern to match some token; anyArg requires SOME token to match a pattern; a !-prefixed args/anyArg pattern negates. A block must name at least one field.",
|
|
114
|
+
"type": "object",
|
|
115
|
+
"additionalProperties": false,
|
|
116
|
+
"properties": {
|
|
117
|
+
"command": {
|
|
118
|
+
"description": "Command-word globs; ANY simple-command word matching ANY pattern satisfies the field.",
|
|
119
|
+
"type": "array",
|
|
120
|
+
"items": { "type": "string", "minLength": 1 }
|
|
121
|
+
},
|
|
122
|
+
"args": {
|
|
123
|
+
"description": "Argument-token patterns; EVERY pattern must match at least one token; a !-prefixed pattern negates.",
|
|
124
|
+
"type": "array",
|
|
125
|
+
"items": { "$ref": "#/$defs/pattern" },
|
|
126
|
+
"minItems": 1
|
|
127
|
+
},
|
|
128
|
+
"anyArg": {
|
|
129
|
+
"description": "Argument-token patterns; ANY token matching ANY pattern satisfies the field; a !-prefixed pattern negates.",
|
|
130
|
+
"type": "array",
|
|
131
|
+
"items": { "$ref": "#/$defs/pattern" },
|
|
132
|
+
"minItems": 1
|
|
133
|
+
},
|
|
134
|
+
"pipeline": {
|
|
135
|
+
"description": "Pipeline-signature globs matched against the command words joined by `|` (e.g. curl|sh); ANY pattern matching satisfies the field.",
|
|
136
|
+
"type": "array",
|
|
137
|
+
"items": { "type": "string", "minLength": 1 }
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
},
|
|
112
141
|
"network": {
|
|
113
142
|
"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
143
|
"type": "object",
|