dsh-perm-gate 5.0.1 → 5.0.3
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 +2 -0
- package/CHANGELOG.ko.md +2 -0
- package/CHANGELOG.md +2 -0
- package/INSTALL.ja.md +15 -0
- package/INSTALL.ko.md +15 -0
- package/INSTALL.md +15 -0
- package/INSTALL.zh.md +15 -0
- package/README.de.md +487 -0
- package/README.es.md +489 -0
- package/README.fr.md +498 -0
- package/README.it.md +490 -0
- package/README.ja.md +18 -1
- package/README.ko.md +18 -1
- package/README.md +18 -1
- package/README.ru.md +478 -0
- package/README.zh.md +18 -1
- package/dsh.plugin.json +2 -2
- package/lib/client.js +788 -5
- package/lib/client.js.map +1 -1
- package/package.json +2 -2
package/README.ru.md
ADDED
|
@@ -0,0 +1,478 @@
|
|
|
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
|
+
> **Примечание о совместимости:** в v2.0.0 входят словари `ja` / `ko`, однако официальный DSH
|
|
31
|
+
> предоставляет через `LocaleRuntime` только `zh` / `en` (`LOCALE_IDS = ["zh", "en"]`). На
|
|
32
|
+
> стандартном DSH выбор `ja` / `ko` завершается ошибкой `locale "<id>" is not registered`.
|
|
33
|
+
> Используйте форк DSH, который обновляет `LOCALE_IDS` (locale-settings.ts) и подписи `LOCALES`
|
|
34
|
+
> (client/index.ts), и выполните пересборку.
|
|
35
|
+
|
|
36
|
+
> **▼ Совместимость версий DSH**
|
|
37
|
+
>
|
|
38
|
+
> Две линии DSH обслуживаются двумя долгоживущими ветками, каждая со своей серией версий, своим
|
|
39
|
+
> `engines.dsh` и своим npm dist-tag ([схема релизов](./RELEASING.md)):
|
|
40
|
+
>
|
|
41
|
+
> | Версия DSH | Ветка | Версия | npm-тег |
|
|
42
|
+
> | --- | --- | --- | --- |
|
|
43
|
+
> | 0.1.0-rc.7 ~ 0.1.1-rc.x | `legacy` | `1.x` | `@legacy` |
|
|
44
|
+
> | 0.1.2-alpha.1+ (вкл. 0.1.5-rc.2) | `main` | `2.x` | `@latest` / `@dsh-0.1.2` (`@2.x` — это диапазон) |
|
|
45
|
+
> | 0.2.0-rc.1 (линия 0.2.0) | `compat/0.2.0` | `5.x` | `@dsh-0.2.0` |
|
|
46
|
+
>
|
|
47
|
+
> Номер серии следует за **линией DSH** (`1.x` = DSH ≤ 0.1.1, `2.x` = DSH 0.1.2+), а мажорные
|
|
48
|
+
> версии отгорожены друг от друга: установка `^1.x` никогда не разрешится в релиз `2.x`, и
|
|
49
|
+
> наоборот. `engines.dsh` фиксирует то же разделение, но DSH его никогда не читает — старый DSH
|
|
50
|
+
> удерживают на `1.x` именно диапазоны и dist-теги.
|
|
51
|
+
>
|
|
52
|
+
> `@deepseek-ai/dsh-client-runtime` был **удалён** в `0.1.2-alpha.1` — он не просто переехал.
|
|
53
|
+
> Линия `legacy` по-прежнему получает `ctx.slots` через него; `main` берёт то же объявление из
|
|
54
|
+
> `@deepseek-ai/dsh-client-ui-renderer/client`. Два чувствительных к версии стыка обрабатываются
|
|
55
|
+
> пробами возможностей, а не проверками версии: (1) регистрация настроек использует `register`,
|
|
56
|
+
> который есть в обеих линиях (`installSection` — дополнение, а не замена); (2) `effectivePolicy`
|
|
57
|
+
> — **приватный** метод сервиса пользовательского одобрения в обеих линиях, поэтому он читается
|
|
58
|
+
> через пробу `typeof` и деградирует до «политика неизвестна», когда метода нет или он бросает
|
|
59
|
+
> исключение.
|
|
60
|
+
|
|
61
|
+
Версия **2.6.0** — см. [список изменений](./CHANGELOG.ru.md).
|
|
62
|
+
|
|
63
|
+
Единые, самодостаточные, детерминированные в первую очередь и fail-closed ворота разрешений
|
|
64
|
+
(gate) для DeepSeek Harness.
|
|
65
|
+
|
|
66
|
+
`dsh-perm-gate` выносит решение по каждому вызову инструмента по фиксированной цепочке
|
|
67
|
+
приоритетов:
|
|
68
|
+
|
|
69
|
+
| Этап | Решение | Что это |
|
|
70
|
+
| ---- | ---- | ---- |
|
|
71
|
+
| **P0** | `deny` | детерминированный жёсткий запрет: учётные данные, изменение защищённых путей, опасный shell |
|
|
72
|
+
| **P1** | `allow` | точная ограниченная **сессионная выдача** (grant) |
|
|
73
|
+
| **P2** | `deny/allow/ask` | статическая цепочка правил: сначала blacklist, затем allow, затем ask |
|
|
74
|
+
| **P3** | `allow/deny/ask` | опциональный семантический LLM-классификатор (**выключен** по умолчанию) |
|
|
75
|
+
| **P4** | `ask` | официальный шов одобрения (approval seam) |
|
|
76
|
+
|
|
77
|
+
Строго fail-closed: решение P0 никогда не отменяется выдачей, правилом, классификатором или
|
|
78
|
+
человеком.
|
|
79
|
+
|
|
80
|
+
## Зачем
|
|
81
|
+
|
|
82
|
+
Экосистема безопасности DSH размазывает это по нескольким плагинам (`dsh-permission-rules`,
|
|
83
|
+
`dsh-auto-mode`, `dsh-auto-review`, `dsh-movein-permissions`). `dsh-perm-gate` объединяет ворота +
|
|
84
|
+
одобрение + (опциональный) классификатор в одном пакете — с единой аудиторской тропой и без
|
|
85
|
+
перекрёстной связности версий между плагинами.
|
|
86
|
+
|
|
87
|
+
## Возможности
|
|
88
|
+
|
|
89
|
+
- **Белый/чёрный список команд** — сопоставление по **декомпозиции argv** (а не по сырой строке),
|
|
90
|
+
с рекурсивным спуском в `sh -c`/`bash -c`, обнаружением конвейеров, проверкой целей
|
|
91
|
+
перенаправления и распознаванием recursive/force (`rm -rf`).
|
|
92
|
+
- **Приоритет запрета** — совпавшее правило deny побеждает любое правило allow.
|
|
93
|
+
- **Сессионные выдачи** — точные выдачи `(инструмент, канонический отпечаток)` с `TTL` +
|
|
94
|
+
`maxUses`; повторный запуск с другой целью никогда не переиспользует полномочия. Субагенты
|
|
95
|
+
наследуют их, но не могут создавать.
|
|
96
|
+
- **Движок правил на чистых функциях** — компиляция glob/regex с пределом ReDoS, громкий отказ на
|
|
97
|
+
некорректных правилах и кэш компиляции по хэшу исходника.
|
|
98
|
+
- **Аудит** — каждое решение пишется в журнал как событие `{ignorable:true}` со своим `callId`;
|
|
99
|
+
причина, видимая модели, совпадает с записанным исходом.
|
|
100
|
+
- **Уровень 自动审查** (`permissive`, плюс `permissive-full`) — **независимый режим одобрения**
|
|
101
|
+
(отдельный от уровней «только чтение», «полный доступ» и белого списка), не являющийся ни
|
|
102
|
+
«автоодобрением», ни слепым доверием. Фронтенд предоставляет **один переключатель**
|
|
103
|
+
(`permissive`); четыре серверные стратегии **комбинируются** и управляются настройками плагина —
|
|
104
|
+
по-прежнему fail-closed против P0. И селектор разрешений, и строка настроек показывают его под
|
|
105
|
+
продуктовым названием 自动审查, без иконки. Вариант `permissive-full` сохраняет то же поведение
|
|
106
|
+
одобрения, но убирает встроенную файловую «песочницу», которая в остальном запрещает именованные
|
|
107
|
+
каналы, нужные `git clone` / Cygwin / ConPTY.
|
|
108
|
+
- **Автоответ на эскалацию песочницы** (`trustEscalation`) — эскалация песочницы запрашивается
|
|
109
|
+
*изнутри* тела инструмента shell / pwsh / edit, после `tools/pre-execute`, так что ворота её
|
|
110
|
+
никогда не видели, и вызов, который они автоматически разрешили, всё равно предлагал вам
|
|
111
|
+
подтвердить расширение. Когда стратегия включена, именно тот вызов, который ворота пропустили
|
|
112
|
+
(опознанный по `callId`), получает ответ здесь.
|
|
113
|
+
- **`llmAssist` с градацией риска** — собственная LLM, совместимая с OpenAI, оценивает каждый
|
|
114
|
+
`ask` как `safe` / `risky:<категория>`; жёсткие категории (удаление, учётные данные, удалённый
|
|
115
|
+
доступ, система, массовые операции) **всегда спрашивают**, нейтральные питают обучение вердиктов,
|
|
116
|
+
и любой сбой остаётся fail-closed.
|
|
117
|
+
- **Обучение вердиктов** — нейтральные по риску `ask`, одобренные человеком и реально
|
|
118
|
+
исполненные, накапливаются; после порога *та же самая операция* (совпадение по отпечатку)
|
|
119
|
+
разрешается автоматически.
|
|
120
|
+
- **Лента событий решений** — каждое решение дописывается в JSONL-ленту и показывается
|
|
121
|
+
браузерной половиной в виде полосы уведомления над полем ввода беседы, плюс вкладка журнала
|
|
122
|
+
одобрений (новейшие сверху) в окне беседы.
|
|
123
|
+
|
|
124
|
+
Предустановленный **чёрный список deny-ключевых слов** (унаследован от `DEFAULT_DENY_KEYWORDS`
|
|
125
|
+
dsh-approval-gate: `rm -rf`, `push --force`, `drop table`, `mkfs`, `git reset --hard`,
|
|
126
|
+
`docker system prune`, …) накладывает вето на любой вызов, чей текст содержит ключевое слово —
|
|
127
|
+
подстрока без учёта регистра, применяется раньше белого списка, выдач и LLM. Он редактируется как
|
|
128
|
+
список в карточке настроек (предустановленные записи помечены, есть восстановление в один клик);
|
|
129
|
+
не задан или пуст — применяется предустановка — чёрный список никогда не отключается молча.
|
|
130
|
+
|
|
131
|
+
## Установка
|
|
132
|
+
|
|
133
|
+
Требуется существующая установка [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness).
|
|
134
|
+
|
|
135
|
+
```sh
|
|
136
|
+
dsh plugin --profile web add dsh-perm-gate
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Полные шаги установки, обновления, миграции и устранения неполадок живут в
|
|
140
|
+
[Руководстве по установке](./INSTALL.ru.md) — также доступном на
|
|
141
|
+
[English](./INSTALL.md) / [中文](./INSTALL.zh.md) / [日本語](./INSTALL.ja.md) /
|
|
142
|
+
[한국어](./INSTALL.ko.md) / [Français](./INSTALL.fr.md) / [Deutsch](./INSTALL.de.md) /
|
|
143
|
+
[Italiano](./INSTALL.it.md) / [Español](./INSTALL.es.md).
|
|
144
|
+
|
|
145
|
+
## Конфигурация
|
|
146
|
+
|
|
147
|
+
Добавьте плагин в `cordis.yml`:
|
|
148
|
+
|
|
149
|
+
```yaml
|
|
150
|
+
- id: dsh-perm-gate
|
|
151
|
+
name: dsh-perm-gate
|
|
152
|
+
config:
|
|
153
|
+
rulesFile: ./permissions.yaml # optional; defaults to $DSH_HOME/perm-gate/rules.yml
|
|
154
|
+
dshHome: $DSH_HOME # root pinned for protected-target checks
|
|
155
|
+
defaultAction: ask # allow | ask | deny
|
|
156
|
+
gatePresets: [permissive, permissive-full] # tiers where the gate is active (default)
|
|
157
|
+
sessionSweep: true # hourly cleanup of archived/dead sessions' gate data
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Сессионная уборка (session sweep)
|
|
161
|
+
|
|
162
|
+
При запуске и каждый час ворота читают хранилище рабочих пространств DSH
|
|
163
|
+
(`$DSH_HOME/storages/workspace.json`, только чтение) и классифицируют каждую сессию, по которой
|
|
164
|
+
держат данные цепочки авторизации. Сессии, которые DSH архивировал
|
|
165
|
+
(`global.archivedSessionIds`) или вообще больше не отслеживает, теряют свои события решений из
|
|
166
|
+
`$DSH_HOME/perm-gate/events.jsonl`, а их снимки до изменения удаляются из
|
|
167
|
+
`$DSH_HOME/perm-gate/snapshots/` — история, которой страница обзора больше не может достать, для
|
|
168
|
+
данных, которые сам харнесс считает исчезнувшими. Живые сессии не затрагиваются, неатрибутируемые
|
|
169
|
+
строки (пустой идентификатор сессии) никогда не удаляются, а любой сбой — fail-open: проход
|
|
170
|
+
пропускается и повторяется через час. Задайте `sessionSweep: false`, чтобы отключить;
|
|
171
|
+
`workspaceStoreFile` переопределяет путь к хранилищу. Восстановление архивированной сессии не
|
|
172
|
+
возвращает её вычищенную историю.
|
|
173
|
+
|
|
174
|
+
### Файл правил
|
|
175
|
+
|
|
176
|
+
```yaml
|
|
177
|
+
permissions:
|
|
178
|
+
defaultAction: ask
|
|
179
|
+
deny:
|
|
180
|
+
- command: [rm#recursive]
|
|
181
|
+
reason: no recursive rm
|
|
182
|
+
- command: [ssh]
|
|
183
|
+
reason: no direct ssh
|
|
184
|
+
- paths: [.dsh/**]
|
|
185
|
+
reason: protect harness metadata
|
|
186
|
+
allow:
|
|
187
|
+
- command: [pnpm, node]
|
|
188
|
+
reason: dev tools
|
|
189
|
+
- command: [curl, wget]
|
|
190
|
+
args: ["https://*.example.com/*"]
|
|
191
|
+
reason: allowed endpoint
|
|
192
|
+
ask:
|
|
193
|
+
- command: [bash, sh]
|
|
194
|
+
reason: ask shells
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Запись команды `word#flag` сопоставляет слово команды (`word`) с модификатором `recursive` или
|
|
198
|
+
`force` — так `rm#recursive` соответствует `rm -rf`, `env rm -rf` и `sh -c "rm -rf /"`.
|
|
199
|
+
|
|
200
|
+
### Сетевая политика (opt-in)
|
|
201
|
+
|
|
202
|
+
Локальный HTTP/CONNECT-прокси, который разбирает исходящий трафик **подпроцессов shell** по тому
|
|
203
|
+
же файлу правил, плюс путь одобрения для целей, которых не покрывает ни одно правило. **По
|
|
204
|
+
умолчанию выключено** — включение занимает loopback-порт и переписывает прокси-окружение
|
|
205
|
+
дочерних процессов, поэтому никогда не включается неявно.
|
|
206
|
+
|
|
207
|
+
```yaml
|
|
208
|
+
- id: dsh-perm-gate
|
|
209
|
+
config:
|
|
210
|
+
networkEnabled: false # master switch (default false)
|
|
211
|
+
networkMode: whitelist # deny-all | whitelist | allow-all
|
|
212
|
+
networkUnlisted: ask # ask | deny — unlisted target handling
|
|
213
|
+
networkUnattributed: allow # allow | deny — traffic with no shell attribution
|
|
214
|
+
networkInjectEnv: true # rewrite HTTP(S)_PROXY/ALL_PROXY for children
|
|
215
|
+
networkAskTimeoutMs: 120000 # approval wait before failing closed
|
|
216
|
+
networkGrantTtlMs: 1800000 # how long one approval covers its target
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
**Поуровневое поведение.** Ничто не достигает сети без правила allow. Незарегистрированная цель
|
|
220
|
+
эскалируется в интерактивный шов одобрения, поднятый от имени команды shell, открывшей
|
|
221
|
+
соединение; одобрение расширяет охват этой цели на сессию. Правило `deny` **никогда** не
|
|
222
|
+
эскалируется — одобрение может расширить то, чего незарегистрированная цель может достичь, но оно
|
|
223
|
+
никогда не отменит правило, говорящее «нет».
|
|
224
|
+
|
|
225
|
+
**Граница — прочтите это, прежде чем полагаться.** Прокси — *кооперативный* слой политики, а не
|
|
226
|
+
граница принуждения. Он видит только трафик клиентов, читающих прокси-окружение:
|
|
227
|
+
|
|
228
|
+
| Клиент | Покрыт? |
|
|
229
|
+
|--------|----------|
|
|
230
|
+
| `curl`, `wget`, `git`, Go `net/http`, Python `requests` | да |
|
|
231
|
+
| **Node.js `http`/`https`/`fetch`** | **нет — подключается напрямую** |
|
|
232
|
+
| Java (без флагов прокси `-D`), .NET `HttpClient` | нет |
|
|
233
|
+
| Сырые сокеты, собственный TCP | нет |
|
|
234
|
+
| DNS, QUIC/HTTP3, не-HTTP протоколы | нет |
|
|
235
|
+
| Соединения с литеральным IP | нет |
|
|
236
|
+
|
|
237
|
+
Так что команда shell вида `node -e "require('http').get('http://host/')"` не перехватывается.
|
|
238
|
+
Считайте это ограждением от случайностей и местом, где фиксируется намерение, а не герметичной
|
|
239
|
+
песочницей.
|
|
240
|
+
|
|
241
|
+
**Собственный** сетевой трафик DSH — встроенные сетевые инструменты и LLM-транспорт — намеренно
|
|
242
|
+
оставлен в покое. Эти соединения не несут shell-атрибуции, и `networkUnattributed: allow`
|
|
243
|
+
(значение по умолчанию) пропускает их без проверки: проверять их значило бы позволить хосту
|
|
244
|
+
заблокировать *самого себя* — провал хуже пропущенной блокировки. Задавайте
|
|
245
|
+
`networkUnattributed: deny`, только если знаете, что клиенты вашего хоста игнорируют
|
|
246
|
+
прокси-окружение.
|
|
247
|
+
|
|
248
|
+
Живое состояние запрашивается на `GET /api/dsh-perm-gate/network` (режим, привязка, порт,
|
|
249
|
+
живость прокси, состояние инъекции окружения, счётчики блокировок, недавние блокировки).
|
|
250
|
+
|
|
251
|
+
## Уровень 自动审查 (машинное значение `permissive`)
|
|
252
|
+
|
|
253
|
+
自动审查 — **независимый уровень одобрения** в селекторе разрешений DSH, наравне с «Только
|
|
254
|
+
чтение» / «Запись в рабочем пространстве» / «Полный доступ» / «Белый список». Это **не** общее
|
|
255
|
+
«автоодобрение», и он никогда не чеканит слепые полномочия: он лишь сужает или расширяет шов
|
|
256
|
+
*до* шага человек/LLM, пока жёсткий запрет P0 остаётся монотонным и не подлежащим обсуждению
|
|
257
|
+
**внутри собственного охвата ворот**.
|
|
258
|
+
|
|
259
|
+
> **P0 ограничен по охвату, а не глобален.** Ворота действуют, только пока пресет разрешений
|
|
260
|
+
> сессии входит в `gatePresets` (по умолчанию `permissive` / `permissive-full`). Под любым другим
|
|
261
|
+
> пресетом — «Только чтение», «Запись в рабочем пространстве» или «Полный доступ» — **весь**
|
|
262
|
+
> набор ворот отступает, включая жёсткий запрет P0, потому что собственной политикой выбранного
|
|
263
|
+
> уровня управляется эта сессия. Это сделано сознательно (см. `gatePresets` в таблице
|
|
264
|
+
> конфигурации), но означает, что «P0 не подлежит обсуждению» верно *внутри* уровней ворот, а не
|
|
265
|
+
> на каждом уровне. Отступление не беззвучно: ворота записывают одно событие `stand-down` на
|
|
266
|
+
> переход сессия/пресет, а браузер показывает липкую полосу **GATE OFF** над полем ввода. Задайте
|
|
267
|
+
> `gatePresets: ['*']`, чтобы снова сделать P0 глобальным.
|
|
268
|
+
|
|
269
|
+
**Поставляются два варианта**, потому что `sandbox` и `approval` пресета — независимые ручки, и их
|
|
270
|
+
связка вынуждала к плохому компромиссу:
|
|
271
|
+
|
|
272
|
+
| Подпись в селекторе | Машинное значение | sandbox | approval |
|
|
273
|
+
|--------------|---------------|---------|----------|
|
|
274
|
+
| 自动审查 | `permissive` | `workspace-write` | `ask` |
|
|
275
|
+
| 自动审查(高权限) | `permissive-full` | `danger-full-access` | `ask` |
|
|
276
|
+
|
|
277
|
+
Простой уровень сохраняет встроенную файловую песочницу. Та песочница запрещает и именованные
|
|
278
|
+
каналы, которые нужны дочернему процессу для старта, поэтому `git clone`, `sh.exe` MSYS2/Cygwin и
|
|
279
|
+
ConPTY под ней падают с `Win32 error 5` / `couldn't create signal pipe`. Поскольку ворота активны
|
|
280
|
+
**только** на уровнях, перечисленных в `gatePresets`, желать ворота значило принимать это
|
|
281
|
+
ограничение. 自动审查(高权限) убирает связку: то же поведение одобрения, без ограничения
|
|
282
|
+
файловой песочницы. Собственное описание уровня говорит о компромиссе прямо — рабочий процесс
|
|
283
|
+
глаже, одобрения по-прежнему применяются к каждому вызову, но **системной песочницы как страховки
|
|
284
|
+
больше нет**. Оба уровня входят в `gatePresets` по умолчанию, так что любой из них даёт полную
|
|
285
|
+
цепочку P0–P4 — ворота читают только **имя** пресета, но не режим песочницы.
|
|
286
|
+
|
|
287
|
+
Подпись в селекторе — **строка продукта, передаваемая хостом**, а не словарная запись для
|
|
288
|
+
конкретной локали: DSH рендерит `name:` уровня плагина дословно на обеих поверхностях разрешений
|
|
289
|
+
(строка по умолчанию в общих настройках и селектор композера) и даёт собственные локализованные
|
|
290
|
+
подписи только трём встроенным значениям, поэтому `cordis.patch.yml` поставляет китайскую подпись
|
|
291
|
+
для каждой сессии.
|
|
292
|
+
|
|
293
|
+
С **иконкой** история иная. Карта глифов композера закрыта, и её собственный комментарий
|
|
294
|
+
формулирует правило: *host-configured names outside the design set get none.* `permissive` —
|
|
295
|
+
встроенное значение, поэтому у 自动审查 уже есть глиф «щит+глаз»; `permissive-full` получает тот
|
|
296
|
+
же глиф только потому, что `npx dsh-perm-gate-patch-glyph` добавляет его в эту карту. Тот патч
|
|
297
|
+
правит пакет **хоста**, поэтому теряется при каждом обновлении DSH — см.
|
|
298
|
+
[После обновления DSH](./INSTALL.ru.md).
|
|
299
|
+
|
|
300
|
+
В `cordis.yml`:
|
|
301
|
+
|
|
302
|
+
```yaml
|
|
303
|
+
- id: dsh-perm-gate
|
|
304
|
+
name: dsh-perm-gate
|
|
305
|
+
config:
|
|
306
|
+
rulesFile: ./permissions.yaml
|
|
307
|
+
defaultAction: ask
|
|
308
|
+
permissive: true # single front-facing switch (independent tier on)
|
|
309
|
+
permissiveStrategies: # backend, combinable
|
|
310
|
+
trustAutoAllow: true # in-scope safe ops auto-allow; dangerous/unknown ask
|
|
311
|
+
alwaysConfirm: false # every crossing asks; allow-controls add repeat-allow / wl-migrate buttons
|
|
312
|
+
llmAssist: false # LLM classify first, human fallback on ask/failure
|
|
313
|
+
trustEscalation: true # a cleared call's own sandbox escalation needs no prompt
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
`trustAutoAllow` — базовый средний уровень (allow по правилу проходит автоматически).
|
|
317
|
+
`alwaysConfirm` показывает панель одобрения при каждом пересечении; его «allow-кнопки» добавляют
|
|
318
|
+
две расширенные кнопки — **повторно разрешить этот тип в этой сессии** (ограниченная сессионная
|
|
319
|
+
выдача) и **разрешить каждое вхождение** (она сохраняет слово команды в белый список `allow` файла
|
|
320
|
+
`permissions.yaml` через `approveAllowEverywhere`). `llmAssist` обращается к настоящей
|
|
321
|
+
настраиваемой LLM (`classifierEndpoint` / `classifierModel`, любой OpenAI-совместимый API), чтобы
|
|
322
|
+
автоматически решить `ask`, и возвращается к человеческому шову при `ask`/ошибке — всегда
|
|
323
|
+
fail-closed. `trustEscalation` (включён по умолчанию, когда включён уровень) отвечает на
|
|
324
|
+
эскалацию `sandbox_permissions`, поднятую изнутри вызова, который ворота уже разрешили; см. ниже.
|
|
325
|
+
Когда `permissive` выключен, ворота ведут себя ровно как раньше.
|
|
326
|
+
|
|
327
|
+
### Эскалация песочницы: почему вердикт `safe` всё равно показывал запрос
|
|
328
|
+
|
|
329
|
+
Вызов инструмента может поднять **два независимых одобрения**. Первым владеют ворота — их
|
|
330
|
+
собственный `ask` в водопаде `tools/pre-execute`. Второе приходит от `approveEscalation`
|
|
331
|
+
**внутри тела инструмента**, в момент `tools/execute`, всякий раз, когда модель передала
|
|
332
|
+
`sandbox_permissions` + `justification`; к тому моменту `tools/pre-execute` уже завершён, так что
|
|
333
|
+
allow ворот до него не доходит. Вызов, оценённый LLM как `safe` и автоматически разрешённый
|
|
334
|
+
воротами, всё равно показывал запрос подтвердить расширение песочницы.
|
|
335
|
+
|
|
336
|
+
`trustEscalation` закрывает эту брешь. Ворота помнят каждый вызов, который они положительно
|
|
337
|
+
разрешили (ключ — `callId` хоста, который запрос эскалации повторяет), и сами отвечают на
|
|
338
|
+
эскалацию `allowed-once`. Применяется только когда выполняется **всё**:
|
|
339
|
+
|
|
340
|
+
- уровень 自动审查 включён и `trustEscalation` включён;
|
|
341
|
+
- запрос несёт `callId`, который ворота пропустили, с совпадающим именем инструмента;
|
|
342
|
+
- причина — распознанная эскалация, называющая `workspace-write` или `danger-full-access`.
|
|
343
|
+
|
|
344
|
+
Всё остальное — нераспознанная причина, другой вызов, вызов, который ворота спрашивали или
|
|
345
|
+
запретили, сквозной проход `approval: never` — неизменно делегируется человеку, так что будущая
|
|
346
|
+
смена формулировок DSH завершится закрыто, а не открыто. Автоответ фиксируется в ленте событий
|
|
347
|
+
(`verdict: "escalation-auto"`, `mode: <цель>`). Выключите переключатель, чтобы расширение
|
|
348
|
+
песочницы оставалось под контролем человека, пока остальные allow остаются автоматическими.
|
|
349
|
+
|
|
350
|
+
### llmAssist с градацией риска, обучение вердиктов и лента событий
|
|
351
|
+
|
|
352
|
+
Когда `llmAssist` включён, настроенная LLM оценивает по одному `ask` за раз по структурному
|
|
353
|
+
протоколу. Оценка происходит **внутри водопада `tools/pre-execute` ворот, до того как решение
|
|
354
|
+
вернётся хосту**: вердикт `safe` проводит вызов напрямую, так что панель одобрения вообще не
|
|
355
|
+
появляется; до вас доходит только по-настоящему неопределённый вердикт. В карточке настроек
|
|
356
|
+
выбираются два источника приёмника: **собственный API** (любой OpenAI-совместимый endpoint —
|
|
357
|
+
`classifierEndpoint` / `classifierModel` / `classifierApiKey`, с пресетами, включающими Xiaomi
|
|
358
|
+
MiMo `https://api.xiaomimimo.com/v1`), либо **группа моделей хоста DSH** (настроенный сервис
|
|
359
|
+
`llm` сессии, через `agentDefaultModel.currentSelection`, с возможной подменой через
|
|
360
|
+
`classifierProvider` / `classifierModel`). Кнопка **проверки здоровья**
|
|
361
|
+
(`POST /api/dsh-perm-gate/health`) выполняет минимальную completion и сообщает задержку.
|
|
362
|
+
|
|
363
|
+
- `safe` → вызов разрешается автоматически (в аудите — источник `classifier`); панель не
|
|
364
|
+
показывается.
|
|
365
|
+
- `risky` + **жёсткая категория** (`deletion`, `credential`, `remote`, `system`, `bulk`) → вызов
|
|
366
|
+
**сохраняет человеческий ask**. Классификатор **никогда не запрещает**: путь запрета
|
|
367
|
+
принадлежит одним лишь детерминированным слоям (жёсткий запрет P0, чёрный список ключевых слов,
|
|
368
|
+
явные правила `deny:`), так что неверно оценённая категория всегда остаётся переговорной, а не
|
|
369
|
+
становится блокировкой без права обжалования. Жёсткие категории отличаются от `neutral` одним
|
|
370
|
+
важным образом: они **никогда не выучиваются**, поэтому повторные человеческие одобрения не
|
|
371
|
+
могут осадить их в авто-allow.
|
|
372
|
+
(Запрет на слове модели был измерен вживую: безобидный `git commit -F …`, оценённый как
|
|
373
|
+
`remote`, дал авто-запрет без панели и без выдачи для повторной попытки.)
|
|
374
|
+
- `risky:neutral` → при включённом `riskLearning` (карточка настроек, по умолчанию выключено)
|
|
375
|
+
каждое человеческое одобрение, реально исполненное (учитывается через событие `tools/result`
|
|
376
|
+
хоста), идёт в счёт ключа `инструмент|категория`; как только счётчик достигает `riskThreshold`
|
|
377
|
+
(по умолчанию 3) **и** отпечаток операции нового вызова (слово команды + имя файла цели)
|
|
378
|
+
совпадает с подтверждённым образцом, та же самая операция разрешается автоматически. Другая
|
|
379
|
+
цель никогда не переиспользует эти полномочия. С осадкой (`riskSediment`, по умолчанию
|
|
380
|
+
включена) подтверждённые образцы ключа, достигшего порога, становятся
|
|
381
|
+
**детерминированными правилами allow**: точное совпадение разрешает сразу, без нового вызова
|
|
382
|
+
LLM — даже при выключенном `llmAssist` — а осевшие правила видимы и управляемы (завершить /
|
|
383
|
+
удалить) в карточке настроек.
|
|
384
|
+
- Таймауты (`riskTimeoutMs`, по умолчанию 20 с, одна повторная попытка), транспортные сбои и
|
|
385
|
+
выход за пределы протокола оставляют ask нетронутым — ворота никогда не угадывают.
|
|
386
|
+
|
|
387
|
+
Состояние обучения сохраняется в принадлежащем плагину JSON (`$DSH_HOME/perm-gate/learning.json`
|
|
388
|
+
или `learningFile`), но никогда в ваш YAML-файл правил. Каждое решение дописывается в
|
|
389
|
+
`$DSH_HOME/perm-gate/events.jsonl` (или `eventsFile`) и раздаётся на
|
|
390
|
+
`GET /api/dsh-perm-gate/events?sessionId=&since=`; браузерная половина опрашивает её и показывает
|
|
391
|
+
последнее решение полосой уведомления над полем ввода беседы (ask остаётся видимым до следующего
|
|
392
|
+
события) и перечисляет все решения сессии от новейших во вкладке **Approvals** окна беседы.
|
|
393
|
+
|
|
394
|
+
Затронутые файлы каждого решения снимаются в снапшот до того, как изменение приземлится
|
|
395
|
+
(≤ 5 файлов, ≤ 256 КБ каждый), под `$DSH_HOME/perm-gate/snapshots/`; во вкладке **Approvals**
|
|
396
|
+
каждый чип файла открывает построчный дифф (`GET /api/dsh-perm-gate/diff`) с действием
|
|
397
|
+
**отката**, которое доставляет инструкцию восстановления в беседу
|
|
398
|
+
(`POST /api/dsh-perm-gate/revert`). Панель инвентаря снапшотов очищает их по сессии или целиком
|
|
399
|
+
(`GET /api/dsh-perm-gate/snapshots-stats` / `POST /api/dsh-perm-gate/snapshots-clear`).
|
|
400
|
+
|
|
401
|
+
`ask`, который ворота направили человеку, отслеживается до ответа человека: пассивный
|
|
402
|
+
наблюдатель `approval/request` записывает закрытый исход (`allowed-once` → **одобрено**,
|
|
403
|
+
`rejected` → **отклонено**, `cancelled` → **отменено**, `unavailable` → отказ, поскольку канала
|
|
404
|
+
одобрения не существовало), причём `tools/result` закрывает тот же ask как запасной механизм,
|
|
405
|
+
когда наблюдатель не может его сопоставить. Одобрения сообщают прогресс обучения после
|
|
406
|
+
одобрения (`n`/порог), а полоса уведомления подписывает все три терминальных состояния.
|
|
407
|
+
|
|
408
|
+
自动审查 и 自动审查(高权限) оба рисуют в селекторе глиф «щит+глаз» — первый из встроенной карты
|
|
409
|
+
DSH, второй из хостового патча, который описывает руководство по установке. Без того патча второй
|
|
410
|
+
уровень на всех поверхностях показывается текстом; его подпись и регулирование не затронуты.
|
|
411
|
+
|
|
412
|
+
### Выбираемый уровень сессии
|
|
413
|
+
|
|
414
|
+
`cordis.patch.yml` добавляет пресет `permissive` (`sandbox: workspace-write`, `approval: ask`,
|
|
415
|
+
имя **自动审查**) между «Записью в рабочем пространстве» и «Полным доступом». Патч бандла DSH
|
|
416
|
+
заменяет всю карту `permission.config.presets`, а не сливает по ключам, поэтому файл также
|
|
417
|
+
повторяет три встроенных (`read-only` / `workspace-write` / `danger-full-access`, из
|
|
418
|
+
`@deepseek-ai/dsh-base/cordis.patch.yml`); `test/patch-presets.spec.ts` фиксирует этот набор
|
|
419
|
+
ключей. Так селектор разрешений сессии предлагает 自动审查 как независимый выбираемый уровень
|
|
420
|
+
одобрения, а не общий режим «автоодобрения».
|
|
421
|
+
|
|
422
|
+
Ворота активны **только на уровнях, перечисленных в `gatePresets`** (по умолчанию
|
|
423
|
+
`['permissive', 'permissive-full']` — два уровня, добавляемых этим плагином). На любом другом
|
|
424
|
+
уровне — «Только чтение», «Запись в рабочем пространстве», «Полный доступ», `custom` — поток
|
|
425
|
+
решений ворот не запускается вовсе: ни allow, ни ask, ни deny, ни жёсткого запрета P0, ни вето
|
|
426
|
+
ключевых слов, ни события аудита. Выбранный уровень регулирует вызов собственной политикой, и в
|
|
427
|
+
этом смысл: встроенный `danger-full-access` определён как «полный доступ без запросов одобрения»,
|
|
428
|
+
так что перебить его ask'ом (там неответимым — шов одобрения отклоняет прежде, чем любой
|
|
429
|
+
ответчик запустится, выдавая `the user rejected tool "..."` без панели) или жёстким запретом —
|
|
430
|
+
значило бы молча противоречить выбранному пользователем уровню. `gatePresets: ['*']` снова
|
|
431
|
+
делает ворота глобальными (включая жёсткий запрет); внутри активного уровня `ask` по-прежнему
|
|
432
|
+
деградирует до сквозного прохода, когда действующая политика одобрения сессии — `never`, — вот
|
|
433
|
+
почему оба уровня 自动审查 объявляют `approval: ask`.
|
|
434
|
+
|
|
435
|
+
### Настраивается в интерфейсе
|
|
436
|
+
|
|
437
|
+
Уровень также регулируется во время выполнения через **Настройки → Плагины → 自动审查**
|
|
438
|
+
(страница `settings.plugins.tab`, которую рендерит браузерный клиент плагина): один переключатель
|
|
439
|
+
переключает `permissive`, четыре — правят серверные `permissiveStrategies`. Хост читает
|
|
440
|
+
пространство имён вживую, поэтому изменение применяется к следующему вызову инструмента без
|
|
441
|
+
перезапуска. Это независимый класс одобрения, а НЕ общий режим «автоодобрения».
|
|
442
|
+
|
|
443
|
+
### Тест правила (dry-run)
|
|
444
|
+
|
|
445
|
+
На той же странице есть панель **теста правила**: введите имя инструмента и команду, нажмите
|
|
446
|
+
«Тест», и ворота вынесут вердикт по этому вызову против текущего набора правил — ничего не
|
|
447
|
+
запуская и не записывая правил. Вы получите вердикт, совпавшее правило (индекс и действие),
|
|
448
|
+
размерности, которые оно ограничивает, и причину.
|
|
449
|
+
|
|
450
|
+
Панель сообщает и эффективный вердикт (вся цепочка P0 → P1 → P2 → P3 → P4), *и* собственный ответ
|
|
451
|
+
слоя правил, а это не одно и то же: жёсткий запрет P0 или deny-ключевое слово пресета срабатывают
|
|
452
|
+
до цепочки правил и не оставляют после себя индекса правила, поэтому панель говорит «ни одно
|
|
453
|
+
правило не совпало», вместо того чтобы называть постороннее правило. Пометка «загружено 0 правил»
|
|
454
|
+
означает, что путь `rulesFile` не разрешился ни во что.
|
|
455
|
+
|
|
456
|
+
Панель общается с `POST /api/dsh-perm-gate/dry-run`, который **по построению доступен только для
|
|
457
|
+
чтения** — у него вообще нет формы записи, так что тест правила никогда не сможет его изменить.
|
|
458
|
+
|
|
459
|
+
## CLI
|
|
460
|
+
|
|
461
|
+
Прогон одного вызова по файлу правил (без харнесса):
|
|
462
|
+
|
|
463
|
+
```sh
|
|
464
|
+
dsh-perm-gate --rules permissions.yaml --tool bash --args '{"command":"pnpm install"}'
|
|
465
|
+
dsh-perm-gate --rules permissions.yaml --list
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
## Разработка
|
|
469
|
+
|
|
470
|
+
```sh
|
|
471
|
+
npm run typecheck
|
|
472
|
+
npm test
|
|
473
|
+
npm run build
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
## Лицензия
|
|
477
|
+
|
|
478
|
+
[MIT](./LICENSE)
|
package/README.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
|
> **兼容性说明:** v2.0.0 自带 `ja` / `ko` 字典,但官方 DSH 的 `LocaleRuntime`
|
|
16
31
|
> 只暴露 `zh` / `en`(`LOCALE_IDS = ["zh", "en"]`)。在原版 DSH 上选择 `ja` / `ko`
|
|
@@ -74,7 +89,9 @@ dsh plugin --profile web add dsh-perm-gate
|
|
|
74
89
|
```
|
|
75
90
|
|
|
76
91
|
完整的安装、升级、迁移与排查步骤见[中文安装指南](./INSTALL.zh.md)(另有
|
|
77
|
-
[English](./INSTALL.md) / [日本語](./INSTALL.ja.md) / [한국어](./INSTALL.ko.md)
|
|
92
|
+
[English](./INSTALL.md) / [日本語](./INSTALL.ja.md) / [한국어](./INSTALL.ko.md) /
|
|
93
|
+
[Français](./INSTALL.fr.md) / [Deutsch](./INSTALL.de.md) / [Italiano](./INSTALL.it.md) /
|
|
94
|
+
[Русский](./INSTALL.ru.md) / [Español](./INSTALL.es.md))。
|
|
78
95
|
|
|
79
96
|
## 配置
|
|
80
97
|
|
package/dsh.plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "dsh-perm-gate",
|
|
3
3
|
"name": "dsh-perm-gate",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "5.0.2",
|
|
5
5
|
"description": {
|
|
6
6
|
"en": "Deterministic-first, fail-closed permission gate for DeepSeek Harness 0.1.2+: a P0 hard-deny -> P1 precise session grant -> P2 static allow/deny rule chain -> P3 optional LLM classifier -> P4 ask pipeline over the tools/pre-execute waterfall, with a standalone CLI dry-run evaluator and an auditable decision log. It only vetoes with deny/ask and always delegates otherwise, and unknown or malformed rules fail loud instead of silently standing the gate down.",
|
|
7
7
|
"zh": "面向 DeepSeek Harness 0.1.2+ 的确定性优先、失败即拒的权限闸门:在 tools/pre-execute 瀑布上串起 P0 硬拒绝 → P1 精确会话授权 → P2 静态 allow/deny 规则链 → P3 可选 LLM 语义分类 → P4 ask,并附带独立 CLI 干跑评估器与可审计的决策日志。只以 deny/ask 否决、其余一律放行,规则未知或非法时直接报错,绝不静默失效。"
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
},
|
|
14
14
|
"license": "MIT",
|
|
15
15
|
"engines": {
|
|
16
|
-
"dsh": ">=0.
|
|
16
|
+
"dsh": ">=0.2.0-rc.1 <0.2.1-0"
|
|
17
17
|
},
|
|
18
18
|
"components": {
|
|
19
19
|
"host": "lib/index.js",
|