dsh-perm-gate 5.0.2 → 5.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.ja.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # 変更履歴
2
2
 
3
+ 言語: [English](./CHANGELOG.md) · [日本語](./CHANGELOG.ja.md) · [한국어](./CHANGELOG.ko.md) · [Français](./CHANGELOG.fr.md) · [Deutsch](./CHANGELOG.de.md) · [Italiano](./CHANGELOG.it.md) · [Русский](./CHANGELOG.ru.md) · [Español](./CHANGELOG.es.md)
4
+
3
5
  このプロジェクトの重要な変更はすべてこのファイルに記録されます。
4
6
 
5
7
  形式は [Keep a Changelog](https://keepachangelog.com/ja/1.1.0/) に従い、
package/CHANGELOG.ko.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # 변경 로그
2
2
 
3
+ 언어: [English](./CHANGELOG.md) · [日本語](./CHANGELOG.ja.md) · [한국어](./CHANGELOG.ko.md) · [Français](./CHANGELOG.fr.md) · [Deutsch](./CHANGELOG.de.md) · [Italiano](./CHANGELOG.it.md) · [Русский](./CHANGELOG.ru.md) · [Español](./CHANGELOG.es.md)
4
+
3
5
  이 프로젝트의 중요한 변경 사항은 모두 이 파일에 기록됩니다.
4
6
 
5
7
  형식은 [Keep a Changelog](https://keepachangelog.com/ko/1.1.0/)을 따르며,
package/CHANGELOG.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # Changelog
2
2
 
3
+ Languages: [English](./CHANGELOG.md) · [日本語](./CHANGELOG.ja.md) · [한국어](./CHANGELOG.ko.md) · [Français](./CHANGELOG.fr.md) · [Deutsch](./CHANGELOG.de.md) · [Italiano](./CHANGELOG.it.md) · [Русский](./CHANGELOG.ru.md) · [Español](./CHANGELOG.es.md)
4
+
3
5
  All notable changes to this project will be documented in this file.
4
6
 
5
7
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
package/INSTALL.ja.md CHANGED
@@ -4,13 +4,28 @@
4
4
  - [中文 README](./README.zh.md)
5
5
  - [日本語 README](./README.ja.md)
6
6
  - [한국어 README](./README.ko.md)
7
+ - [Français README](./README.fr.md)
8
+ - [Deutsch README](./README.de.md)
9
+ - [Italiano README](./README.it.md)
10
+ - [Русский README](./README.ru.md)
11
+ - [Español README](./README.es.md)
7
12
  - [Installation guide](./INSTALL.md)
8
13
  - [中文安装指南](./INSTALL.zh.md)
9
14
  - [日本語インストールガイド](./INSTALL.ja.md)
10
15
  - [한국어 설치 안내](./INSTALL.ko.md)
16
+ - [Guide d'installation](./INSTALL.fr.md)
17
+ - [Installationsanleitung](./INSTALL.de.md)
18
+ - [Guida all'installazione](./INSTALL.it.md)
19
+ - [Руководство по установке](./INSTALL.ru.md)
20
+ - [Guía de instalación](./INSTALL.es.md)
11
21
  - [Changelog](./CHANGELOG.md)
12
22
  - [日本語 changelog](./CHANGELOG.ja.md)
13
23
  - [한국어 changelog](./CHANGELOG.ko.md)
24
+ - [Français changelog](./CHANGELOG.fr.md)
25
+ - [Deutsch changelog](./CHANGELOG.de.md)
26
+ - [Italiano changelog](./CHANGELOG.it.md)
27
+ - [Русский changelog](./CHANGELOG.ru.md)
28
+ - [Español changelog](./CHANGELOG.es.md)
14
29
 
15
30
  `dsh-perm-gate` バージョン **2.4.1**。判定チェーン、ルールファイル形式、自动审查
16
31
  ティアについては [日本語 README](./README.ja.md) を参照してください。
package/INSTALL.ko.md CHANGED
@@ -4,13 +4,28 @@
4
4
  - [中文 README](./README.zh.md)
5
5
  - [日本語 README](./README.ja.md)
6
6
  - [한국어 README](./README.ko.md)
7
+ - [Français README](./README.fr.md)
8
+ - [Deutsch README](./README.de.md)
9
+ - [Italiano README](./README.it.md)
10
+ - [Русский README](./README.ru.md)
11
+ - [Español README](./README.es.md)
7
12
  - [Installation guide](./INSTALL.md)
8
13
  - [中文安装指南](./INSTALL.zh.md)
9
14
  - [日本語インストールガイド](./INSTALL.ja.md)
10
15
  - [한국어 설치 안내](./INSTALL.ko.md)
16
+ - [Guide d'installation](./INSTALL.fr.md)
17
+ - [Installationsanleitung](./INSTALL.de.md)
18
+ - [Guida all'installazione](./INSTALL.it.md)
19
+ - [Руководство по установке](./INSTALL.ru.md)
20
+ - [Guía de instalación](./INSTALL.es.md)
11
21
  - [Changelog](./CHANGELOG.md)
12
22
  - [日本語 changelog](./CHANGELOG.ja.md)
13
23
  - [한국어 changelog](./CHANGELOG.ko.md)
24
+ - [Français changelog](./CHANGELOG.fr.md)
25
+ - [Deutsch changelog](./CHANGELOG.de.md)
26
+ - [Italiano changelog](./CHANGELOG.it.md)
27
+ - [Русский changelog](./CHANGELOG.ru.md)
28
+ - [Español changelog](./CHANGELOG.es.md)
14
29
 
15
30
  `dsh-perm-gate` 버전 **2.4.1**. 결정 체인, 규칙 파일 형식, 自动审查 티어는
16
31
  [한국어 README](./README.ko.md)를 참고하세요.
package/INSTALL.md CHANGED
@@ -4,13 +4,28 @@
4
4
  - [中文 README](./README.zh.md)
5
5
  - [日本語 README](./README.ja.md)
6
6
  - [한국어 README](./README.ko.md)
7
+ - [Français README](./README.fr.md)
8
+ - [Deutsch README](./README.de.md)
9
+ - [Italiano README](./README.it.md)
10
+ - [Русский README](./README.ru.md)
11
+ - [Español README](./README.es.md)
7
12
  - [Installation guide](./INSTALL.md)
8
13
  - [中文安装指南](./INSTALL.zh.md)
9
14
  - [日本語インストールガイド](./INSTALL.ja.md)
10
15
  - [한국어 설치 안내](./INSTALL.ko.md)
16
+ - [Guide d'installation](./INSTALL.fr.md)
17
+ - [Installationsanleitung](./INSTALL.de.md)
18
+ - [Guida all'installazione](./INSTALL.it.md)
19
+ - [Руководство по установке](./INSTALL.ru.md)
20
+ - [Guía de instalación](./INSTALL.es.md)
11
21
  - [Changelog](./CHANGELOG.md)
12
22
  - [日本語 changelog](./CHANGELOG.ja.md)
13
23
  - [한국어 changelog](./CHANGELOG.ko.md)
24
+ - [Français changelog](./CHANGELOG.fr.md)
25
+ - [Deutsch changelog](./CHANGELOG.de.md)
26
+ - [Italiano changelog](./CHANGELOG.it.md)
27
+ - [Русский changelog](./CHANGELOG.ru.md)
28
+ - [Español changelog](./CHANGELOG.es.md)
14
29
 
15
30
  `dsh-perm-gate` version **2.4.1**. Continue with the [README](./README.md) for the
16
31
  decision chain, the rules file format and the 自动审查 tier.
package/INSTALL.zh.md CHANGED
@@ -4,13 +4,28 @@
4
4
  - [中文 README](./README.zh.md)
5
5
  - [日本語 README](./README.ja.md)
6
6
  - [한국어 README](./README.ko.md)
7
+ - [Français README](./README.fr.md)
8
+ - [Deutsch README](./README.de.md)
9
+ - [Italiano README](./README.it.md)
10
+ - [Русский README](./README.ru.md)
11
+ - [Español README](./README.es.md)
7
12
  - [Installation guide](./INSTALL.md)
8
13
  - [中文安装指南](./INSTALL.zh.md)
9
14
  - [日本語インストールガイド](./INSTALL.ja.md)
10
15
  - [한국어 설치 안내](./INSTALL.ko.md)
16
+ - [Guide d'installation](./INSTALL.fr.md)
17
+ - [Installationsanleitung](./INSTALL.de.md)
18
+ - [Guida all'installazione](./INSTALL.it.md)
19
+ - [Руководство по установке](./INSTALL.ru.md)
20
+ - [Guía de instalación](./INSTALL.es.md)
11
21
  - [Changelog](./CHANGELOG.md)
12
22
  - [日本語 changelog](./CHANGELOG.ja.md)
13
23
  - [한국어 changelog](./CHANGELOG.ko.md)
24
+ - [Français changelog](./CHANGELOG.fr.md)
25
+ - [Deutsch changelog](./CHANGELOG.de.md)
26
+ - [Italiano changelog](./CHANGELOG.it.md)
27
+ - [Русский changelog](./CHANGELOG.ru.md)
28
+ - [Español changelog](./CHANGELOG.es.md)
14
29
 
15
30
  `dsh-perm-gate` 版本 **2.4.1**。裁决链、规则文件格式与自动审查档位请见
16
31
  [中文 README](./README.zh.md)。
package/README.de.md ADDED
@@ -0,0 +1,487 @@
1
+ # dsh-perm-gate
2
+
3
+ - [English README](./README.md)
4
+ - [中文 README](./README.zh.md)
5
+ - [日本語 README](./README.ja.md)
6
+ - [한국어 README](./README.ko.md)
7
+ - [Français README](./README.fr.md)
8
+ - [Deutsch README](./README.de.md)
9
+ - [Italiano README](./README.it.md)
10
+ - [Русский README](./README.ru.md)
11
+ - [Español README](./README.es.md)
12
+ - [Installation guide](./INSTALL.md)
13
+ - [中文安装指南](./INSTALL.zh.md)
14
+ - [日本語インストールガイド](./INSTALL.ja.md)
15
+ - [한국어 설치 안내](./INSTALL.ko.md)
16
+ - [Guide d'installation](./INSTALL.fr.md)
17
+ - [Installationsanleitung](./INSTALL.de.md)
18
+ - [Guida all'installazione](./INSTALL.it.md)
19
+ - [Руководство по установке](./INSTALL.ru.md)
20
+ - [Guía de instalación](./INSTALL.es.md)
21
+ - [Changelog](./CHANGELOG.md)
22
+ - [日本語 changelog](./CHANGELOG.ja.md)
23
+ - [한국어 changelog](./CHANGELOG.ko.md)
24
+ - [Français changelog](./CHANGELOG.fr.md)
25
+ - [Deutsch changelog](./CHANGELOG.de.md)
26
+ - [Italiano changelog](./CHANGELOG.it.md)
27
+ - [Русский changelog](./CHANGELOG.ru.md)
28
+ - [Español changelog](./CHANGELOG.es.md)
29
+
30
+ > **Kompatibilitätshinweis:** v2.0.0 liefert `ja`-/`ko`-Wörterbücher mit, aber das offizielle DSH
31
+ > stellt über `LocaleRuntime` nur `zh` / `en` bereit (`LOCALE_IDS = ["zh", "en"]`). Auf einem
32
+ > unveränderten DSH schlägt die Auswahl von `ja` / `ko` mit `locale "<id>" is not registered` fehl.
33
+ > Verwenden Sie einen DSH-Fork, der `LOCALE_IDS` (locale-settings.ts) und die `LOCALES`-Beschriftungen
34
+ > (client/index.ts) aktualisiert, und bauen Sie ihn neu.
35
+
36
+ > **▼ DSH-Versionskompatibilität**
37
+ >
38
+ > Zwei DSH-Linien werden von zwei langlebigen Branches bedient, jeder mit seiner eigenen
39
+ > Versionsserie, seinem `engines.dsh` und seinem npm-Dist-Tag
40
+ > ([Release-Aufteilung](./RELEASING.md)):
41
+ >
42
+ > | DSH-Version | Branch | Version | npm-Tag |
43
+ > | --- | --- | --- | --- |
44
+ > | 0.1.0-rc.7 ~ 0.1.1-rc.x | `legacy` | `1.x` | `@legacy` |
45
+ > | 0.1.2-alpha.1+ (inkl. 0.1.5-rc.2) | `main` | `2.x` | `@latest` / `@dsh-0.1.2` (`@2.x` ist eine Range) |
46
+ > | 0.2.0-rc.1 (0.2.0-Linie) | `compat/0.2.0` | `5.x` | `@dsh-0.2.0` |
47
+ >
48
+ > Die Seriennummer folgt der **DSH-Linie** (`1.x` = DSH ≤ 0.1.1, `2.x` = DSH 0.1.2+), und die
49
+ > Majors zäunen einander ein: Eine `^1.x`-Installation löst nie eine `2.x`-Version auf und
50
+ > umgekehrt. `engines.dsh` sagt dieselbe Trennung aus, aber DSH liest es nie — die Ranges und
51
+ > Dist-Tags sind es, die ein altes DSH auf `1.x` halten.
52
+ >
53
+ > `@deepseek-ai/dsh-client-runtime` wurde mit `0.1.2-alpha.1` **entfernt** — es ist nicht bloß
54
+ > umgezogen. Die `legacy`-Linie erreicht `ctx.slots` weiterhin über es; `main` bekommt dieselbe
55
+ > Deklaration aus `@deepseek-ai/dsh-client-ui-renderer/client`. Zwei versionsempfindliche Nähte
56
+ > werden über Capability-Probes statt Versionsprüfungen gehandhabt: (1) Die
57
+ > Einstellungsregistrierung verwendet `register`, das es auf beiden Linien gibt (`installSection`
58
+ > ist eine Ergänzung, kein Ersatz); (2) `effectivePolicy` ist auf beiden eine **private** Methode
59
+ > des Nutzerfreigabe-Dienstes, daher wird sie hinter einer `typeof`-Probe gelesen und degradiert
60
+ > zu „Policy unbekannt“, wenn sie fehlt oder wirft.
61
+
62
+ Version **2.6.0** — siehe das [Changelog](./CHANGELOG.de.md).
63
+
64
+ Ein einzelnes, in sich geschlossenes, determinismus-zuerst- und fail-closed-Permission-Gate für
65
+ DeepSeek Harness.
66
+
67
+ `dsh-perm-gate` entscheidet jeden Toolaufruf über eine feste Prioritätskette:
68
+
69
+ | Stufe | Entscheidung | Was es ist |
70
+ | ---- | ---- | ---- |
71
+ | **P0** | `deny` | deterministisches Hard-Deny: Zugangsdaten-Material, Mutation geschützter Pfade, gefährliche Shell |
72
+ | **P1** | `allow` | ein präzises, begrenztes **Session-Grant** |
73
+ | **P2** | `deny/allow/ask` | statische Regelkette: zuerst Blacklist, dann Allow, dann Ask |
74
+ | **P3** | `allow/deny/ask` | optionaler LLM-Semantikklassifikator (Standardmäßig **aus**) |
75
+ | **P4** | `ask` | offizieller Approval-Seam |
76
+
77
+ Streng fail-closed: Eine P0-Entscheidung wird nie von einem Grant, einer Regel, dem Klassifikator
78
+ oder einem Menschen überstimmt.
79
+
80
+ ## Warum
81
+
82
+ Das DSH-Sicherheitsökosystem verteilt dies auf mehrere Plugins (`dsh-permission-rules`,
83
+ `dsh-auto-mode`, `dsh-auto-review`, `dsh-movein-permissions`). `dsh-perm-gate` führt Gate +
84
+ Freigabe + (optionalen) Klassifikator in einem Paket zusammen — mit einem einzigen Audit-Trail und
85
+ ohne versionsübergreifende Plugin-Kopplung.
86
+
87
+ ## Funktionen
88
+
89
+ - **Befehls-Whitelist / Blacklist** — Abgleich über eine **argv-Zerlegung** (kein Rohstring), mit
90
+ rekursivem Abstieg in `sh -c`/`bash -c`, Pipeline-Erkennung, Prüfung von
91
+ Redirect-Zielen und Erkennung von recursive/force (`rm -rf`).
92
+ - **Deny-Priorität** — eine passende Deny-Regel schlägt jede Allow-Regel.
93
+ - **Session-Grants** — präzise `(Tool, kanonischer Fingerabdruck)`-Grants mit `TTL` + `maxUses`;
94
+ ein erneutes Ausführen mit anderem Ziel nutzt die Autorität nie wieder. Sub-Agents erben, können
95
+ aber nicht selbst münzen.
96
+ - **Pure-Function-Regel-Engine** — glob/regex-Kompilierung mit ReDoS-Schranke, lautes Versagen bei
97
+ fehlerhaften Regeln und Compile-Cache über Source-Hash.
98
+ - **Audit** — jede Entscheidung wird als `{ignorable:true}`-Ereignis mit ihrer `callId`
99
+ protokolliert; der modellsichtbare Grund entspricht dem aufgezeichneten Ausgang.
100
+ - **自动审查-Stufe** (`permissive`, dazu `permissive-full`) — ein **unabhängiger
101
+ Freigabemodus** (getrennt von den Stufen Read-only, Full-access und Whitelist), der weder
102
+ „Auto-Genehmigung“ noch pauschales Vertrauen ist. Das Frontend stellt einen **einzigen
103
+ Schalter** bereit (`permissive`); die vier Backend-Strategien sind **kombinierbar** und werden
104
+ über die Plugin-Einstellungen gesteuert — weiterhin fail-closed gegenüber P0. Der
105
+ Berechtigungs-Picker und die Einstellungszeile zeigen sie beide unter dem Produktlabel
106
+ 自动审查, ohne Icon. Die Variante `permissive-full` behält dasselbe Freigabeverhalten, fällt
107
+ aber die eingebaute Datei-Sandbox weg, die sonst die benannten Pipes verweigert, die
108
+ `git clone` / Cygwin / ConPTY brauchen.
109
+ - **Auto-Antwort auf Sandbox-Eskalation** (`trustEscalation`) — eine Sandbox-Eskalation wird aus
110
+ *dem Inneren* des shell-/pwsh-/edit-Toolkörpers heraus gestellt, nach `tools/pre-execute`; das
111
+ Gate sah sie nie, und ein automatisch erlaubter Aufruf forderte Sie trotzdem auf, die
112
+ Ausweitung zu genehmigen. Ist diese Strategie an, wird genau der vom Gate freigegebene Aufruf
113
+ (an `callId` erkannt) hier beantwortet.
114
+ - **Risikogestufter `llmAssist`** — ein eigenes OpenAI-kompatibles LLM stuft jedes `ask` als
115
+ `safe` / `risky:<Kategorie>` ein; harte Kategorien (Löschen, Zugangsdaten, Remote, System,
116
+ Massen) **fragen immer**, Neutrales fließt in das Verdict-Lernen ein, und jeder Fehler bleibt
117
+ fail-closed.
118
+ - **Verdict-Lernen** — neutrale `ask`s, die ein Mensch freigibt und die tatsächlich ausgeführt
119
+ werden, zählen hoch; nach dem Schwellenwert wird *exakt derselbe Vorgang* (fingerabdruckgleich)
120
+ automatisch erlaubt.
121
+ - **Entscheidungs-Ereignis-Feed** — jede Entscheidung wird an einen JSONL-Feed angehängt und von
122
+ der Browser-Hälfte als Hinweisband über der Konversationseingabe plus ein
123
+ Freigabe-Protokoll-Tab (neueste zuerst) in der Konversationsansicht angezeigt.
124
+
125
+ Eine vordefinierte **Deny-Keyword-Blacklist** (geerbt von dsh-approval-gates
126
+ `DEFAULT_DENY_KEYWORDS`: `rm -rf`, `push --force`, `drop table`, `mkfs`, `git reset --hard`,
127
+ `docker system prune`, …) setzt ein Veto gegen jeden Aufruf, dessen Text ein Keyword enthält —
128
+ case-insensitiver Teilstring, angewandt vor Whitelist, Grants und LLM. Sie ist in der
129
+ Einstellungskarte als Liste editierbar (Vorlageneinträge sind markiert, ein Klick stellt die
130
+ Vorlage wieder her); nicht gesetzt oder leer bedeutet die Vorlage — die Blacklist schaltet sich
131
+ nie still ab.
132
+
133
+ ## Installation
134
+
135
+ Benötigt eine bestehende
136
+ [DeepSeek-Harness](https://github.com/deepseek-ai/deepseek-harness)-Installation.
137
+
138
+ ```sh
139
+ dsh plugin --profile web add dsh-perm-gate
140
+ ```
141
+
142
+ Vollständige Schritte zu Installation, Upgrade, Migration und Fehlerbehebung stehen in der
143
+ [Installationsanleitung](./INSTALL.de.md) — auch verfügbar auf
144
+ [English](./INSTALL.md) / [中文](./INSTALL.zh.md) / [日本語](./INSTALL.ja.md) /
145
+ [한국어](./INSTALL.ko.md) / [Français](./INSTALL.fr.md) / [Italiano](./INSTALL.it.md) /
146
+ [Русский](./INSTALL.ru.md) / [Español](./INSTALL.es.md).
147
+
148
+ ## Konfiguration
149
+
150
+ Fügen Sie das Plugin in die `cordis.yml` ein:
151
+
152
+ ```yaml
153
+ - id: dsh-perm-gate
154
+ name: dsh-perm-gate
155
+ config:
156
+ rulesFile: ./permissions.yaml # optional; defaults to $DSH_HOME/perm-gate/rules.yml
157
+ dshHome: $DSH_HOME # root pinned for protected-target checks
158
+ defaultAction: ask # allow | ask | deny
159
+ gatePresets: [permissive, permissive-full] # tiers where the gate is active (default)
160
+ sessionSweep: true # hourly cleanup of archived/dead sessions' gate data
161
+ ```
162
+
163
+ ### Session-Sweep
164
+
165
+ Beim Start und stündlich liest das Gate den Workspace-Speicher von DSH
166
+ (`$DSH_HOME/storages/workspace.json`, nur lesend) und klassifiziert jede Sitzung, für die es
167
+ Autorisationskettendaten hält. Sitzungen, die DSH archiviert hat (`global.archivedSessionIds`)
168
+ oder gar nicht mehr führt, verlieren ihre Entscheidungsereignisse aus
169
+ `$DSH_HOME/perm-gate/events.jsonl`, und ihre Snapshot-Dateien vor der Änderung werden aus
170
+ `$DSH_HOME/perm-gate/snapshots/` gelöscht — Historie, die die Review-Seite nicht mehr erreichen
171
+ kann, für Daten, die der Harness selbst als weg betrachtet. Lebende Sitzungen bleiben unberührt,
172
+ nicht zuordenbare Zeilen (leere Sitzungs-ID) werden nie gelöscht, und jeder Fehler ist fail-open:
173
+ Der Durchlauf wird übersprungen und eine Stunde später wiederholt. Setzen Sie
174
+ `sessionSweep: false` zum Deaktivieren; `workspaceStoreFile` überschreibt den Speicherpfad. Die
175
+ Wiederherstellung einer archivierten Sitzung stellt deren weggesäuberte Historie nicht wieder her.
176
+
177
+ ### Regeldatei
178
+
179
+ ```yaml
180
+ permissions:
181
+ defaultAction: ask
182
+ deny:
183
+ - command: [rm#recursive]
184
+ reason: no recursive rm
185
+ - command: [ssh]
186
+ reason: no direct ssh
187
+ - paths: [.dsh/**]
188
+ reason: protect harness metadata
189
+ allow:
190
+ - command: [pnpm, node]
191
+ reason: dev tools
192
+ - command: [curl, wget]
193
+ args: ["https://*.example.com/*"]
194
+ reason: allowed endpoint
195
+ ask:
196
+ - command: [bash, sh]
197
+ reason: ask shells
198
+ ```
199
+
200
+ Ein Befehlseintrag `word#flag` matcht das Befehlswort (`word`) mit dem Modifikator `recursive`
201
+ oder `force` — `rm#recursive` matcht also `rm -rf`, `env rm -rf` und `sh -c "rm -rf /"`.
202
+
203
+ ### Netzwerk-Policy (Opt-in)
204
+
205
+ Ein lokaler HTTP/CONNECT-Proxy, der den ausgehenden Verkehr von **Shell-Subprozessen** anhand
206
+ derselben Regeldatei entscheidet, plus ein Freigabepfad für Ziele, die keine Regel abdeckt.
207
+ **Standardmäßig aus** — die Aktivierung bindet einen Loopback-Port und schreibt die
208
+ Proxy-Umgebung der Kindprozesse um; sie wird daher nie implizit eingeschaltet.
209
+
210
+ ```yaml
211
+ - id: dsh-perm-gate
212
+ config:
213
+ networkEnabled: false # master switch (default false)
214
+ networkMode: whitelist # deny-all | whitelist | allow-all
215
+ networkUnlisted: ask # ask | deny — unlisted target handling
216
+ networkUnattributed: allow # allow | deny — traffic with no shell attribution
217
+ networkInjectEnv: true # rewrite HTTP(S)_PROXY/ALL_PROXY for children
218
+ networkAskTimeoutMs: 120000 # approval wait before failing closed
219
+ networkGrantTtlMs: 1800000 # how long one approval covers its target
220
+ ```
221
+
222
+ **Gestuftes Verhalten.** Nichts erreicht das Netzwerk ohne Allow-Regel. Ein nicht gelistetes
223
+ Ziel wird an die interaktive Freigabenaht eskaliert, im Namen des Shell-Befehls erhoben, der die
224
+ Verbindung öffnete; eine Freigabe weitet die Reichweite für dieses Ziel für die Sitzung. Eine
225
+ `deny`-Regel wird **nie** eskaliert — eine Freigabe kann erweitern, was ein nicht gelistetes Ziel
226
+ erreichen darf, aber sie kann nie eine Regel überstimmen, die Nein sagt.
227
+
228
+ **Die Grenze — lesen Sie das, bevor Sie sich darauf verlassen.** Der Proxy ist eine
229
+ *kooperative* Policy-Schicht, keine Durchsetzungsgrenze. Er sieht nur Verkehr von Clients, die die
230
+ Proxy-Umgebung lesen:
231
+
232
+ | Client | Abgedeckt? |
233
+ |--------|----------|
234
+ | `curl`, `wget`, `git`, Go `net/http`, Python `requests` | ja |
235
+ | **Node.js `http`/`https`/`fetch`** | **nein — verbindet direkt** |
236
+ | Java (ohne `-D`-Proxy-Flags), .NET `HttpClient` | nein |
237
+ | Raw Sockets, eigenes TCP | nein |
238
+ | DNS, QUIC/HTTP3, Nicht-HTTP-Protokolle | nein |
239
+ | Verbindungen zu einer literalen IP | nein |
240
+
241
+ Ein Shell-Befehl wie `node -e "require('http').get('http://host/')"` wird also nicht
242
+ abgefangen. Betrachten Sie dies als Leitplanke gegen Unfälle und als Ort, um Absicht zu
243
+ dokumentieren — nicht als hermetische Sandbox.
244
+
245
+ DSHs **eigener** Netzwerkverkehr — die eingebauten Netzwerk-Tools und der LLM-Transport — wird
246
+ bewusst in Ruhe gelassen. Diese Verbindungen tragen keine Shell-Zuordnung, und
247
+ `networkUnattributed: allow` (der Default) lässt sie ungeprüft durch: Sie zu prüfen würde dem
248
+ Host erlauben, *sich selbst* zu blockieren — ein schlimmerer Fehler als ein verpasster Block.
249
+ Setzen Sie `networkUnattributed: deny` nur, wenn Sie wissen, dass die Clients Ihres Hosts die
250
+ Proxy-Umgebung ignorieren.
251
+
252
+ Den Live-Zustand abfragen über `GET /api/dsh-perm-gate/network` (Modus, Bind, Port,
253
+ Proxy-Lebendigkeit, Env-Injection-Status, Block-Zähler, letzte Blocks).
254
+
255
+ ## Die 自动审查-Stufe (Maschinenwert `permissive`)
256
+
257
+ 自动审查 ist eine **unabhängige Freigabestufe** im DSH-Berechtigungs-Picker, parallel zu
258
+ Read Only / Workspace Write / Full Access / Whitelist. Sie ist **keine** generische
259
+ „Auto-Genehmigung“ und prägt nie pauschale Autorität: Sie verengt oder weitet nur die Naht *vor*
260
+ dem Mensch/LLM-Schritt, während das P0-Hard-Deny **innerhalb des eigenen Geltungsbereichs des
261
+ Gates** monoton und nicht verhandelbar bleibt.
262
+
263
+ > **P0 ist begrenzt, nicht global.** Das Gate wirkt nur, solange der Berechtigungs-Preset der
264
+ > Sitzung einer der `gatePresets` ist (Default `permissive` / `permissive-full`). Unter jedem
265
+ > anderen Preset — Read Only, Workspace Write oder Full Access — tritt das **gesamte** Gate
266
+ > zurück, das P0-Hard-Deny eingeschlossen, weil die eigene Policy der gewählten Stufe diese
267
+ > Sitzung regiert. Das ist beabsichtigt (siehe `gatePresets` in der Konfigurationstabelle), heißt
268
+ > aber: „P0 ist nicht verhandelbar“ gilt *innerhalb* der Gate-Stufen, nicht über jede Stufe
269
+ > hinweg. Ein Rückzug ist nicht lautlos: Das Gate zeichnet ein `stand-down`-Ereignis pro
270
+ > Sitzungs-/Preset-Wechsel auf, und der Browser zeigt ein klebendes **GATE OFF**-Band über der
271
+ > Eingabe. Setzen Sie `gatePresets: ['*']`, um P0 wieder global zu machen.
272
+
273
+ **Es werden zwei Varianten ausgeliefert**, denn `sandbox` und `approval` eines Presets sind
274
+ unabhängige Stellschrauben, und ihre Kopplung erzwang einen schlechten Kompromiss:
275
+
276
+ | Picker-Label | Maschinenwert | sandbox | approval |
277
+ |--------------|---------------|---------|----------|
278
+ | 自动审查 | `permissive` | `workspace-write` | `ask` |
279
+ | 自动审查(高权限) | `permissive-full` | `danger-full-access` | `ask` |
280
+
281
+ Die schlichte Stufe behält die eingebaute Datei-Sandbox. Diese Sandbox verweigert auch die
282
+ benannten Pipes, die ein Kindprozess zum Starten braucht; deshalb scheitern daran `git clone`,
283
+ MSYS2/Cygwin-`sh.exe` und ConPTY mit `Win32 error 5` / `couldn't create signal pipe`. Da das Gate
284
+ **nur** in den in `gatePresets` gelisteten Stufen aktiv ist, hieß Gate wollen, diese Einschränkung
285
+ akzeptieren. 自动审查(高权限) löst die Kopplung: identisches Freigabeverhalten, keine
286
+ Datei-Sandbox-Einschränkung. Die Beschreibung der Stufe sagt den Kompromiss offen — der
287
+ Workflow ist reibungsloser, Freigaben gelten weiterhin pro Aufruf, aber es bleibt **keine
288
+ System-Sandbox als Rückfallebene**. Beide stehen im Default der `gatePresets`, also gibt Ihnen
289
+ jede die volle P0–P4-Kette — das Gate liest nur den **Namen** des Presets, nie den Sandbox-Modus.
290
+
291
+ Das Picker-Label ist ein **vom Host gelieferter Produkt-String**, kein pro-Locale-Wörterbucheintrag:
292
+ DSH rendert den `name:` einer Plugin-Stufe wortwörtlich auf beiden Berechtigungsflächen (die
293
+ Default-Zeile in den allgemeinen Einstellungen und der Composer-Picker) und liefert eigene
294
+ lokalisierte Labels nur für die drei eingebauten Werte; `cordis.patch.yml` liefert daher das
295
+ chinesische Label für jede Sitzung.
296
+
297
+ Beim **Icon** ist es eine andere Geschichte. Die Glyph-Map des Composers ist geschlossen, und ihr
298
+ eigener Kommentar nennt die Regel: *host-configured names outside the design set get none.*
299
+ `permissive` ist ein eingebauter Wert, daher hat 自动审查 bereits ein Schild+Auge-Glyph;
300
+ `permissive-full` bekommt dasselbe Glyph nur, weil `npx dsh-perm-gate-patch-glyph` es in diese Map
301
+ einträgt. Dieser Patch editiert ein **Host**-Paket und geht deshalb bei jedem DSH-Upgrade
302
+ verloren — siehe [Nach einem DSH-Upgrade](./INSTALL.de.md).
303
+
304
+ In der `cordis.yml`:
305
+
306
+ ```yaml
307
+ - id: dsh-perm-gate
308
+ name: dsh-perm-gate
309
+ config:
310
+ rulesFile: ./permissions.yaml
311
+ defaultAction: ask
312
+ permissive: true # single front-facing switch (independent tier on)
313
+ permissiveStrategies: # backend, combinable
314
+ trustAutoAllow: true # in-scope safe ops auto-allow; dangerous/unknown ask
315
+ alwaysConfirm: false # every crossing asks; allow-controls add repeat-allow / wl-migrate buttons
316
+ llmAssist: false # LLM classify first, human fallback on ask/failure
317
+ trustEscalation: true # a cleared call's own sandbox escalation needs no prompt
318
+ ```
319
+
320
+ `trustAutoAllow` ist die Basismittelstufe (eine Regel-Allow läuft automatisch durch).
321
+ `alwaysConfirm` zeigt bei jedem Übertritt das Freigabepanel; seine „Allow-Controls“ fügen zwei
322
+ erweiterte Buttons hinzu — **diesen Typ für diese Sitzung wiederholt erlauben** (ein begrenztes
323
+ Session-Grant) und **jedes Vorkommen erlauben** (das das Befehlswort per `approveAllowEverywhere`
324
+ in die `allow`-Whitelist der `permissions.yaml` überträgt). `llmAssist` zieht ein echtes,
325
+ konfigurierbares LLM hinzu (`classifierEndpoint` / `classifierModel`, jede OpenAI-kompatible
326
+ API), um ein `ask` automatisch zu entscheiden, und fällt bei `ask`/Fehler auf die menschliche
327
+ Naht zurück — immer fail-closed. `trustEscalation` (standardmäßig an, solange die Stufe an ist)
328
+ beantwortet eine `sandbox_permissions`-Eskalation, die aus dem Inneren eines bereits vom Gate
329
+ erlaubten Aufrufs gestellt wurde; siehe unten. Bei ausgeschaltetem `permissive` verhält sich das
330
+ Gate genau wie zuvor.
331
+
332
+ ### Sandbox-Eskalation: warum ein `safe`-Verdict trotzdem nachfragte
333
+
334
+ Ein Toolaufruf kann **zwei unabhängige Freigaben** auslösen. Das Gate besitzt die erste — sein
335
+ eigenes `ask` im `tools/pre-execute`-Wasserfall. Die zweite kommt von `approveEscalation`
336
+ **im Toolkörper**, zur Zeit von `tools/execute`, wann immer das Modell `sandbox_permissions` +
337
+ `justification` übergeben hat; bis dahin ist `tools/pre-execute` bereits entschieden, sodass das
338
+ Allow des Gates sie nie erreicht. Ein vom LLM als `safe` eingestufter und vom Gate automatisch
339
+ erlaubter Aufruf zeigte also trotzdem eine Aufforderung, die Sandbox-Ausweitung zu genehmigen.
340
+
341
+ `trustEscalation` schließt diese Lücke. Das Gate merkt sich jeden positiv erlaubten Aufruf
342
+ (schlüsselt auf der `callId` des Hosts, die die Eskalationsanfrage wiederholt), und beantwortet
343
+ die Eskalation selbst mit `allowed-once`. Sie greift nur, wenn **alles** zutrifft:
344
+
345
+ - die 自动审查-Stufe ist an und `trustEscalation` ist an;
346
+ - die Anfrage trägt eine vom Gate freigegebene `callId` mit passendem Toolnamen;
347
+ - der Grund ist eine erkannte Eskalation, die `workspace-write` oder `danger-full-access` nennt.
348
+
349
+ Alles andere — ein unbekannter Grund, ein anderer Aufruf, ein vom Gate nachgefragter oder
350
+ verweigerter Aufruf, der `approval: never`-Durchgriff — delegiert unverändert an den Menschen,
351
+ sodass eine künftige Formulierungsänderung in DSH geschlossen statt offen scheitert. Die
352
+ Auto-Antwort wird im Ereignis-Feed aufgezeichnet (`verdict: "escalation-auto"`, `mode: <Ziel>`).
353
+ Schalten Sie den Schalter aus, um Sandbox-Ausweitungen menschlich gateen zu lassen, während
354
+ andere Allows automatisch bleiben.
355
+
356
+ ### Risikogestufter llmAssist, Verdict-Lernen und der Ereignis-Feed
357
+
358
+ Ist `llmAssist` an, stuft das konfigurierte LLM jeweils ein `ask` nach einem strukturierten
359
+ Protokoll ein. Die Bewertung geschieht **im `tools/pre-execute`-Wasserfall des Gates, bevor die
360
+ Entscheidung an den Host zurückgeht**: ein `safe`-Verdict delegiert den Aufruf direkt durch, das
361
+ Freigabepanel erscheint also nie; nur ein wirklich unsicheres Verdict erreicht Sie. Zwei
362
+ Empfängerquellen sind in der Einstellungskarte wählbar: eine **eigene API** (jeder
363
+ OpenAI-kompatible Endpoint — `classifierEndpoint` / `classifierModel` / `classifierApiKey`, mit
364
+ Presets u. a. Xiaomi MiMo `https://api.xiaomimimo.com/v1`) oder die **DSH-Host-Modellgruppe** (der
365
+ konfigurierte `llm`-Dienst der Sitzung, über `agentDefaultModel.currentSelection`, optional
366
+ überschrieben mit `classifierProvider` / `classifierModel`). Ein **Gesundheitstest**-Button
367
+ (`POST /api/dsh-perm-gate/health`) führt eine minimale Completion aus und meldet die Latenz.
368
+
369
+ - `safe` → der Aufruf wird automatisch erlaubt (auditert mit der Quelle `classifier`); kein Panel.
370
+ - `risky` + eine **harte Kategorie** (`deletion`, `credential`, `remote`, `system`, `bulk`) → der
371
+ Aufruf **behält die menschliche Nachfrage**. Der Klassifikator **verweigert nie**: Der
372
+ Deny-Pfad gehört allein den deterministischen Schichten (P0-Hard-Deny, die
373
+ Deny-Keyword-Blacklist, explizite `deny:`-Regeln), sodass eine falsch eingestufte Kategorie
374
+ immer verhandelbar bleibt statt einer nicht anfechtbaren Blockade. Harte Kategorien bleiben in
375
+ einem wichtigen Punkt von `neutral` verschieden: Sie werden **nie gelernt**, sodass wiederholte
376
+ menschliche Freigaben sie nie zu einem Auto-Allow sedimentieren können.
377
+ (Das Verweigern nach dem Wort des Modells wurde live gemessen: ein harmloses `git commit -F …`,
378
+ eingestuft als `remote`, erzeugte ein Auto-Deny ohne Panel und ohne Grant zum Wiederholen.)
379
+ - `risky:neutral` → mit aktiviertem `riskLearning` (Einstellungskarte, standardmäßig aus) zählt
380
+ jede menschliche Freigabe, die tatsächlich ausgeführt wird (über das `tools/result`-Ereignis des
381
+ Hosts abgerechnet), auf einen `tool|category`-Schlüssel; sobald der Zähler `riskThreshold`
382
+ (Default 3) erreicht **und** der Vorgangs-Fingerabdruck des neuen Aufrufs (Befehlswort +
383
+ Basisname des Ziels) zu einer bestätigten Probe passt, wird exakt derselbe Vorgang automatisch
384
+ erlaubt. Ein anderes Ziel nutzt diese Autorität nie wieder. Mit Sedimentation (`riskSediment`,
385
+ standardmäßig an) werden die bestätigten Proben eines Schwellenwert erreichten Schlüssels zu
386
+ **deterministischen Allow-Regeln**: ein exakter Treffer erlaubt ohne weiteren LLM-Aufruf —
387
+ sogar bei ausgeschaltetem `llmAssist` — und die sedimentierten Regeln sind in der
388
+ Einstellungskarte sichtbar und verwalten (beenden / entfernen).
389
+ - Timeouts (`riskTimeoutMs`, Default 20 s, ein Wiederholungsversuch), Transportfehler und
390
+ protokollfremde Modellausgaben lassen das `ask` unangetastet — das Gate rät nie.
391
+
392
+ Der Lernzustand persistiert in einer plugin-eigenen JSON-Datei
393
+ (`$DSH_HOME/perm-gate/learning.json` oder `learningFile`), nie in Ihre YAML-Regeldatei. Jede
394
+ Entscheidung wird an `$DSH_HOME/perm-gate/events.jsonl` (oder `eventsFile`) angehängt und unter
395
+ `GET /api/dsh-perm-gate/events?sessionId=&since=` ausgeliefert; die Browser-Hälfte fragt sie ab
396
+ und zeigt die letzte Entscheidung als Hinweisband über der Konversationseingabe (Asks bleiben
397
+ sichtbar, bis das nächste Ereignis kommt) und listet alle Entscheidungen der Sitzung, neueste
398
+ zuerst, im **Approvals**-Tab der Konversationsansicht.
399
+
400
+ Die betroffenen Dateien jeder Entscheidung werden gesnapshottet, bevor die Änderung landet
401
+ (≤ 5 Dateien, je ≤ 256 KB) unter `$DSH_HOME/perm-gate/snapshots/`; im **Approvals**-Tab öffnet
402
+ jeder Datei-Chip ein Zeilen-Diff (`GET /api/dsh-perm-gate/diff`) mit einer **Revert**-Aktion, die
403
+ eine Wiederherstellungsanweisung in die Konversation liefert (`POST /api/dsh-perm-gate/revert`).
404
+ Eine Snapshot-Bestandsleiste löscht sie pro Sitzung oder insgesamt
405
+ (`GET /api/dsh-perm-gate/snapshots-stats` / `POST /api/dsh-perm-gate/snapshots-clear`).
406
+
407
+ Ein an einen Menschen gerichtetes `ask` wird verfolgt, bis der Mensch antwortet: ein passiver
408
+ `approval/request`-Beobachter zeichnet den geschlossenen Ausgang auf (`allowed-once` →
409
+ **genehmigt**, `rejected` → **abgelehnt**, `cancelled` → **abgebrochen**, `unavailable` → eine
410
+ Ablehnung, da kein Freigabekanal existierte), mit `tools/result`, dasselbe Ask als Fallback
411
+ abzuschließen, wenn der Beobachter es nicht zuordnen kann. Freigaben berichten den
412
+ Lernfortschritt nach der Freigabe (`n`/Schwellenwert), und das Hinweisband beschriftet alle drei
413
+ Endzustände.
414
+
415
+ 自动审查 und 自动审查(高权限) zeichnen beide das Schild+Auge-Glyph im Picker — die erste aus
416
+ DSHs eingebauter Map, die zweite aus dem Host-Patch, das die Installationsanleitung beschreibt.
417
+ Ohne diesen Patch ist die zweite Stufe überall nur Text; Label und Gate-Verhalten bleiben
418
+ unberührt.
419
+
420
+ ### Eine wählbare Sitzungsstufe
421
+
422
+ `cordis.patch.yml` fügt ein `permissive`-Preset hinzu (`sandbox: workspace-write`,
423
+ `approval: ask`, Name **自动审查**) zwischen Workspace Write und Full Access. Der
424
+ DSH-Bundle-Patch ersetzt die gesamte `permission.config.presets`-Map, statt per Schlüssel zu
425
+ mergen, daher restatuiert die Datei auch die drei eingebauten (`read-only` / `workspace-write` /
426
+ `danger-full-access`, aus `@deepseek-ai/dsh-base/cordis.patch.yml`); `test/patch-presets.spec.ts`
427
+ pinnt diesen Schlüsselsatz. So bietet der Berechtigungs-Picker der Sitzung 自动审查 als
428
+ unabhängige, wählbare Freigabestufe an — nicht als generischen „Auto-Genehmigungs“-Modus.
429
+
430
+ Das Gate ist **nur in den in `gatePresets` gelisteten Stufen aktiv** (Default
431
+ `['permissive', 'permissive-full']`, die beiden Stufen, die dieses Plugin hinzufügt). In jeder
432
+ anderen Stufe — Read Only, Workspace Write, Full Access, `custom` — läuft der Entscheidungsfluss
433
+ des Gates gar nicht: kein Allow, kein Ask, kein Deny, kein P0-Hard-Deny, kein
434
+ Deny-Keyword-Veto, kein Audit-Ereignis. Die eigene Policy der gewählten Stufe regiert den Aufruf,
435
+ und das ist der Punkt: Das eingebaute `danger-full-access` ist als „Vollzugriff ohne
436
+ Freigabeaufforderungen“ definiert, es also mit einem Ask zu überstimmen (dort unbeantwortbar —
437
+ die Freigabenaht lehnt ab, bevor irgendein Antworter läuft, was
438
+ `the user rejected tool "..."` ohne Panel ergibt) oder mit einem Hard-Deny würde die vom Nutzer
439
+ gewählte Stufe still widerrufen. `gatePresets: ['*']` macht das Gate wieder global (Hard-Deny
440
+ eingeschlossen); innerhalb einer aktiven Stufe wird ein `ask` weiterhin zum Durchgriff degradiert,
441
+ wenn die effektive Freigabe-Policy der Sitzung `never` ist — deshalb deklarieren beide
442
+ 自动审查-Stufen `approval: ask`.
443
+
444
+ ### In der Oberfläche konfigurierbar
445
+
446
+ Die Stufe lässt sich auch zur Laufzeit anpassen unter **Einstellungen → Plugins → 自动审查**
447
+ (eine `settings.plugins.tab`-Seite, gerendert vom Browser-Client des Plugins): Ein Schalter
448
+ legt `permissive` um, vier weitere editieren die Backend-`permissiveStrategies`. Der Host liest
449
+ den Namespace live, eine Änderung greift also beim nächsten Toolaufruf ohne Neustart. Dies ist
450
+ eine unabhängige Freigabeklasse, KEIN generischer „Auto-Genehmigungs“-Modus.
451
+
452
+ ### Regeltest (Dry-Run)
453
+
454
+ Dieselbe Seite trägt ein **Regeltest**-Panel: Toolnamen und Befehl eingeben, Test drücken, und
455
+ das Gate beurteilt diesen Aufruf gegen den aktuell geladenen Regelsatz — ohne etwas auszuführen
456
+ und ohne eine Regel zu schreiben. Sie erhalten das Verdict, die passende Regel (Index und
457
+ Aktion), die Dimensionen, die sie einschränkt, und den Grund.
458
+
459
+ Es berichtet sowohl das effektive Verdict (die ganze Kette P0 → P1 → P2 → P3 → P4) *als auch* die
460
+ eigene Antwort der Regelebene — nicht dasselbe: Ein P0-Hard-Deny oder ein Preset-Deny-Keyword
461
+ zündet vor der Regelkette und hinterlässt keinen Regelindex, daher sagt das Panel „keine Regel
462
+ gematcht“, statt eine unrelated Regel zu benennen. Ein Hinweis „0 Regeln geladen“ bedeutet, dass
463
+ der `rulesFile`-Pfad nichts aufgelöst hat.
464
+
465
+ Das Panel spricht mit `POST /api/dsh-perm-gate/dry-run`, das **konstruktionsbedingt nur lesend**
466
+ ist — es hat überhaupt keine Schreibform, ein Regeltest kann eine Regel also nie ändern.
467
+
468
+ ## CLI
469
+
470
+ Dry-Run eines Aufrufs gegen eine Regeldatei (ohne Harness):
471
+
472
+ ```sh
473
+ dsh-perm-gate --rules permissions.yaml --tool bash --args '{"command":"pnpm install"}'
474
+ dsh-perm-gate --rules permissions.yaml --list
475
+ ```
476
+
477
+ ## Entwicklung
478
+
479
+ ```sh
480
+ npm run typecheck
481
+ npm test
482
+ npm run build
483
+ ```
484
+
485
+ ## Lizenz
486
+
487
+ [MIT](./LICENSE)