dsh-permission-rules 0.6.18 → 0.6.19

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,3 +1,23 @@
1
+ ## v0.6.19 - 2026-09-10
2
+
3
+ ### Security
4
+
5
+ - A CONNECT tunnel refuses to dial a hostname when the adjudication resolved no address: `connectUpstream` now fails closed (logged, 502 to the client) instead of falling back to `connect(port, target.host)` — a second DNS resolution whose answer the rules never saw, and the one remaining path governed by Node's 250 ms `autoSelectFamilyAttemptTimeout`. The reported path itself was already fixed in 0.6.17 (`connect` dials the addresses `decideWithResolution` returned, and an IP literal skips the family race entirely); this closes the residual (issue #21).
6
+
7
+ ### Fixed
8
+
9
+ - Session-less host-level traffic is judged against the CONFIGURED chain instead of an empty map. `proxyChains()` consulted only `byCwd`, which `rulesFor(cwd)` populates from a session or tool call, so at boot the harness's own host-level fetches met the whitelist default and were blocked as `[network: blocked pending approval]` even with a matching allow rule already configured. `hostChain()` loads the chain for the host process's own working directory (project file → absolute `rulesFile` → configured fallback → shipped baseline) while no per-cwd chain is loaded; it never becomes a `byCwd` member, so a session workspace chain always outranks it, and an unusable configured chain warns once and degrades to the mode default (issue #18).
10
+ - Rule files are normalized before parsing: CRLF maps onto LF, a stray CR inside a line is dropped, and every CR is a line separator in a CR-only file. A lone CR is not a line break to the YAML parser in every position — it survived into the scalar it terminated (`domains: [registry.npmjs.org\r]`, or a final line ending in a bare CR), compiling a pattern that silently never matched its target (issue #19 item 4).
11
+ - Rule-file watches poll on WSL drvfs mounts: `watchPollingFor` switches a watch to `usePolling` (300 ms interval) when the watched rule file is under `/mnt/<drive>` or `/proc/version` reports a Microsoft kernel, because chokidar's native change events are unreliable on drvfs/9p and a rule edit otherwise stopped hot-reloading silently with no error. Detection is per watch, so every other host keeps the native watcher, and `/rules reload` stays the manual fallback (issue #19 item 1).
12
+
13
+ ### Added
14
+
15
+ - A failing model call now names the policy. The injected proxy environment is process-wide, so the harness's own LLM transport is adjudicated by this policy too, and a blocked provider endpoint surfaced as a bare `Connection error. / TRANSPORT`. A transparent `llm/stream` listener appends the `[network: …]` marker — blocked target, mode/rule, remediation — to the ORIGINAL error object (its class, code and retry facts stay intact) and logs it, so a turn can be attributed to the policy rather than reading as a provider outage. It registers only when `network.enabled`. It does NOT auto-allow provider endpoints: the two other resolutions the issue offers are not reachable from a plugin (`ctx.llm.listProviders()` exposes provider ids/names only, configured base URLs are adapter-owned, and there is no per-spawn environment seam for shell children), which the five READMEs record as a known limitation (issue #22).
16
+
17
+ ### Tests
18
+
19
+ - Gate chain green: 23 files / 302 tests, coverage 92.27% statements / 87.29% branches / 93.62% functions.
20
+
1
21
  ## v0.6.18 - 2026-09-10
2
22
 
3
23
  ### Docs
package/README.es.md CHANGED
@@ -45,7 +45,7 @@ Cada acierto **y** cada paso directo se registra como un evento de sesión `perm
45
45
  - **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`.
46
46
  - **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.
47
47
  - **Despliegue en dry-run** — `enforce: false` audita lo que la política *haría* mientras deja pasar cada llamada.
48
- - **Recarga en caliente** — vigilancia Chokidar con debounce; una edición rota conserva las reglas anteriores, nunca falla.
48
+ - **Recarga en caliente** — vigilancia Chokidar con debounce; una edición rota conserva las reglas anteriores, nunca falla. En un host WSL, o para un archivo de reglas bajo `/mnt/<unidad>`, la vigilancia pasa a sondeo (polling) porque allí los eventos nativos de cambio no son fiables.
49
49
  - **Fallo ruidoso** — YAML inválido, acciones/campos desconocidos, globs/regexes malos, patrones propensos a backtracking o más de `maxRules` reglas fallan la carga.
50
50
 
51
51
  ## Rule syntax
@@ -78,6 +78,7 @@ Una **política de red a nivel de proceso** estilo Codex: el tráfico de subproc
78
78
 
79
79
  - **Emparejamiento** — `match.network` con `domains` / `ips` / `ports` / `schemes` (globs, comodines, CIDR, rangos de puertos; se aceptan puertos YAML numéricos). La extracción de candidatos URL en la ruta caliente `tools/pre-execute` se dispara sobre argumentos de herramientas web y URLs embebidas en texto de comandos bash/pwsh; los destinos de loopback pueden cortocircuitar reglas según la política `loopback`. Los literales IPv6 mapeados a IPv4 se normalizan a su forma IPv4 antes de comparar, y el proxy se conecta a las direcciones con las que se tomó la decisión, sin una segunda resolución DNS.
80
80
  - **Auditoría** — las conexiones denegadas anexan `permissionRules/network` a la sesión propietaria (la misma puerta adaptativa `ignorable`), con contadores de bloqueo e intercepciones recientes en `/rules network` y la página de settings.
81
+ - **Diagnóstico** — las conexiones bloqueadas llevan un mensaje `[network: …]`, y una llamada al modelo que falla mientras el proxy bloqueó un destino (el entorno de proxy inyectado también alcanza al transporte LLM del host) nombra ese destino y la solución en el texto del fallo, en lugar de quedar como un error de transporte desnudo.
81
82
 
82
83
  ## Quick start
83
84
 
@@ -174,6 +175,7 @@ Todos los parámetros son campos Schemastery `Config` (modificables desde cordis
174
175
  - **Los candidatos de ruta son heurísticos.** Solo las claves de argumento documentadas alimentan el emparejamiento de rutas, y el emparejamiento relativo al workspace es insensible a mayúsculas ASCII solo con `caseInsensitivePaths` activado.
175
176
  - **Los globs son un subconjunto conservador.** Sin expansión de llaves — escribe dos patrones, o usa modo regex.
176
177
  - **La guardia de backtracking de regex es estructural, no exhaustiva.** Prefiere el modo glob para archivos no confiables.
178
+ - **Los endpoints del proveedor LLM necesitan una regla allow.** El entorno de proxy inyectado es de proceso completo, así que en modo whitelist el propio transporte del modelo del host se adjudica como cualquier otra conexión, y el plugin no puede enumerar los endpoints que llamará un adaptador (las rutas de proveedor exponen ids, y las base URL configuradas viven en los settings de cada adaptador). Añade una regla allow para el endpoint del proveedor, o pon `network.injectEnv: false` para dejar el proceso del host fuera del proxy.
177
179
 
178
180
  ## Collaborating with dsh-auto-review
179
181
 
package/README.hi.md CHANGED
@@ -45,7 +45,7 @@
45
45
  - **अंतर्निहित उच्च-जोखिम आधाररेखा** — एक साथ भेजा गया deny/ask नियम-समूह (विनाशकारी कमांड, विशेषाधिकार वृद्धि, डाउनलोड-और-निष्पादन, संवेदनशील पथ) डिफ़ॉल्ट रूप से सक्षम और उपयोगकर्ता नियमों के बाद जोड़ा गया (निकटतम उपयोगकर्ता नियम इसे अधिरोहित कर सकता है); `builtin.enabled` से टॉगल करें।
46
46
  - **पदानुक्रमित नियम फ़ाइलें** — वैकल्पिक `searchUp` सत्र cwd से फ़ाइल-सिस्टम रूट तक हर `.dsh/rules.yaml` को मर्ज करता है, निकटतम पहले।
47
47
  - **dry-run रोलआउट** — `enforce: false` ऑडिट करता है कि नीति *क्या* करती, हर कॉल को पास करते हुए।
48
- - **हॉट रीलोड** — debounce सहित Chokidar निगरानी; टूटा संपादन पिछले नियम रखता है, कभी क्रैश नहीं।
48
+ - **हॉट रीलोड** — debounce सहित Chokidar निगरानी; टूटा संपादन पिछले नियम रखता है, कभी क्रैश नहीं। WSL होस्ट पर, या `/mnt/<drive>` के नीचे रखी नियम फ़ाइल के लिए, निगरानी polling पर चली जाती है क्योंकि वहाँ मूल परिवर्तन इवेंट भरोसेमंद नहीं होते।
49
49
  - **ज़ोर से विफल** — अमान्य YAML, अज्ञात action/फ़ील्ड, ख़राब globs/regex, बैकट्रैकिंग-प्रवण पैटर्न या `maxRules` से अधिक नियम लोड को विफल करते हैं।
50
50
 
51
51
  ## Rule syntax
@@ -78,6 +78,7 @@ Codex-शैली की **प्रक्रिया-स्तरीय न
78
78
 
79
79
  - **मिलान** — `match.network` `domains` / `ips` / `ports` / `schemes` के साथ (globs, वाइल्डकार्ड, CIDR, पोर्ट श्रेणियाँ; संख्यात्मक YAML पोर्ट स्वीकृत)। `tools/pre-execute` हॉट पथ पर URL-उम्मीदवार निष्कर्षण वेब-टूल तर्कों और bash/pwsh कमांड टेक्स्ट में एम्बेडेड URLs पर चलता है; लूपबैक लक्ष्य `loopback` नीति के अनुसार नियमों को शॉर्ट-सर्किट कर सकते हैं। IPv4-मैप्ड IPv6 लिटरल तुलना से पहले IPv4 रूप में सामान्यीकृत होते हैं, और प्रॉक्सी उन्हीं पतों पर कनेक्ट करता है जिन पर निर्णय लिया गया — दूसरा DNS रिज़ॉल्यूशन नहीं।
80
80
  - **ऑडिट** — अस्वीकृत कनेक्शन स्वामी सत्र में `permissionRules/network` जोड़ते हैं (वही अनुकूली `ignorable` द्वार), `/rules network` और settings पृष्ठ में ब्लॉक काउंटर व हाल की अवरोधन के साथ।
81
+ - **निदान** — अवरुद्ध कनेक्शन `[network: …]` संदेश लेकर आते हैं, और यदि मॉडल कॉल विफल होते समय प्रॉक्सी ने कोई लक्ष्य अवरुद्ध किया हो (इंजेक्ट किया गया प्रॉक्सी वातावरण होस्ट के LLM ट्रांसपोर्ट तक भी पहुँचता है), तो विफलता संदेश उस लक्ष्य और समाधान का नाम लेता है — केवल कच्चा ट्रांसपोर्ट त्रुटि नहीं दिखाता।
81
82
 
82
83
  ## Quick start
83
84
 
@@ -174,6 +175,7 @@ dsh --profile web --dump-config | grep -A4 'id: permission-rules'
174
175
  - **पथ उम्मीदवार अनुमानी हैं।** केवल दस्तावेज़ित तर्क कुंजियाँ पथ मिलान को खिलाती हैं, और कार्यक्षेत्र-सापेक्ष मिलान केवल `caseInsensitivePaths` चालू होने पर ASCII-केस-असंवेदी है।
175
176
  - **globs एक रूढ़िवादी उपसमुच्चय हैं।** कोई ब्रेस विस्तार नहीं — दो पैटर्न लिखें, या regex मोड उपयोग करें।
176
177
  - **regex बैकट्रैकिंग गार्ड संरचनात्मक है, संपूर्ण नहीं।** अविश्वसनीय फ़ाइलों के लिए glob मोड पसंद करें।
178
+ - **LLM प्रदाता endpoints के लिए allow नियम चाहिए।** इंजेक्ट किया गया प्रॉक्सी वातावरण पूरे प्रोसेस का है, इसलिए whitelist मोड में होस्ट का अपना मॉडल ट्रांसपोर्ट भी किसी अन्य कनेक्शन की तरह adjudicate होता है, और प्लगइन यह गिन नहीं सकता कि कोई adapter किन endpoints को कॉल करेगा (provider routes केवल ids देते हैं, और configured base URLs हर adapter की अपनी settings में रहते हैं)। प्रदाता endpoint के लिए allow नियम जोड़ें, या होस्ट प्रोसेस को प्रॉक्सी से बाहर रखने के लिए `network.injectEnv: false` सेट करें।
177
179
 
178
180
  ## Collaborating with dsh-auto-review
179
181
 
package/README.md CHANGED
@@ -46,7 +46,7 @@ Every hit **and** every passthrough is audit-logged as a `permissionRules/decisi
46
46
  - **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`.
47
47
  - **Hierarchical rule files** — optional `searchUp` merges every `.dsh/rules.yaml` from the session cwd to the filesystem root, nearest first.
48
48
  - **Dry-run rollout** — `enforce: false` audits what the policy *would* do while passing every call through.
49
- - **Hot reload** — Chokidar watch with debounce; a broken edit keeps the previous rules, never crashes.
49
+ - **Hot reload** — Chokidar watch with debounce; a broken edit keeps the previous rules, never crashes. On a WSL host, or for a rule file under `/mnt/<drive>`, the watch switches to polling because native change events are unreliable there.
50
50
  - **Fail loud** — invalid YAML, unknown actions/fields, bad globs/regexes, backtracking-prone patterns, or more than `maxRules` rules fail the load.
51
51
 
52
52
  ## Rule syntax
@@ -79,6 +79,7 @@ A Codex-style **process-level network policy**: shell subprocess traffic flows t
79
79
 
80
80
  - **Matching** — `match.network` with `domains` / `ips` / `ports` / `schemes` (globs, wildcards, CIDRs, port ranges; numeric YAML ports are accepted). URL-candidate extraction on the `tools/pre-execute` hot path fires on web-tool arguments and URLs embedded in bash/pwsh command text; loopback targets can short-circuit rules per `loopback` policy. IPv4-mapped IPv6 literals are normalized to their IPv4 form before matching, and the proxy connects on the addresses the decision was made on — never a second DNS resolution.
81
81
  - **Audit** — denied connections append `permissionRules/network` to the owning session (same adaptive `ignorable` gate), with block counters and recent interceptions in `/rules network` and the settings page.
82
+ - **Diagnosis** — blocked connections carry a `[network: …]` message, and a model call that fails while the proxy blocked a target (the injected proxy environment also reaches the host's LLM transport) names that target and the remediation in its failure text instead of failing as a bare transport error.
82
83
 
83
84
  ## Quick start
84
85
 
@@ -175,6 +176,7 @@ All tunables are Schemastery `Config` fields (changeable from cordis.yml). An id
175
176
  - **Path candidates are heuristic.** Only the documented argument keys feed path matching, and workspace-relative matching is ASCII-case-insensitive only when `caseInsensitivePaths` is on.
176
177
  - **Globs are a conservative subset.** No brace expansion — write two patterns, or use regex mode.
177
178
  - **The regex backtracking guard is structural, not exhaustive.** Prefer glob mode for untrusted files.
179
+ - **LLM provider endpoints need an allow rule.** The injected proxy environment is process-wide, so in whitelist mode the host's own model transport is adjudicated like any other connection, and the plugin cannot enumerate the endpoints an adapter will call (provider routes expose ids, and configured base URLs live in adapter-owned settings). Add an allow rule for the provider endpoint, or set `network.injectEnv: false` to keep the host process out of the proxy.
178
180
 
179
181
  ## Collaborating with dsh-auto-review
180
182
 
package/README.pt.md CHANGED
@@ -45,7 +45,7 @@ Cada acerto **e** cada passagem direta é registrada como um evento de sessão `
45
45
  - **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`.
46
46
  - **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.
47
47
  - **Implantação em dry-run** — `enforce: false` audita o que a política *faria* enquanto deixa cada chamada passar.
48
- - **Recarga a quente** — vigilância Chokidar com debounce; uma edição quebrada mantém as regras anteriores, nunca falha.
48
+ - **Recarga a quente** — vigilância Chokidar com debounce; uma edição quebrada mantém as regras anteriores, nunca falha. Num host WSL, ou para um arquivo de regras sob `/mnt/<unidade>`, a vigilância passa a polling porque os eventos nativos de mudança não são confiáveis ali.
49
49
  - **Falha ruidosa** — YAML inválido, ações/campos desconhecidos, globs/regexes ruins, padrões propensos a backtracking ou mais de `maxRules` regras falham a carga.
50
50
 
51
51
  ## Rule syntax
@@ -78,6 +78,7 @@ Uma **política de rede em nível de processo** estilo Codex: o tráfego de subp
78
78
 
79
79
  - **Correspondência** — `match.network` com `domains` / `ips` / `ports` / `schemes` (globs, curingas, CIDR, faixas de porta; portas YAML numéricas são aceitas). A extração de candidatos URL na rota quente `tools/pre-execute` dispara sobre argumentos de ferramentas web e URLs embutidas em texto de comando bash/pwsh; destinos de loopback podem curto-circuitar regras conforme a política `loopback`. Literais IPv6 mapeados em IPv4 são normalizados para a forma IPv4 antes da comparação, e o proxy conecta nos endereços usados na decisão, sem uma segunda resolução DNS.
80
80
  - **Auditoria** — conexões negadas anexam `permissionRules/network` à sessão proprietária (a mesma porta adaptativa `ignorable`), com contadores de bloqueio e intercepções recentes em `/rules network` e na página de settings.
81
+ - **Diagnóstico** — conexões bloqueadas trazem uma mensagem `[network: …]`, e uma chamada ao modelo que falha enquanto o proxy bloqueou um destino (o ambiente de proxy injetado também alcança o transporte LLM do host) nomeia esse destino e a solução no texto da falha, em vez de ficar como um erro de transporte cru.
81
82
 
82
83
  ## Quick start
83
84
 
@@ -174,6 +175,7 @@ Todos os parâmetros são campos Schemastery `Config` (alteráveis pelo cordis.y
174
175
  - **Candidatos de caminho são heurísticos.** Somente as chaves de argumento documentadas alimentam a correspondência de caminho, e a correspondência relativa ao workspace é insensível a maiúsculas ASCII apenas com `caseInsensitivePaths` ativado.
175
176
  - **Globs são um subconjunto conservador.** Sem expansão de chaves — escreva dois padrões, ou use o modo regex.
176
177
  - **A guarda de backtracking de regex é estrutural, não exaustiva.** Prefira o modo glob para arquivos não confiáveis.
178
+ - **Os endpoints do provedor LLM precisam de uma regra allow.** O ambiente de proxy injetado é do processo inteiro, então no modo whitelist o próprio transporte do modelo do host é adjudicado como qualquer outra conexão, e o plugin não consegue enumerar os endpoints que um adaptador vai chamar (as rotas de provedor expõem apenas ids, e as base URLs configuradas ficam nos settings de cada adaptador). Adicione uma regra allow para o endpoint do provedor, ou defina `network.injectEnv: false` para manter o processo do host fora do proxy.
177
179
 
178
180
  ## Collaborating with dsh-auto-review
179
181
 
package/README.zh.md CHANGED
@@ -45,7 +45,7 @@
45
45
  - **内置高危基线** —— 随附的 deny/ask 规则集(破坏性命令、权限提升、下载即执行、敏感路径),默认开启并追加在用户规则之后(更近的用户规则可覆盖);用 `builtin.enabled` 开关。
46
46
  - **分层规则文件** —— 可选 `searchUp` 从会话 cwd 到文件系统根合并每个 `.dsh/rules.yaml`,就近优先。
47
47
  - **试运行上线** —— `enforce: false` 审计策略*会*做什么,同时放行每个调用。
48
- - **热重载** —— Chokidar 监听带去抖;损坏的编辑保留旧规则、绝不崩溃。
48
+ - **热重载** —— Chokidar 监听带去抖;损坏的编辑保留旧规则、绝不崩溃。在 WSL 主机上(或规则文件位于 `/mnt/<盘符>` 下时)监听切换为轮询,因为该处的原生变更事件不可靠。
49
49
  - **失败大声** —— 非法 YAML、未知 action/字段、坏 glob/正则、易回溯模式或超过 `maxRules` 的规则在加载期失败。
50
50
 
51
51
  ## Rule syntax
@@ -78,6 +78,7 @@ Codex 风格的**进程级网络策略**:shell 子进程流量经内置本地
78
78
 
79
79
  - **匹配** —— `match.network` 用 `domains` / `ips` / `ports` / `schemes`(glob、通配符、CIDR、端口范围;数值型 YAML 端口可接受)。`tools/pre-execute` 热路径上的 URL 候选抽取作用于 web 工具参数与嵌入 bash/pwsh 命令文本的 URL;回环目标可按 `loopback` 策略短路规则。IPv4 映射的 IPv6 字面量在匹配前归一化为 IPv4 形式;代理按裁决到的地址建连,不做二次 DNS 解析。
80
80
  - **审计** —— 被拒连接向所属会话追加 `permissionRules/network`(同样的自适应 `ignorable` 门),块计数器与近期拦截在 `/rules network` 与设置页展示。
81
+ - **诊断** —— 被拦截的连接带 `[network: …]` 消息;若模型调用失败时代理恰好拦截了某个目标(注入的代理环境同样作用于宿主的 LLM 传输),失败文本会点名该目标与处置办法,而不再只是一个裸的传输错误。
81
82
 
82
83
  ## Quick start
83
84
 
@@ -174,6 +175,7 @@ dsh --profile web --dump-config | grep -A4 'id: permission-rules'
174
175
  - **路径候选是启发式的。** 只有文档化的参数键参与路径匹配,且工作区相对匹配仅在 `caseInsensitivePaths` 开启时忽略 ASCII 大小写。
175
176
  - **glob 是保守子集。** 无花括号展开——写两个模式,或用正则模式。
176
177
  - **正则回溯守卫是结构性的、非穷尽的。** 对不可信文件优先用 glob 模式。
178
+ - **LLM 供应商端点需要显式允许规则。** 注入的代理环境是进程级的,因此白名单模式下宿主自身的模型传输与其他连接一样被裁决,而插件无法枚举某个适配器将要调用的端点(供应商路由只暴露 id,配置的 base URL 存在各适配器自己的设置里)。请为供应商端点添加 allow 规则,或设 `network.injectEnv: false` 让宿主进程不经过该代理。
177
179
 
178
180
  ## Collaborating with dsh-auto-review
179
181
 
package/lib/index.js CHANGED
@@ -516,7 +516,7 @@ var RuleError = class extends Error {
516
516
  * @returns the validated document.
517
517
  */
518
518
  function parseRulesDocument(text) {
519
- const parsed = parseYaml(text);
519
+ const parsed = parseYaml(normalizeLineEndings(text));
520
520
  if (parsed === null || parsed === void 0) return { rules: [] };
521
521
  const root = asRecord(parsed, "rules document root");
522
522
  const unknownRoot = Object.keys(root).filter((key) => key !== "rules");
@@ -875,6 +875,25 @@ function compileParamPatterns(key, patterns, options) {
875
875
  negative
876
876
  };
877
877
  }
878
+ /**
879
+ * Normalize the line endings of one rule-file body before parsing. A file
880
+ * authored or checked out on the Windows side arrives with CRLF, and a lone
881
+ * CR — one not paired with an LF — is NOT a line break to the YAML parser in
882
+ * every position: it survives into the scalar it terminates, so a pattern
883
+ * spelled `domains: [registry.npmjs.org<CR>]`, or a final line ending in a
884
+ * bare CR, compiled a pattern that could never match its target (issue #19
885
+ * item 4). CRLF becomes LF; a stray CR inside a line is dropped (it is junk
886
+ * there — a deliberate CR is written as the YAML escape `\r` and never
887
+ * reaches this text); and a CR-only file (classic Mac line endings, no LF
888
+ * anywhere) treats every CR as its line separator. Every line-ending style
889
+ * therefore parses to the same document.
890
+ * @param text - the raw rule-file text.
891
+ * @returns the same text with LF line breaks and no stray CR.
892
+ */
893
+ function normalizeLineEndings(text) {
894
+ if (!text.includes("\n")) return text.replace(/\r/g, "\n");
895
+ return text.replace(/\r\n/g, "\n").replace(/\r/g, "");
896
+ }
878
897
  /** Parse YAML through the `yaml` dependency, mapping parse failures onto {@link RuleError}. */
879
898
  function parseYaml(text) {
880
899
  try {
@@ -2187,13 +2206,22 @@ var NetworkProxy = class {
2187
2206
  }
2188
2207
  /**
2189
2208
  * Connect to the first adjudicated address that accepts, so the tunnel lands
2190
- * on an address the decision approved; a hostname target with no resolution
2191
- * keeps the historical hostname connect. Each candidate is tried in order so
2192
- * a multi-address host does not lose its fallback.
2209
+ * on an address the decision approved. Each candidate is tried in order so a
2210
+ * multi-address host does not lose its fallback.
2211
+ *
2212
+ * A target whose adjudication resolved no address FAILS here instead of
2213
+ * dialing the hostname (issue #21): dialing the name would be a second DNS
2214
+ * resolution — the answer could differ from the one the rules were evaluated
2215
+ * against — and it is the one remaining path where Node's happy-eyeballs
2216
+ * race applies, with its 250 ms `autoSelectFamilyAttemptTimeout` default
2217
+ * that kills any endpoint further away than that. Dialing an already-
2218
+ * resolved literal skips that algorithm entirely (net.js connects an IP
2219
+ * without a lookup), so the tunnel only ever reaches an adjudicated
2220
+ * address; the client's retry resolves and adjudicates again.
2193
2221
  */
2194
2222
  async connectUpstream(target, port) {
2195
2223
  const pinned = pinnedAddresses(target);
2196
- if (pinned.length === 0) return connect(port, target.host);
2224
+ if (pinned.length === 0) throw new Error(`no adjudicated address for ${target.host}`);
2197
2225
  let lastError;
2198
2226
  for (const address of pinned) try {
2199
2227
  return await new Promise((resolve, reject) => {
@@ -2547,6 +2575,60 @@ const WEB_TOOLS = ["web_fetch", "web_search"];
2547
2575
  /** Stale in-flight entries are dropped after this many milliseconds (attribution is best-effort). */
2548
2576
  const IN_FLIGHT_TTL_MS = 6e5;
2549
2577
  /**
2578
+ * Clock slack for attributing a proxy block to a model call: a block recorded
2579
+ * within this window before the call started still counts, so millisecond
2580
+ * granularity or a listener ordering between the retry middleware and this
2581
+ * waterfall cannot hide the block that caused the failure.
2582
+ */
2583
+ const LLM_BLOCK_SKEW_MS = 1e3;
2584
+ /**
2585
+ * Chokidar polling interval used when the watched path lives on a filesystem
2586
+ * whose change events are unreliable (WSL drvfs/9p mounts): polling costs a
2587
+ * stat per interval, which is negligible for a rule file and far cheaper than
2588
+ * a rule edit that silently never hot-reloads.
2589
+ */
2590
+ const POLLING_WATCH_INTERVAL_MS = 300;
2591
+ /**
2592
+ * Whether one watched path lies under a WSL drvfs mount (`/mnt/<drive>/…`),
2593
+ * where inotify-style change events are known to be unreliable — chokidar
2594
+ * then has to poll (issue #19 item 1). The separator is normalized first so
2595
+ * the check also recognizes the path as the Windows side spells it; a
2596
+ * Windows drive path (`D:\…`) never matches.
2597
+ * @param path - the watched file or directory path.
2598
+ * @returns true when the path addresses a drvfs mount.
2599
+ */
2600
+ function isDvrfsPath(path) {
2601
+ return /^\/mnt\/[a-z](?:\/|$)/i.test(path.replace(/\\/g, "/"));
2602
+ }
2603
+ /**
2604
+ * Whether the running kernel is WSL's, as `/proc/version` reports it. Every
2605
+ * WSL kernel string carries `microsoft` (`…-microsoft-standard-WSL2`), which
2606
+ * is what the issue asks the detection to key on; a non-WSL Linux kernel
2607
+ * never does, and hosts without `/proc/version` (Windows, macOS) answer
2608
+ * false.
2609
+ * @param procVersion - the `/proc/version` text, or an empty string when it cannot be read.
2610
+ * @returns true when the kernel is Microsoft's.
2611
+ */
2612
+ function isWslKernel(procVersion) {
2613
+ return procVersion.toLowerCase().includes("microsoft");
2614
+ }
2615
+ /** `/proc/version` is immutable for the process lifetime: read it at most once. */
2616
+ let procVersionText;
2617
+ /** Read `/proc/version`, or an empty string wherever the file is absent (Windows, macOS). */
2618
+ function readProcVersion() {
2619
+ if (procVersionText === void 0) try {
2620
+ procVersionText = readFileSync("/proc/version", "utf8");
2621
+ } catch {
2622
+ procVersionText = "";
2623
+ }
2624
+ return procVersionText;
2625
+ }
2626
+ /** Chokidar options that switch one watch to polling (drvfs mount or WSL host). */
2627
+ const POLLING_WATCH_OPTIONS = {
2628
+ usePolling: true,
2629
+ interval: POLLING_WATCH_INTERVAL_MS
2630
+ };
2631
+ /**
2550
2632
  * State and behavior. One instance per plugin mount; disposals are owned by
2551
2633
  * the watcher/timer/proxy effects registered in {@link apply}.
2552
2634
  */
@@ -2558,6 +2640,10 @@ var PermissionRulesRuntime = class {
2558
2640
  compileCache = /* @__PURE__ */ new Map();
2559
2641
  /** Live watchers per watched path (rule file or candidate ancestor directory), with the cwds each serves. */
2560
2642
  watchers = /* @__PURE__ */ new Map();
2643
+ /** The configured chain session-less proxy connections are judged against (see {@link hostChain}); never a `byCwd` member. */
2644
+ hostLoaded;
2645
+ /** Whether the one-time "configured chain unusable" warning was already logged. */
2646
+ hostChainWarned = false;
2561
2647
  /** Debounce timers per rule-file path. */
2562
2648
  timers = /* @__PURE__ */ new Map();
2563
2649
  /** Whether the host honors the audit envelope's `ignorable` marker: unknown until the first decision (or peer-version check). */
@@ -3015,8 +3101,91 @@ var PermissionRulesRuntime = class {
3015
3101
  ruleset: loaded.compiled,
3016
3102
  sources: loaded.sources
3017
3103
  });
3104
+ if (chains.length === 0) {
3105
+ const host = this.hostChain();
3106
+ if (host !== void 0) chains.push(host);
3107
+ }
3018
3108
  return chains;
3019
3109
  }
3110
+ /**
3111
+ * The CONFIGURED chain a session-less connection is judged against: the
3112
+ * chain for the host process's own working directory (project rule file →
3113
+ * an absolute `rulesFile` → the configured fallback → the shipped
3114
+ * baseline), loaded on first use. Proxy traffic that carries no session —
3115
+ * the harness's own plugin-market catalog/update/npm lookups at boot — used
3116
+ * to be judged against an empty chain and therefore always hit the mode
3117
+ * default, so a target an already-configured allow rule permits stayed
3118
+ * blocked until some session had run a tool call (issue #18). The chain is
3119
+ * kept OUT of `byCwd` on purpose: it must never outrank a session workspace
3120
+ * chain, and it stops being consulted as soon as one is loaded (a host-level
3121
+ * connection is then judged exactly as before, against the loaded workspace
3122
+ * chains). It is not watched either: the session-less window is the boot
3123
+ * window, and the first tool call of any session takes over with the normal
3124
+ * per-workspace chain, which is watched.
3125
+ * @returns the configured chain, or undefined when it cannot be loaded (the mode default then applies).
3126
+ */
3127
+ hostChain() {
3128
+ if (this.hostLoaded === void 0) try {
3129
+ this.hostLoaded = this.load(process.cwd());
3130
+ } catch (error) {
3131
+ if (!this.hostChainWarned) {
3132
+ this.hostChainWarned = true;
3133
+ this.ctx.logger.warn(`permission-rules: cannot load the configured rules for session-less connections at ${process.cwd()}: ${String(error)} (the network mode default applies)`);
3134
+ }
3135
+ return;
3136
+ }
3137
+ return {
3138
+ ruleset: this.hostLoaded.compiled,
3139
+ sources: this.hostLoaded.sources
3140
+ };
3141
+ }
3142
+ /**
3143
+ * The `llm/stream` waterfall listener (issue #22). The proxy environment
3144
+ * this plugin injects for shell subprocesses is process-wide, so it is also
3145
+ * visible to the harness's own LLM transport; a provider endpoint that the
3146
+ * policy blocks then surfaces as a bare transport failure ("Connection
3147
+ * error. / TRANSPORT" after the retry budget) with nothing pointing at the
3148
+ * policy. This wrapper is transparent for every call: it only ENRICHES a
3149
+ * failure whose window contains a proxy block, so no turn fails silently on
3150
+ * a policy decision the user cannot see.
3151
+ * @param next - the downstream chain (the resolved adapter's stream).
3152
+ * @returns the same chunks, with any failure carrying the policy diagnosis.
3153
+ */
3154
+ llmStream(next) {
3155
+ return this.diagnosedLlmStream(next);
3156
+ }
3157
+ /** Delegate one model call and enrich its failure with the proxy diagnosis, if any. */
3158
+ async *diagnosedLlmStream(next) {
3159
+ const startedAt = Date.now();
3160
+ try {
3161
+ yield* next();
3162
+ } catch (error) {
3163
+ const diagnosis = this.llmFailureDiagnosis(startedAt);
3164
+ if (diagnosis !== void 0) {
3165
+ this.ctx.logger.warn(`permission-rules: ${diagnosis}`);
3166
+ if (error instanceof Error && !error.message.includes("[network:")) error.message = `${error.message} — ${diagnosis}`;
3167
+ }
3168
+ throw error;
3169
+ }
3170
+ }
3171
+ /**
3172
+ * The policy diagnosis for one failed model call, or undefined when the
3173
+ * proxy blocked nothing while it ran. The newest block in the window names
3174
+ * the target and the mode, and the remediation is the honest one: allow the
3175
+ * endpoint or stop injecting the policy environment into the host process.
3176
+ */
3177
+ llmFailureDiagnosis(since) {
3178
+ const proxy = this.networkProxy;
3179
+ if (proxy === void 0) return void 0;
3180
+ const blocks = proxy.recentBlocks().filter((record) => record.time >= since - LLM_BLOCK_SKEW_MS);
3181
+ const newest = blocks[0];
3182
+ if (newest === void 0) return void 0;
3183
+ const target = `${newest.scheme ?? "https"}://${newest.domain}${newest.port !== void 0 ? `:${newest.port}` : ""}`;
3184
+ const marker = newest.action === "ask" ? "[network: blocked pending approval]" : "[network: denied]";
3185
+ const rule = newest.matched ? `, rule ${(newest.ruleIndex ?? 0) + 1}${newest.source !== "" ? ` (${newest.source})` : ""}` : ", no matching allow rule";
3186
+ const more = blocks.length > 1 ? ` (and ${blocks.length - 1} more block${blocks.length > 2 ? "s" : ""} in the same window)` : "";
3187
+ return `${marker} the local policy proxy blocked ${target} during this model call (mode ${newest.mode}${rule})${more} — the host process inherits the policy proxy environment (network.injectEnv), so the model transport is adjudicated like any other connection; add an allow rule for the provider endpoint or set network.injectEnv: false`;
3188
+ }
3020
3189
  /** Mark one delegated shell execution as in-flight (newest attribution wins). */
3021
3190
  markShell(exec) {
3022
3191
  this.inFlight.set(String(exec.callId), {
@@ -3534,6 +3703,19 @@ var PermissionRulesRuntime = class {
3534
3703
  throw error instanceof RuleError ? error : new RuleError(`permission-rules: cannot load ${filePath}: ${message}`);
3535
3704
  }
3536
3705
  }
3706
+ /**
3707
+ * Whether one watched path must be polled instead of trusting the platform's
3708
+ * change events. WSL is the case issue #19 item 1 reports: a rule file under
3709
+ * `/mnt/<drive>` (drvfs/9p) or any file on a WSL host can stop delivering
3710
+ * chokidar change events with no error at all, so the watcher edits would
3711
+ * silently stop hot-reloading. Polling is the documented chokidar remedy;
3712
+ * everywhere else the native watcher stays in place.
3713
+ * @param path - the watched rule file or candidate directory.
3714
+ * @returns true when the watch should run in polling mode.
3715
+ */
3716
+ watchPollingFor(path) {
3717
+ return isDvrfsPath(path) || isWslKernel(readProcVersion());
3718
+ }
3537
3719
  /** Attach (once per file path) the Chokidar watcher feeding {@link reload}. */
3538
3720
  attachWatch(cwd, source) {
3539
3721
  if (!this.config.watch || source === "" || this.config.builtin.enabled && source === this.config.builtin.path) return;
@@ -3543,9 +3725,11 @@ var PermissionRulesRuntime = class {
3543
3725
  return;
3544
3726
  }
3545
3727
  const cwds = /* @__PURE__ */ new Set([cwd]);
3728
+ const polling = this.watchPollingFor(source);
3546
3729
  const watcher = chokidar.watch(source, {
3547
3730
  persistent: true,
3548
- ignoreInitial: true
3731
+ ignoreInitial: true,
3732
+ ...polling ? POLLING_WATCH_OPTIONS : {}
3549
3733
  });
3550
3734
  const onEvent = () => this.scheduleReload(source);
3551
3735
  watcher.on("add", onEvent);
@@ -3641,6 +3825,7 @@ var PermissionRulesRuntime = class {
3641
3825
  const watchOptions = {
3642
3826
  persistent: true,
3643
3827
  ignoreInitial: true,
3828
+ ...this.watchPollingFor(candidate) ? POLLING_WATCH_OPTIONS : {},
3644
3829
  ignored: (path) => path.split(sep).includes("node_modules") || path.split(sep).includes(".git")
3645
3830
  };
3646
3831
  const watcher = dir === parent ? chokidar.watch(dir, watchOptions) : chokidar.watch(dir, {
@@ -3784,7 +3969,10 @@ async function apply(ctx, config) {
3784
3969
  return next();
3785
3970
  });
3786
3971
  attachSettingsSection(ctx, runtime, config);
3787
- if (resolved.network.enabled) await runtime.attachNetworkProxy();
3972
+ if (resolved.network.enabled) {
3973
+ await runtime.attachNetworkProxy();
3974
+ ctx.on("llm/stream", (_options, next) => runtime.llmStream(next));
3975
+ }
3788
3976
  ctx.inject(["systemPrompt"], (scope) => {
3789
3977
  const systemPrompt = scope.get("systemPrompt");
3790
3978
  if (systemPrompt?.context === void 0) return;
@@ -101,9 +101,18 @@ export declare class NetworkProxy {
101
101
  private handleRequest;
102
102
  /**
103
103
  * Connect to the first adjudicated address that accepts, so the tunnel lands
104
- * on an address the decision approved; a hostname target with no resolution
105
- * keeps the historical hostname connect. Each candidate is tried in order so
106
- * a multi-address host does not lose its fallback.
104
+ * on an address the decision approved. Each candidate is tried in order so a
105
+ * multi-address host does not lose its fallback.
106
+ *
107
+ * A target whose adjudication resolved no address FAILS here instead of
108
+ * dialing the hostname (issue #21): dialing the name would be a second DNS
109
+ * resolution — the answer could differ from the one the rules were evaluated
110
+ * against — and it is the one remaining path where Node's happy-eyeballs
111
+ * race applies, with its 250 ms `autoSelectFamilyAttemptTimeout` default
112
+ * that kills any endpoint further away than that. Dialing an already-
113
+ * resolved literal skips that algorithm entirely (net.js connects an IP
114
+ * without a lookup), so the tunnel only ever reaches an adjudicated
115
+ * address; the client's retry resolves and adjudicates again.
107
116
  */
108
117
  private connectUpstream;
109
118
  /** CONNECT tunneling (HTTPS and friends): adjudicate, then either 403 or an established TCP tunnel. */
@@ -1 +1 @@
1
- {"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../../src/proxy.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../../src/proxy.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"rules.d.ts","sourceRoot":"","sources":["../../src/rules.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"rules.d.ts","sourceRoot":"","sources":["../../src/rules.ts"],"names":[],"mappings":"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"}
@@ -12,6 +12,7 @@
12
12
  */
13
13
  import type { Context } from '@deepseek-ai/cordis';
14
14
  import type { CommandInvocation, CommandResult } from '@deepseek-ai/dsh-commands';
15
+ import type { StreamChunk } from '@deepseek-ai/dsh-llm';
15
16
  import type { PreToolDecision, ToolExecution } from '@deepseek-ai/dsh-tools';
16
17
  import type { Config, ResolvedConfig } from './config.js';
17
18
  import type { CompiledRuleset, RuleHit } from './rules.js';
@@ -33,6 +34,26 @@ interface LoadedRules {
33
34
  /** `true` when the last initial load threw under `badFilePolicy: 'fail'`. */
34
35
  readonly failed?: true;
35
36
  }
37
+ /**
38
+ * Whether one watched path lies under a WSL drvfs mount (`/mnt/<drive>/…`),
39
+ * where inotify-style change events are known to be unreliable — chokidar
40
+ * then has to poll (issue #19 item 1). The separator is normalized first so
41
+ * the check also recognizes the path as the Windows side spells it; a
42
+ * Windows drive path (`D:\…`) never matches.
43
+ * @param path - the watched file or directory path.
44
+ * @returns true when the path addresses a drvfs mount.
45
+ */
46
+ export declare function isDvrfsPath(path: string): boolean;
47
+ /**
48
+ * Whether the running kernel is WSL's, as `/proc/version` reports it. Every
49
+ * WSL kernel string carries `microsoft` (`…-microsoft-standard-WSL2`), which
50
+ * is what the issue asks the detection to key on; a non-WSL Linux kernel
51
+ * never does, and hosts without `/proc/version` (Windows, macOS) answer
52
+ * false.
53
+ * @param procVersion - the `/proc/version` text, or an empty string when it cannot be read.
54
+ * @returns true when the kernel is Microsoft's.
55
+ */
56
+ export declare function isWslKernel(procVersion: string): boolean;
36
57
  /**
37
58
  * State and behavior. One instance per plugin mount; disposals are owned by
38
59
  * the watcher/timer/proxy effects registered in {@link apply}.
@@ -45,6 +66,10 @@ export declare class PermissionRulesRuntime {
45
66
  private readonly compileCache;
46
67
  /** Live watchers per watched path (rule file or candidate ancestor directory), with the cwds each serves. */
47
68
  private readonly watchers;
69
+ /** The configured chain session-less proxy connections are judged against (see {@link hostChain}); never a `byCwd` member. */
70
+ private hostLoaded;
71
+ /** Whether the one-time "configured chain unusable" warning was already logged. */
72
+ private hostChainWarned;
48
73
  /** Debounce timers per rule-file path. */
49
74
  private readonly timers;
50
75
  /** Whether the host honors the audit envelope's `ignorable` marker: unknown until the first decision (or peer-version check). */
@@ -222,6 +247,46 @@ export declare class PermissionRulesRuntime {
222
247
  private decideProxyTarget;
223
248
  /** The loaded rule chains in cache order, nearest workspace first. */
224
249
  private proxyChains;
250
+ /**
251
+ * The CONFIGURED chain a session-less connection is judged against: the
252
+ * chain for the host process's own working directory (project rule file →
253
+ * an absolute `rulesFile` → the configured fallback → the shipped
254
+ * baseline), loaded on first use. Proxy traffic that carries no session —
255
+ * the harness's own plugin-market catalog/update/npm lookups at boot — used
256
+ * to be judged against an empty chain and therefore always hit the mode
257
+ * default, so a target an already-configured allow rule permits stayed
258
+ * blocked until some session had run a tool call (issue #18). The chain is
259
+ * kept OUT of `byCwd` on purpose: it must never outrank a session workspace
260
+ * chain, and it stops being consulted as soon as one is loaded (a host-level
261
+ * connection is then judged exactly as before, against the loaded workspace
262
+ * chains). It is not watched either: the session-less window is the boot
263
+ * window, and the first tool call of any session takes over with the normal
264
+ * per-workspace chain, which is watched.
265
+ * @returns the configured chain, or undefined when it cannot be loaded (the mode default then applies).
266
+ */
267
+ private hostChain;
268
+ /**
269
+ * The `llm/stream` waterfall listener (issue #22). The proxy environment
270
+ * this plugin injects for shell subprocesses is process-wide, so it is also
271
+ * visible to the harness's own LLM transport; a provider endpoint that the
272
+ * policy blocks then surfaces as a bare transport failure ("Connection
273
+ * error. / TRANSPORT" after the retry budget) with nothing pointing at the
274
+ * policy. This wrapper is transparent for every call: it only ENRICHES a
275
+ * failure whose window contains a proxy block, so no turn fails silently on
276
+ * a policy decision the user cannot see.
277
+ * @param next - the downstream chain (the resolved adapter's stream).
278
+ * @returns the same chunks, with any failure carrying the policy diagnosis.
279
+ */
280
+ llmStream(next: () => AsyncIterable<StreamChunk>): AsyncIterable<StreamChunk>;
281
+ /** Delegate one model call and enrich its failure with the proxy diagnosis, if any. */
282
+ private diagnosedLlmStream;
283
+ /**
284
+ * The policy diagnosis for one failed model call, or undefined when the
285
+ * proxy blocked nothing while it ran. The newest block in the window names
286
+ * the target and the mode, and the remediation is the honest one: allow the
287
+ * endpoint or stop injecting the policy environment into the host process.
288
+ */
289
+ private llmFailureDiagnosis;
225
290
  /** Mark one delegated shell execution as in-flight (newest attribution wins). */
226
291
  private markShell;
227
292
  /** Drop one shell execution when it settles. */
@@ -344,6 +409,17 @@ export declare class PermissionRulesRuntime {
344
409
  validateDeploymentFiles(): void;
345
410
  /** Parse and compile one mandated file, rethrowing as a mount failure. */
346
411
  private loadForValidation;
412
+ /**
413
+ * Whether one watched path must be polled instead of trusting the platform's
414
+ * change events. WSL is the case issue #19 item 1 reports: a rule file under
415
+ * `/mnt/<drive>` (drvfs/9p) or any file on a WSL host can stop delivering
416
+ * chokidar change events with no error at all, so the watcher edits would
417
+ * silently stop hot-reloading. Polling is the documented chokidar remedy;
418
+ * everywhere else the native watcher stays in place.
419
+ * @param path - the watched rule file or candidate directory.
420
+ * @returns true when the watch should run in polling mode.
421
+ */
422
+ watchPollingFor(path: string): boolean;
347
423
  /** Attach (once per file path) the Chokidar watcher feeding {@link reload}. */
348
424
  private attachWatch;
349
425
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/runtime.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/runtime.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-permission-rules",
3
- "version": "0.6.18",
3
+ "version": "0.6.19",
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",
package/src/proxy.ts CHANGED
@@ -199,13 +199,22 @@ export class NetworkProxy {
199
199
 
200
200
  /**
201
201
  * Connect to the first adjudicated address that accepts, so the tunnel lands
202
- * on an address the decision approved; a hostname target with no resolution
203
- * keeps the historical hostname connect. Each candidate is tried in order so
204
- * a multi-address host does not lose its fallback.
202
+ * on an address the decision approved. Each candidate is tried in order so a
203
+ * multi-address host does not lose its fallback.
204
+ *
205
+ * A target whose adjudication resolved no address FAILS here instead of
206
+ * dialing the hostname (issue #21): dialing the name would be a second DNS
207
+ * resolution — the answer could differ from the one the rules were evaluated
208
+ * against — and it is the one remaining path where Node's happy-eyeballs
209
+ * race applies, with its 250 ms `autoSelectFamilyAttemptTimeout` default
210
+ * that kills any endpoint further away than that. Dialing an already-
211
+ * resolved literal skips that algorithm entirely (net.js connects an IP
212
+ * without a lookup), so the tunnel only ever reaches an adjudicated
213
+ * address; the client's retry resolves and adjudicates again.
205
214
  */
206
215
  private async connectUpstream(target: NetworkTarget, port: number): Promise<Duplex> {
207
216
  const pinned = pinnedAddresses(target)
208
- if (pinned.length === 0) return connect(port, target.host)
217
+ if (pinned.length === 0) throw new Error(`no adjudicated address for ${target.host}`)
209
218
  let lastError: unknown
210
219
  for (const address of pinned) {
211
220
  try {
package/src/rules.ts CHANGED
@@ -291,7 +291,7 @@ export interface MatchContext {
291
291
  * @returns the validated document.
292
292
  */
293
293
  export function parseRulesDocument(text: string): RulesFileDoc {
294
- const parsed = parseYaml(text)
294
+ const parsed = parseYaml(normalizeLineEndings(text))
295
295
  // An empty (or comment-only) file is a valid empty rule set.
296
296
  if (parsed === null || parsed === undefined) return { rules: [] }
297
297
  const root = asRecord(parsed, 'rules document root')
@@ -714,6 +714,26 @@ function compileParamPatterns(key: string, patterns: readonly string[], options:
714
714
 
715
715
  // --- YAML/shape validation -------------------------------------------------
716
716
 
717
+ /**
718
+ * Normalize the line endings of one rule-file body before parsing. A file
719
+ * authored or checked out on the Windows side arrives with CRLF, and a lone
720
+ * CR — one not paired with an LF — is NOT a line break to the YAML parser in
721
+ * every position: it survives into the scalar it terminates, so a pattern
722
+ * spelled `domains: [registry.npmjs.org<CR>]`, or a final line ending in a
723
+ * bare CR, compiled a pattern that could never match its target (issue #19
724
+ * item 4). CRLF becomes LF; a stray CR inside a line is dropped (it is junk
725
+ * there — a deliberate CR is written as the YAML escape `\r` and never
726
+ * reaches this text); and a CR-only file (classic Mac line endings, no LF
727
+ * anywhere) treats every CR as its line separator. Every line-ending style
728
+ * therefore parses to the same document.
729
+ * @param text - the raw rule-file text.
730
+ * @returns the same text with LF line breaks and no stray CR.
731
+ */
732
+ function normalizeLineEndings(text: string): string {
733
+ if (!text.includes('\n')) return text.replace(/\r/g, '\n')
734
+ return text.replace(/\r\n/g, '\n').replace(/\r/g, '')
735
+ }
736
+
717
737
  /** Parse YAML through the `yaml` dependency, mapping parse failures onto {@link RuleError}. */
718
738
  function parseYaml(text: string): unknown {
719
739
  try {
package/src/runtime.ts CHANGED
@@ -18,6 +18,7 @@ import { dirname, isAbsolute, join, resolve, sep } from 'node:path'
18
18
  import chokidar from 'chokidar'
19
19
  import type { Context } from '@deepseek-ai/cordis'
20
20
  import type { CommandInvocation, CommandResult } from '@deepseek-ai/dsh-commands'
21
+ import type { GenerateOptions, StreamChunk } from '@deepseek-ai/dsh-llm'
21
22
  import type { PreToolDecision, ToolExecution } from '@deepseek-ai/dsh-tools'
22
23
  import type { SessionEvent } from '@deepseek-ai/dsh-session'
23
24
  import { resolveConfig } from './config.ts'
@@ -81,6 +82,66 @@ const WEB_TOOLS: readonly string[] = ['web_fetch', 'web_search']
81
82
  /** Stale in-flight entries are dropped after this many milliseconds (attribution is best-effort). */
82
83
  const IN_FLIGHT_TTL_MS = 10 * 60 * 1000
83
84
 
85
+ /**
86
+ * Clock slack for attributing a proxy block to a model call: a block recorded
87
+ * within this window before the call started still counts, so millisecond
88
+ * granularity or a listener ordering between the retry middleware and this
89
+ * waterfall cannot hide the block that caused the failure.
90
+ */
91
+ const LLM_BLOCK_SKEW_MS = 1000
92
+
93
+ /**
94
+ * Chokidar polling interval used when the watched path lives on a filesystem
95
+ * whose change events are unreliable (WSL drvfs/9p mounts): polling costs a
96
+ * stat per interval, which is negligible for a rule file and far cheaper than
97
+ * a rule edit that silently never hot-reloads.
98
+ */
99
+ const POLLING_WATCH_INTERVAL_MS = 300
100
+
101
+ /**
102
+ * Whether one watched path lies under a WSL drvfs mount (`/mnt/<drive>/…`),
103
+ * where inotify-style change events are known to be unreliable — chokidar
104
+ * then has to poll (issue #19 item 1). The separator is normalized first so
105
+ * the check also recognizes the path as the Windows side spells it; a
106
+ * Windows drive path (`D:\…`) never matches.
107
+ * @param path - the watched file or directory path.
108
+ * @returns true when the path addresses a drvfs mount.
109
+ */
110
+ export function isDvrfsPath(path: string): boolean {
111
+ return /^\/mnt\/[a-z](?:\/|$)/i.test(path.replace(/\\/g, '/'))
112
+ }
113
+
114
+ /**
115
+ * Whether the running kernel is WSL's, as `/proc/version` reports it. Every
116
+ * WSL kernel string carries `microsoft` (`…-microsoft-standard-WSL2`), which
117
+ * is what the issue asks the detection to key on; a non-WSL Linux kernel
118
+ * never does, and hosts without `/proc/version` (Windows, macOS) answer
119
+ * false.
120
+ * @param procVersion - the `/proc/version` text, or an empty string when it cannot be read.
121
+ * @returns true when the kernel is Microsoft's.
122
+ */
123
+ export function isWslKernel(procVersion: string): boolean {
124
+ return procVersion.toLowerCase().includes('microsoft')
125
+ }
126
+
127
+ /** `/proc/version` is immutable for the process lifetime: read it at most once. */
128
+ let procVersionText: string | undefined
129
+
130
+ /** Read `/proc/version`, or an empty string wherever the file is absent (Windows, macOS). */
131
+ function readProcVersion(): string {
132
+ if (procVersionText === undefined) {
133
+ try {
134
+ procVersionText = readFileSync('/proc/version', 'utf8')
135
+ } catch {
136
+ procVersionText = ''
137
+ }
138
+ }
139
+ return procVersionText
140
+ }
141
+
142
+ /** Chokidar options that switch one watch to polling (drvfs mount or WSL host). */
143
+ const POLLING_WATCH_OPTIONS = { usePolling: true, interval: POLLING_WATCH_INTERVAL_MS } as const
144
+
84
145
  /**
85
146
  * State and behavior. One instance per plugin mount; disposals are owned by
86
147
  * the watcher/timer/proxy effects registered in {@link apply}.
@@ -95,6 +156,12 @@ export class PermissionRulesRuntime {
95
156
  /** Live watchers per watched path (rule file or candidate ancestor directory), with the cwds each serves. */
96
157
  private readonly watchers = new Map<string, WatcherEntry>()
97
158
 
159
+ /** The configured chain session-less proxy connections are judged against (see {@link hostChain}); never a `byCwd` member. */
160
+ private hostLoaded: LoadedRules | undefined
161
+
162
+ /** Whether the one-time "configured chain unusable" warning was already logged. */
163
+ private hostChainWarned = false
164
+
98
165
  /** Debounce timers per rule-file path. */
99
166
  private readonly timers = new Map<string, ReturnType<typeof setTimeout>>()
100
167
 
@@ -525,9 +592,101 @@ export class PermissionRulesRuntime {
525
592
  for (const loaded of this.byCwd.values()) {
526
593
  chains.push({ ruleset: loaded.compiled, sources: loaded.sources })
527
594
  }
595
+ if (chains.length === 0) {
596
+ const host = this.hostChain()
597
+ if (host !== undefined) chains.push(host)
598
+ }
528
599
  return chains
529
600
  }
530
601
 
602
+ /**
603
+ * The CONFIGURED chain a session-less connection is judged against: the
604
+ * chain for the host process's own working directory (project rule file →
605
+ * an absolute `rulesFile` → the configured fallback → the shipped
606
+ * baseline), loaded on first use. Proxy traffic that carries no session —
607
+ * the harness's own plugin-market catalog/update/npm lookups at boot — used
608
+ * to be judged against an empty chain and therefore always hit the mode
609
+ * default, so a target an already-configured allow rule permits stayed
610
+ * blocked until some session had run a tool call (issue #18). The chain is
611
+ * kept OUT of `byCwd` on purpose: it must never outrank a session workspace
612
+ * chain, and it stops being consulted as soon as one is loaded (a host-level
613
+ * connection is then judged exactly as before, against the loaded workspace
614
+ * chains). It is not watched either: the session-less window is the boot
615
+ * window, and the first tool call of any session takes over with the normal
616
+ * per-workspace chain, which is watched.
617
+ * @returns the configured chain, or undefined when it cannot be loaded (the mode default then applies).
618
+ */
619
+ private hostChain(): NetworkChain | undefined {
620
+ if (this.hostLoaded === undefined) {
621
+ try {
622
+ this.hostLoaded = this.load(process.cwd())
623
+ } catch (error: unknown) {
624
+ // Never throw into the proxy: an unusable configured chain degrades to
625
+ // the mode default (fail closed), with one warning per mount.
626
+ if (!this.hostChainWarned) {
627
+ this.hostChainWarned = true
628
+ this.ctx.logger.warn(`permission-rules: cannot load the configured rules for session-less connections at ${process.cwd()}: ${String(error)} (the network mode default applies)`)
629
+ }
630
+ return undefined
631
+ }
632
+ }
633
+ return { ruleset: this.hostLoaded.compiled, sources: this.hostLoaded.sources }
634
+ }
635
+
636
+ /**
637
+ * The `llm/stream` waterfall listener (issue #22). The proxy environment
638
+ * this plugin injects for shell subprocesses is process-wide, so it is also
639
+ * visible to the harness's own LLM transport; a provider endpoint that the
640
+ * policy blocks then surfaces as a bare transport failure ("Connection
641
+ * error. / TRANSPORT" after the retry budget) with nothing pointing at the
642
+ * policy. This wrapper is transparent for every call: it only ENRICHES a
643
+ * failure whose window contains a proxy block, so no turn fails silently on
644
+ * a policy decision the user cannot see.
645
+ * @param next - the downstream chain (the resolved adapter's stream).
646
+ * @returns the same chunks, with any failure carrying the policy diagnosis.
647
+ */
648
+ llmStream(next: () => AsyncIterable<StreamChunk>): AsyncIterable<StreamChunk> {
649
+ return this.diagnosedLlmStream(next)
650
+ }
651
+
652
+ /** Delegate one model call and enrich its failure with the proxy diagnosis, if any. */
653
+ private async *diagnosedLlmStream(next: () => AsyncIterable<StreamChunk>): AsyncGenerator<StreamChunk> {
654
+ const startedAt = Date.now()
655
+ try {
656
+ yield* next()
657
+ } catch (error: unknown) {
658
+ const diagnosis = this.llmFailureDiagnosis(startedAt)
659
+ if (diagnosis !== undefined) {
660
+ this.ctx.logger.warn(`permission-rules: ${diagnosis}`)
661
+ // Keep the original error object (its class, code, and retry facts)
662
+ // and only extend the message, so the failure taxonomy is unchanged.
663
+ if (error instanceof Error && !error.message.includes('[network:')) error.message = `${error.message} — ${diagnosis}`
664
+ }
665
+ throw error
666
+ }
667
+ }
668
+
669
+ /**
670
+ * The policy diagnosis for one failed model call, or undefined when the
671
+ * proxy blocked nothing while it ran. The newest block in the window names
672
+ * the target and the mode, and the remediation is the honest one: allow the
673
+ * endpoint or stop injecting the policy environment into the host process.
674
+ */
675
+ private llmFailureDiagnosis(since: number): string | undefined {
676
+ const proxy = this.networkProxy
677
+ if (proxy === undefined) return undefined
678
+ const blocks = proxy.recentBlocks().filter(record => record.time >= since - LLM_BLOCK_SKEW_MS)
679
+ const newest = blocks[0]
680
+ if (newest === undefined) return undefined
681
+ const target = `${newest.scheme ?? 'https'}://${newest.domain}${newest.port !== undefined ? `:${newest.port}` : ''}`
682
+ const marker = newest.action === 'ask' ? '[network: blocked pending approval]' : '[network: denied]'
683
+ const rule = newest.matched
684
+ ? `, rule ${(newest.ruleIndex ?? 0) + 1}${newest.source !== '' ? ` (${newest.source})` : ''}`
685
+ : ', no matching allow rule'
686
+ const more = blocks.length > 1 ? ` (and ${blocks.length - 1} more block${blocks.length > 2 ? 's' : ''} in the same window)` : ''
687
+ return `${marker} the local policy proxy blocked ${target} during this model call (mode ${newest.mode}${rule})${more} — the host process inherits the policy proxy environment (network.injectEnv), so the model transport is adjudicated like any other connection; add an allow rule for the provider endpoint or set network.injectEnv: false`
688
+ }
689
+
531
690
  /** Mark one delegated shell execution as in-flight (newest attribution wins). */
532
691
  private markShell(exec: ToolExecution): void {
533
692
  this.inFlight.set(String(exec.callId), { tool: exec.name, callId: exec.callId, startedAt: Date.now(), agent: exec.agent })
@@ -1014,6 +1173,20 @@ export class PermissionRulesRuntime {
1014
1173
  }
1015
1174
  }
1016
1175
 
1176
+ /**
1177
+ * Whether one watched path must be polled instead of trusting the platform's
1178
+ * change events. WSL is the case issue #19 item 1 reports: a rule file under
1179
+ * `/mnt/<drive>` (drvfs/9p) or any file on a WSL host can stop delivering
1180
+ * chokidar change events with no error at all, so the watcher edits would
1181
+ * silently stop hot-reloading. Polling is the documented chokidar remedy;
1182
+ * everywhere else the native watcher stays in place.
1183
+ * @param path - the watched rule file or candidate directory.
1184
+ * @returns true when the watch should run in polling mode.
1185
+ */
1186
+ watchPollingFor(path: string): boolean {
1187
+ return isDvrfsPath(path) || isWslKernel(readProcVersion())
1188
+ }
1189
+
1017
1190
  /** Attach (once per file path) the Chokidar watcher feeding {@link reload}. */
1018
1191
  private attachWatch(cwd: string, source: string): void {
1019
1192
  if (!this.config.watch || source === '' || (this.config.builtin.enabled && source === this.config.builtin.path)) return
@@ -1023,7 +1196,8 @@ export class PermissionRulesRuntime {
1023
1196
  return
1024
1197
  }
1025
1198
  const cwds = new Set([cwd])
1026
- const watcher = chokidar.watch(source, { persistent: true, ignoreInitial: true })
1199
+ const polling = this.watchPollingFor(source)
1200
+ const watcher = chokidar.watch(source, { persistent: true, ignoreInitial: true, ...(polling ? POLLING_WATCH_OPTIONS : {}) })
1027
1201
  const onEvent = (): void => this.scheduleReload(source)
1028
1202
  watcher.on('add', onEvent)
1029
1203
  watcher.on('change', onEvent)
@@ -1120,9 +1294,11 @@ export class PermissionRulesRuntime {
1120
1294
  }
1121
1295
  const cwds = new Set([cwd])
1122
1296
  const candidates = new Map([[cwd, candidate]])
1297
+ const polling = this.watchPollingFor(candidate)
1123
1298
  const watchOptions = {
1124
1299
  persistent: true,
1125
1300
  ignoreInitial: true,
1301
+ ...(polling ? POLLING_WATCH_OPTIONS : {}),
1126
1302
  ignored: (path: string): boolean => path.split(sep).includes('node_modules') || path.split(sep).includes('.git'),
1127
1303
  }
1128
1304
  const watcher = dir === parent
@@ -1279,7 +1455,14 @@ export async function apply(ctx: Context, config: Config): Promise<void> {
1279
1455
  return next()
1280
1456
  })
1281
1457
  attachSettingsSection(ctx, runtime, config)
1282
- if (resolved.network.enabled) await runtime.attachNetworkProxy()
1458
+ if (resolved.network.enabled) {
1459
+ await runtime.attachNetworkProxy()
1460
+ // Issue #22: the injected proxy environment is process-wide, so the
1461
+ // harness's own model transport is adjudicated here too. Keep a model-call
1462
+ // failure attributable to the policy instead of leaving it a bare
1463
+ // transport error.
1464
+ ctx.on('llm/stream', (_options: GenerateOptions, next: () => AsyncIterable<StreamChunk>) => runtime.llmStream(next))
1465
+ }
1283
1466
  ctx.inject(['systemPrompt'], (scope) => {
1284
1467
  const systemPrompt = scope.get('systemPrompt') as { context?: (entry: { name: string; order?: number; text: string }) => void } | undefined
1285
1468
  if (systemPrompt?.context === undefined) return