dsh-perm-gate 2.1.2 → 2.6.2
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 +108 -0
- package/CHANGELOG.ko.md +104 -0
- package/CHANGELOG.md +239 -0
- package/INSTALL.ja.md +41 -21
- package/INSTALL.ko.md +37 -19
- package/INSTALL.md +46 -23
- package/INSTALL.zh.md +33 -17
- package/README.ja.md +52 -12
- package/README.ko.md +53 -13
- package/README.md +68 -10
- package/README.zh.md +41 -10
- package/assets/allow1.png +0 -0
- package/assets/allow2.png +0 -0
- package/assets/gateoff.png +0 -0
- package/assets/llms.png +0 -0
- package/assets/permlist.png +0 -0
- package/assets/setting-head.png +0 -0
- package/assets/setting-rules.png +0 -0
- package/cordis.patch.yml +11 -8
- package/docs/baseline-guard.md +56 -0
- package/docs/plan-rules-to-settings.md +217 -0
- package/docs/rules-format.en.md +294 -0
- package/docs/rules-format.md +279 -0
- package/docs/ui-gap-analysis-and-branch-permissions.md +338 -0
- package/dsh.plugin.json +2 -2
- package/examples/permissions.example.yaml +16 -1
- package/lib/allowlist.d.ts +27 -0
- package/lib/allowlist.js +103 -1
- package/lib/cli.js +11 -14
- package/lib/client.js +484 -6
- package/lib/client.js.map +1 -1
- package/lib/command-dispatcher.d.ts +22 -0
- package/lib/command-dispatcher.js +97 -0
- package/lib/command-semantics.d.ts +62 -0
- package/lib/command-semantics.js +6 -0
- package/lib/compiler.d.ts +11 -0
- package/lib/compiler.js +16 -0
- package/lib/config.d.ts +73 -12
- package/lib/config.js +56 -14
- package/lib/dry-run.d.ts +87 -0
- package/lib/dry-run.js +110 -0
- package/lib/engine.js +52 -2
- package/lib/evaluate.js +76 -2
- package/lib/events.d.ts +56 -2
- package/lib/events.js +92 -0
- package/lib/index.js +106 -26
- package/lib/parsers/git.d.ts +43 -0
- package/lib/parsers/git.js +404 -0
- package/lib/parsers/shell-cmds.d.ts +32 -0
- package/lib/parsers/shell-cmds.js +293 -0
- package/lib/preset.d.ts +13 -0
- package/lib/preset.js +17 -0
- package/lib/risk.d.ts +17 -3
- package/lib/risk.js +17 -3
- package/lib/rule-chain.js +13 -22
- package/lib/rule-dims.d.ts +35 -0
- package/lib/rule-dims.js +36 -0
- package/lib/rule.d.ts +36 -1
- package/lib/rule.js +56 -8
- package/lib/rules-view.d.ts +52 -0
- package/lib/rules-view.js +126 -0
- package/lib/runtime.d.ts +121 -7
- package/lib/runtime.js +204 -25
- package/lib/shell.d.ts +14 -0
- package/lib/shell.js +17 -0
- package/package.json +19 -2
- package/scripts/patch-permission-glyph.mjs +225 -42
package/CHANGELOG.ja.md
CHANGED
|
@@ -5,6 +5,114 @@
|
|
|
5
5
|
形式は [Keep a Changelog](https://keepachangelog.com/ja/1.1.0/) に従い、
|
|
6
6
|
このプロジェクトは [Semantic Versioning](https://semver.org/spec/v2.0.0.html) に準拠します。
|
|
7
7
|
|
|
8
|
+
## [2.4.1] - 2026-09-17
|
|
9
|
+
|
|
10
|
+
### 修正
|
|
11
|
+
|
|
12
|
+
- 承認履歴が、フラグ付きの呼び出しがなぜレビューされずに実行されたのかを説明する唯一のイベントについて、生の `preset-passthrough` 文字列を表示していました(ティアは `approval: ask` を宣言しているのに、セッションが `never` で上書きされ、ゲートの ask がパススルーに降格したためです)。他の判定と同じく読みやすいラベルで表示されるようになりました。
|
|
13
|
+
|
|
14
|
+
## [2.6.1] - 2026-09-25
|
|
15
|
+
|
|
16
|
+
### 修正
|
|
17
|
+
|
|
18
|
+
- **入力欄グリフのパッチが DSH 0.1.5 で再び機能します。** `scripts/patch-permission-glyph.mjs`
|
|
19
|
+
は `["permissive",` の直後にエントリを挿入していましたが、そのキーは DSH 0.1.2 がグリフを
|
|
20
|
+
描いていた値で、0.1.5-rc.2 のバンドルにはどこにも存在しません。そのためパッチは
|
|
21
|
+
`anchor not found` で失敗し、両ティアはテキストのみのままでした。取得元はティアごとに
|
|
22
|
+
`GLYPH_TARGETS` で宣言し、アンカーは**解決**します —— まず `["<key>",`、次にバンドルが使う
|
|
23
|
+
定数別名(`danger-full-access` は `[FULL_ACCESS,`)。ホストが描かないキーは、Map が実際に
|
|
24
|
+
持つキーと一緒に報告されます。
|
|
25
|
+
- **UI を提供していない DSH インストールにパッチを当てなくなりました。** 候補は profile
|
|
26
|
+
スコープを先、CLI インストールを次に並べ、同じバンドルを持つ**他の**インストールは黙って
|
|
27
|
+
見送らず出力に列挙します。このプロジェクトのホストで実測:グローバル CLI インストールが
|
|
28
|
+
ランチャー管理のランタイムを隠していたため、パッチは成功と報告しつつピッカーは
|
|
29
|
+
アイコンなしのままでした。
|
|
30
|
+
|
|
31
|
+
### 変更
|
|
32
|
+
|
|
33
|
+
- **各ティアは、共有するファイルサンドボックスのグリフを付けます(共通の盾+目ではなく)。**
|
|
34
|
+
「自动审查」は `workspace-write` のグリフを、「自动审查(高权限)」は `danger-full-access` の
|
|
35
|
+
グリフをコピーするため、アイコンはそのティアが実際に与えるアクセスと一致します。同一アイコンの
|
|
36
|
+
旧挙動は、`permissive` のグリフを持っていた 0.1.2 の Map に由来します。
|
|
37
|
+
- **2 つのグリフは両方適用するか、両方とも適用しません。** 解決できない取得元キーは何も書かない
|
|
38
|
+
拒否であり、部分的にパッチされたバンドルにはなりません。
|
|
39
|
+
|
|
40
|
+
## [2.4.0] - 2026-09-17
|
|
41
|
+
|
|
42
|
+
### 変更
|
|
43
|
+
|
|
44
|
+
- **P3 LLM 分類器はエスカレート専用になり、拒否できなくなりました。** ハードカテゴリ
|
|
45
|
+
(`deletion` / `credential` / `remote` / `system` / `bulk`) の `risky` 判定はこれまでパネルなしで
|
|
46
|
+
**自動拒否**していましたが、現在は**人間への確認(ask)を維持**します。拒否は決定論的レイヤー
|
|
47
|
+
(P0 ハード拒否、拒否キーワードのブラックリスト、明示的な `deny:` ルール)だけに属します。
|
|
48
|
+
確率的な判定が、異議を唱えられないブロックを下してはならないからです。実測: グレーダーは無害な
|
|
49
|
+
`git commit -F …` を **`remote`** と判定し、自動拒否は承認するパネルも再試行する grant も残しませんでした
|
|
50
|
+
(たまたま `safe` と判定された手動リトライだけが通りました)。これは「高リスク操作は**決定論的に**
|
|
51
|
+
遮断し、LLM の判断には委ねない」というプロジェクト自身のルールとも整合します。
|
|
52
|
+
|
|
53
|
+
### 追加
|
|
54
|
+
|
|
55
|
+
- ハードリスクカテゴリは `neutral` との重要な違いを一つだけ保ちます: **決して学習されません**。
|
|
56
|
+
人間の承認を繰り返しても `deletion`/`credential`/`remote`/`system`/`bulk` の判定が自動許可に
|
|
57
|
+
沈殿することはありません(従来は自動拒否の副作用としてのみ成立していましたが、現在は明示的な性質です)。
|
|
58
|
+
|
|
59
|
+
### 動作上の注意
|
|
60
|
+
|
|
61
|
+
- `approval: never` では、ゲートが配信できない ask は従来どおりパススルーに降格するため、分類器が
|
|
62
|
+
フラグを立てた呼び出しは**実行されます**(以前は自動拒否でした)。これは「決定論的に危険なものだけを
|
|
63
|
+
拒否し、それ以外は交渉する」の直接の帰結です —— 交渉には人間が必要で、`never` は人間がいないことを
|
|
64
|
+
意味します。フラグを実際に受け取りたい場合は `approval` が `ask` のティアで運用してください。
|
|
65
|
+
|
|
66
|
+
## [2.3.0] - 2026-09-17
|
|
67
|
+
|
|
68
|
+
### 追加
|
|
69
|
+
|
|
70
|
+
- **ゲートの停止はもはや沈黙しません。** セッションの権限プリセットが `gatePresets` の外にあるとき、
|
|
71
|
+
ゲートは停止して何も記録しませんでした —— そのためツール呼び出しは、ゲートが検査して許可した
|
|
72
|
+
呼び出しと見分けがつきませんでした。現在は (session, preset) の遷移ごとに **1 件だけ**
|
|
73
|
+
`stand-down` 通知を記録し(呼び出しごとではありません)、プリセット・スコープ・P0 ハード拒否が
|
|
74
|
+
無効である事実を明示します。ブラウザは入力欄の上に常駐の **GATE OFF** ストリップとして表示し、
|
|
75
|
+
承認履歴には `ゲート停止` タグが出ます。
|
|
76
|
+
- 自动审查 設定カードがゲート自身のプリセットスコープ(`gatePresets`、既定は
|
|
77
|
+
`permissive` / `permissive-full`)とその外側で何が起きるかを明示するため、スコープはティアを
|
|
78
|
+
設定する場所で確認できます。
|
|
79
|
+
|
|
80
|
+
### 変更
|
|
81
|
+
|
|
82
|
+
- **P0 の文書上の位置づけはグローバルではなくスコープ付きです。** P0 ハード拒否は*ゲートのプリセット
|
|
83
|
+
スコープ内*で単調かつ交渉不能です。プリセットをまたぐとゲートは P0 を含めて完全に停止します ——
|
|
84
|
+
選択されたティア自身のポリシーがそのセッションを所有するためです。コードは常にこう動作していましたが、
|
|
85
|
+
`AGENTS.md` と 4 つの README は逆を主張しており、`danger-full-access` が「P0 は依然適用される」と
|
|
86
|
+
読めてしまいました。`gatePresets: ['*']` を設定すると P0 は再びグローバルになります。
|
|
87
|
+
|
|
88
|
+
### 修正
|
|
89
|
+
|
|
90
|
+
- `DEFAULT_GATE_PRESETS` / `resolveGatePresets` を `config.ts`(schemastery を import しています)から
|
|
91
|
+
依存のない `preset.ts` へ移し、ブラウザ側が node 専用依存をクライアントバンドルに引き込まずに
|
|
92
|
+
スコープを描画できるようにしました。`config.ts` は両方を再エクスポートするため、既存の import は
|
|
93
|
+
変更ありません。
|
|
94
|
+
|
|
95
|
+
## [2.2.0] - 2026-09-17
|
|
96
|
+
|
|
97
|
+
### 修正
|
|
98
|
+
|
|
99
|
+
- **P0 ハード拒否が、4 つのシェルツール以外をすべて素通りさせていました。** `hardDenyReason` はシェル検査をローカルの正規表現 `/^(?:bash|pwsh|sh|cmd)$/` で門番していましたが、これは `shell` / `terminal` / `powershell` に**一致しません**。`shell` は DSH の主要シェルツールであるため、P0 のシェル検査(保護パスへのリダイレクト)は**最も一般的な呼び出し形に対して無効**でした。実測、同一コマンド `echo x > /etc/passwd`:`bash` 経由は遮断、`shell` 経由は**通過**。`engine.ts` は `evaluate.ts` から `SHELL_TOOLS` を取り込むよう変更しました——同じ事実の複製が 3 か所にあり、完全だったのは 1 つだけでした。
|
|
100
|
+
- **`git push --delete` が通常の push として解析されていました。** `hasDelete` は `analyzeSubcommand` で計算されながら `branch` ケースでしか読まれず、`git push --delete origin main` は通常 push 分岐(`destructiveness: 4`)に落ちていました。`DESTRUCTIVENESS_MAP` の `'push-delete': 5` は死んだデータでした。
|
|
101
|
+
- **保護ブランチ判定が、スラッシュを含む名前に誤反応していました。** 述語は最後の `/` より前を落としていたため、`backup/main` や `feat/release` が保護扱いになっていました。既知の ref 接頭辞(`refs/heads/`、`refs/tags/`、`refs/remotes/<remote>/`)のみを剥がすよう変更しました。方向が重要です——この述語は**取り消し不能な** P0 に供給され、見逃しにはキーワード/ルール/LLM 層という受け皿がありますが、誤検出は正当なワークフローを出口なく塞いでしまいます。
|
|
102
|
+
|
|
103
|
+
### 追加
|
|
104
|
+
|
|
105
|
+
- **保護ブランチに対するリモート履歴改変の P0 ハード拒否。** `main` / `master` / `production` / `release` / `stable` を強制上書きまたは削除する `git push` を、LLM 呼び出しより前に決定的に拒否します。従来の保護は、フラットでブランチを見ず、名前空間で上書き可能なキーワード `'push --force'` でした。これはその下に敷く交渉不能な床です。
|
|
106
|
+
- **「エスカレーションのみ」は慣習ではなく構造です**: ヘルパーは `string | undefined` を返し、呼び出し側はそれを deny / 未決定として読みます。allow を表現する手段がないため、P0 に配線してもゲートが許可する範囲を広げることはできません。
|
|
107
|
+
- 意図的に**対象外**(引き続きキーワード/ルール/LLM 層が担当): 非保護ブランチへの force push、ブランチ名を書かない `git push --force`、通常の push。
|
|
108
|
+
- コマンドパーサ(`command-dispatcher` / `command-semantics` / `parsers/git` / `parsers/shell-cmds`)の初の本番利用です。これまで自身のテストファイルからしか参照されていませんでした。
|
|
109
|
+
|
|
110
|
+
### テスト
|
|
111
|
+
|
|
112
|
+
- 新規 `test/git-protected-push.spec.ts`(13 件)。
|
|
113
|
+
- **反証**: 3 つの修正をそれぞれ戻すと、それを守るケースだけが赤くなります(計 7 件)。意図的に許可するケースは緑のままです。
|
|
114
|
+
- 全量 **40 ファイル / 459 件**、`typecheck` クリーン。
|
|
115
|
+
|
|
8
116
|
## [2.0.0] - 2026-09-11
|
|
9
117
|
|
|
10
118
|
### 修正
|
package/CHANGELOG.ko.md
CHANGED
|
@@ -5,6 +5,110 @@
|
|
|
5
5
|
형식은 [Keep a Changelog](https://keepachangelog.com/ko/1.1.0/)을 따르며,
|
|
6
6
|
이 프로젝트는 [Semantic Versioning](https://semver.org/spec/v2.0.0.html)을 준수합니다.
|
|
7
7
|
|
|
8
|
+
## [2.6.1] - 2026-09-25
|
|
9
|
+
|
|
10
|
+
### 수정됨
|
|
11
|
+
|
|
12
|
+
- **입력창 글리프 패치가 DSH 0.1.5에서 다시 동작합니다.** `scripts/patch-permission-glyph.mjs`는
|
|
13
|
+
`["permissive",` 뒤에 항목을 끼워 넣었지만, 그 키는 DSH 0.1.2가 글리프를 그리던 값이고
|
|
14
|
+
0.1.5-rc.2 번들에는 어디에도 없습니다. 그래서 패치는 `anchor not found`로 실패했고 두 티어는
|
|
15
|
+
텍스트만 표시했습니다. 이제 원본은 티어별로 `GLYPH_TARGETS`에 선언하고, 앵커는 **해석**합니다
|
|
16
|
+
—— 먼저 `["<key>",`, 다음으로 번들이 쓰는 상수 별칭(`danger-full-access`는 `[FULL_ACCESS,`).
|
|
17
|
+
호스트가 그리지 않는 키는 Map이 실제로 가진 키와 함께 보고됩니다.
|
|
18
|
+
- **UI를 제공하지 않는 DSH 설치에는 더 이상 패치하지 않습니다.** 후보는 profile 스코프를
|
|
19
|
+
먼저, CLI 설치를 다음으로 정렬하고, 같은 번들을 가진 **다른** 설치는 조용히 넘기지 않고
|
|
20
|
+
출력에 나열합니다. 이 프로젝트의 호스트에서 실측: 전역 CLI 설치가 런처가 관리하는
|
|
21
|
+
런타임을 가려서, 패치는 성공을 보고했지만 선택기는 아이콘 없이 남아 있었습니다.
|
|
22
|
+
|
|
23
|
+
### 변경됨
|
|
24
|
+
|
|
25
|
+
- **각 티어는 공유하는 파일 샌드박스의 글리프를 답니다(공통 방패+눈이 아니라).** 「自动审查」는
|
|
26
|
+
`workspace-write` 글리프를, 「自动审查(高权限)」는 `danger-full-access` 글리프를 복사하므로
|
|
27
|
+
아이콘이 그 티어가 실제로 부여하는 접근 권한과 일치합니다. 동일 아이콘 쌍은 `permissive`
|
|
28
|
+
글리프를 갖고 있던 0.1.2의 Map에서 온 동작입니다.
|
|
29
|
+
- **두 글리프는 모두 적용되거나 모두 적용되지 않습니다.** 해석할 수 없는 원본 키는 아무것도 쓰지
|
|
30
|
+
않는 거부이며, 일부만 패치된 번들이 되지 않습니다.
|
|
31
|
+
|
|
32
|
+
## [2.4.1] - 2026-09-17
|
|
33
|
+
|
|
34
|
+
### 수정됨
|
|
35
|
+
|
|
36
|
+
- 승인 이력이, 플래그된 호출이 왜 검토 없이 실행되었는지 설명하는 유일한 이벤트에 대해 원시 `preset-passthrough` 문자열을 표시했습니다(티어는 `approval: ask`를 선언했지만 세션이 `never`로 재정의되어 게이트의 ask가 패스스루로 강등된 경우입니다). 이제 다른 판정과 마찬가지로 읽기 쉬운 라벨로 표시됩니다.
|
|
37
|
+
|
|
38
|
+
## [2.4.0] - 2026-09-17
|
|
39
|
+
|
|
40
|
+
### 변경됨
|
|
41
|
+
|
|
42
|
+
- **P3 LLM 분류기는 에스컬레이션 전용이 되어 더 이상 거부할 수 없습니다.** 하드 카테고리
|
|
43
|
+
(`deletion` / `credential` / `remote` / `system` / `bulk`)의 `risky` 판정은 이전에는 패널 없이
|
|
44
|
+
**자동 거부**했지만, 이제는 **사람 확인(ask)을 유지**합니다. 거부는 결정론적 계층(P0 하드 거부,
|
|
45
|
+
거부 키워드 블랙리스트, 명시적 `deny:` 규칙)에만 속합니다. 확률적 판정이 이의를 제기할 수 없는
|
|
46
|
+
차단을 내려서는 안 되기 때문입니다. 실측: 그레이더가 무해한 `git commit -F …`를 **`remote`**로
|
|
47
|
+
판정했고, 자동 거부는 승인할 패널도 재시도할 grant도 남기지 않았습니다(우연히 `safe`로 판정된
|
|
48
|
+
수동 재시도만 통과했습니다). 이는 "고위험 작업은 **결정론적으로** 차단하고 LLM 판단에 맡기지
|
|
49
|
+
않는다"는 프로젝트 자체 규칙과도 일치합니다.
|
|
50
|
+
|
|
51
|
+
### 추가됨
|
|
52
|
+
|
|
53
|
+
- 하드 위험 카테고리는 `neutral`과의 중요한 차이를 하나만 유지합니다: **결코 학습되지 않습니다**.
|
|
54
|
+
사람의 승인을 반복해도 `deletion`/`credential`/`remote`/`system`/`bulk` 판정이 자동 허용으로
|
|
55
|
+
침전되지 않습니다(이전에는 자동 거부의 부수 효과로만 성립했지만, 이제는 명시적 속성입니다).
|
|
56
|
+
|
|
57
|
+
### 동작 주의
|
|
58
|
+
|
|
59
|
+
- `approval: never`에서는 게이트가 전달할 수 없는 ask가 여전히 패스스루로 강등되므로, 분류기가
|
|
60
|
+
플래그한 호출은 이제 **실행됩니다**(이전에는 자동 거부였습니다). 이는 "결정론적으로 위험한 것만
|
|
61
|
+
거부하고 나머지는 협상한다"의 직접적 귀결입니다 —— 협상에는 사람이 필요하고, `never`는 사람이
|
|
62
|
+
없다는 뜻입니다. 플래그를 실제로 받으려면 `approval`이 `ask`인 티어에서 운용하십시오.
|
|
63
|
+
|
|
64
|
+
## [2.3.0] - 2026-09-17
|
|
65
|
+
|
|
66
|
+
### 추가됨
|
|
67
|
+
|
|
68
|
+
- **게이트 정지는 더 이상 침묵하지 않습니다.** 세션 권한 프리셋이 `gatePresets` 밖에 있을 때 게이트는
|
|
69
|
+
정지하고 아무것도 기록하지 않았습니다 —— 그래서 그 도구 호출은 게이트가 검사하고 허용한 호출과
|
|
70
|
+
구별되지 않았습니다. 이제 (session, preset) 전환마다 **1건만** `stand-down` 알림을 기록하고
|
|
71
|
+
(호출마다가 아닙니다), 프리셋·범위·P0 하드 거부가 비활성이라는 사실을 명시합니다. 브라우저는
|
|
72
|
+
입력창 위에 상시 **GATE OFF** 표시줄로 렌더링하고, 승인 이력에는 `게이트 정지` 태그가 표시됩니다.
|
|
73
|
+
- 自动审查 설정 카드가 게이트 자체의 프리셋 범위(`gatePresets`, 기본값
|
|
74
|
+
`permissive` / `permissive-full`)와 그 밖에서 벌어지는 일을 명시하므로, 범위를 티어를 설정하는
|
|
75
|
+
곳에서 확인할 수 있습니다.
|
|
76
|
+
|
|
77
|
+
### 변경됨
|
|
78
|
+
|
|
79
|
+
- **P0의 문서상 위치는 전역이 아니라 범위 한정입니다.** P0 하드 거부는 *게이트의 프리셋 범위 안에서*
|
|
80
|
+
단조롭고 협상 불가능합니다. 프리셋을 넘어가면 게이트는 P0를 포함해 완전히 정지합니다 —— 선택된
|
|
81
|
+
티어 자체의 정책이 그 세션을 소유하기 때문입니다. 코드는 항상 이렇게 동작했지만 `AGENTS.md`와
|
|
82
|
+
4개 README는 반대로 주장해, `danger-full-access`가 "P0는 여전히 적용된다"로 읽혔습니다.
|
|
83
|
+
`gatePresets: ['*']`로 설정하면 P0는 다시 전역이 됩니다.
|
|
84
|
+
|
|
85
|
+
### 수정됨
|
|
86
|
+
|
|
87
|
+
- `DEFAULT_GATE_PRESETS` / `resolveGatePresets`를 `config.ts`(schemastery를 import합니다)에서
|
|
88
|
+
의존성 없는 `preset.ts`로 옮겨, 브라우저 쪽이 node 전용 의존성을 클라이언트 번들로 끌어오지 않고
|
|
89
|
+
범위를 렌더링할 수 있게 했습니다. `config.ts`는 둘 다 재수출하므로 기존 import는 변경이 없습니다.
|
|
90
|
+
|
|
91
|
+
## [2.2.0] - 2026-09-17
|
|
92
|
+
|
|
93
|
+
### 수정
|
|
94
|
+
|
|
95
|
+
- **P0 하드 거부가 4개의 셸 도구를 제외한 나머지를 모두 통과시키고 있었습니다.** `hardDenyReason`은 셸 검사를 로컬 정규식 `/^(?:bash|pwsh|sh|cmd)$/`로 막고 있었는데, 이는 `shell` / `terminal` / `powershell`에 **일치하지 않습니다**. `shell`은 DSH의 주 셸 도구이므로, P0의 셸 검사(보호 경로로의 리다이렉트)는 **가장 흔한 호출 형태에 대해 무력**했습니다. 실측, 동일 명령 `echo x > /etc/passwd`: `bash` 경유는 차단, `shell` 경유는 **통과**. `engine.ts`가 이제 `evaluate.ts`에서 `SHELL_TOOLS`를 가져옵니다——같은 사실의 사본이 세 곳에 있었고, 완전한 것은 하나뿐이었습니다.
|
|
96
|
+
- **`git push --delete`가 일반 push로 파싱되고 있었습니다.** `hasDelete`는 `analyzeSubcommand`에서 계산되지만 `branch` 케이스에서만 읽혔고, 그 결과 `git push --delete origin main`은 일반 push 분기(`destructiveness: 4`)로 떨어졌습니다. `DESTRUCTIVENESS_MAP`의 `'push-delete': 5`는 죽은 데이터였습니다.
|
|
97
|
+
- **보호 브랜치 판정이 슬래시를 포함한 이름에 오작동하고 있었습니다.** 술어가 마지막 `/` 앞부분을 잘라내어 `backup/main`과 `feat/release`가 보호 대상으로 읽혔습니다. 이제 알려진 ref 접두사(`refs/heads/`, `refs/tags/`, `refs/remotes/<remote>/`)만 제거합니다. 방향이 중요합니다——이 술어는 **되돌릴 수 없는** P0에 공급되며, 놓침에는 키워드/규칙/LLM 계층이라는 안전망이 있지만 오탐은 정당한 워크플로를 출구 없이 막습니다.
|
|
98
|
+
|
|
99
|
+
### 추가
|
|
100
|
+
|
|
101
|
+
- **보호 브랜치에 대한 원격 이력 재작성의 P0 하드 거부.** `main` / `master` / `production` / `release` / `stable`을 강제 덮어쓰기하거나 삭제하는 `git push`를 LLM 호출 이전에 결정론적으로 거부합니다. 기존 보호는 평면적이고 브랜치를 보지 않으며 네임스페이스로 덮어쓸 수 있는 키워드 `'push --force'`였습니다. 이것은 그 아래에 깔리는 협상 불가능한 바닥입니다.
|
|
102
|
+
- **"에스컬레이션 전용"은 관례가 아니라 구조입니다**: 헬퍼는 `string | undefined`를 반환하고 호출자는 이를 deny / 미결로 읽습니다. allow를 표현할 수단이 없으므로 P0에 배선해도 게이트가 허용하는 범위를 넓힐 수 없습니다.
|
|
103
|
+
- 의도적으로 **대상에서 제외**(키워드/규칙/LLM 계층이 계속 담당): 비보호 브랜치로의 force push, 브랜치명을 쓰지 않은 `git push --force`, 일반 push.
|
|
104
|
+
- 커맨드 파서(`command-dispatcher` / `command-semantics` / `parsers/git` / `parsers/shell-cmds`)의 첫 프로덕션 사용입니다. 지금까지는 자체 테스트 파일에서만 참조되고 있었습니다.
|
|
105
|
+
|
|
106
|
+
### 테스트
|
|
107
|
+
|
|
108
|
+
- 신규 `test/git-protected-push.spec.ts`(13건).
|
|
109
|
+
- **반증**: 세 수정을 각각 되돌리면 그것을 지키는 케이스만 빨개집니다(총 7건). 의도적으로 허용하는 케이스는 초록으로 남습니다.
|
|
110
|
+
- 전체 **40 파일 / 459건**, `typecheck` 클린.
|
|
111
|
+
|
|
8
112
|
## [2.0.0] - 2026-09-11
|
|
9
113
|
|
|
10
114
|
### 수정
|
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,245 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [2.6.2] - 2026-09-25
|
|
9
|
+
|
|
10
|
+
> 2.6.1 was never published: the registry staged that publish and never released
|
|
11
|
+
> it, while keeping the number taken. Same content, next number.
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- **The composer glyph patch works on DSH 0.1.5 again.** `scripts/patch-permission-glyph.mjs`
|
|
16
|
+
spliced its entry after `["permissive",` — a key DSH 0.1.2 rendered a glyph for and 0.1.5-rc.2
|
|
17
|
+
no longer contains anywhere in the bundle — so the patch died with `anchor not found` and both
|
|
18
|
+
plugin tiers stayed text-only. The sources are now declared per tier in `GLYPH_TARGETS` and each
|
|
19
|
+
anchor is **resolved**: `["<key>",` first, then the constant alias the bundle uses instead
|
|
20
|
+
(`danger-full-access` is keyed `[FULL_ACCESS,`). A source key this host does not render is
|
|
21
|
+
reported together with the keys the map does carry.
|
|
22
|
+
- **The probe no longer patches a DSH install that is not serving the UI.** Candidate bundles are
|
|
23
|
+
ordered profile scope first, CLI install second, and any *other* install carrying the same
|
|
24
|
+
bundle is listed in the output instead of being silently passed over. Measured on this
|
|
25
|
+
project's own host: a global CLI install shadowed the launcher-managed runtime, so the patch
|
|
26
|
+
reported success while the picker stayed icon-free.
|
|
27
|
+
|
|
28
|
+
### Changed
|
|
29
|
+
|
|
30
|
+
- **Each tier wears the glyph of the file sandbox it shares, not one shared shield+eye.** 自动审查
|
|
31
|
+
copies the `workspace-write` glyph and 自动审查(高权限) the `danger-full-access` one, so the icon
|
|
32
|
+
matches the access the tier actually grants. The old identical-icon pair came from 0.1.2, whose
|
|
33
|
+
map still keyed a `permissive` glyph that 0.1.5 dropped.
|
|
34
|
+
- **Both glyphs are applied, or neither.** A source key the script cannot resolve is a refusal that
|
|
35
|
+
writes nothing, rather than a half-patched bundle.
|
|
36
|
+
|
|
37
|
+
## [2.6.0] - 2026-09-17
|
|
38
|
+
|
|
39
|
+
### Added
|
|
40
|
+
|
|
41
|
+
- **Rule test (dry-run) in the settings card.** A new panel evaluates one would-be tool call
|
|
42
|
+
against the ruleset the gate currently has loaded and shows the verdict, the matched rule (index
|
|
43
|
+
and action), the dimensions that rule constrains, and the reason. Written for the question the
|
|
44
|
+
YAML cannot answer by inspection: *what will this actually do?* — `args` is an OR over tokens, an
|
|
45
|
+
empty dimension constrains nothing, and `command` matches the decomposed command **word** only, so
|
|
46
|
+
a plausible-looking rule can be dead on arrival.
|
|
47
|
+
|
|
48
|
+
The report keeps two answers apart on purpose. **`verdict`** is the policy result of the whole
|
|
49
|
+
chain (P0 hard-deny → deny-keyword → P1 grant → P2 rules → P4 ask → permissive).
|
|
50
|
+
**`ruleLayer`** is what the `permissions` chain decides on its own, and only that layer can name a
|
|
51
|
+
rule index: a P0 hard-deny or a preset deny-keyword fires *before* it and leaves no rule behind, so
|
|
52
|
+
the panel says so instead of attributing an unrelated rule. `matchedDimensions` likewise lists the
|
|
53
|
+
dimensions the matched rule *constrains* rather than the one that "caused" the match — dimensions
|
|
54
|
+
are ANDed, so a single cause cannot honestly be named. A `ruleCount` of 0 is surfaced as "0 rules
|
|
55
|
+
loaded, check the rulesFile path" rather than as "nothing matched".
|
|
56
|
+
|
|
57
|
+
- **`POST /api/dsh-perm-gate/dry-run`** — the panel's endpoint. **Read-only by construction**: it
|
|
58
|
+
has no write form, unknown body keys are dropped rather than forwarded, and it never touches
|
|
59
|
+
rules, grants, learning state or the settings namespace. Testing a rule must not be able to change
|
|
60
|
+
it. It evaluates against the **live** runtime rather than a fresh one, so the panel tests the rules
|
|
61
|
+
in force — a fresh runtime would re-resolve the chain from a different root and could silently
|
|
62
|
+
answer about a different file.
|
|
63
|
+
|
|
64
|
+
- **`src/dry-run.ts`** — `runDryRun` / `createDryRunRuntime`, the shared evaluator for the CLI and
|
|
65
|
+
the route, plus `PermGateRuntime.explainRules` (read-only rule-layer report) and
|
|
66
|
+
`PermGateRuntime.explainCall` (pure policy evaluation). `src/cli.ts` is now a thin wrapper over it;
|
|
67
|
+
its output is unchanged across nine replayed invocations, verified byte for byte (including the
|
|
68
|
+
`--list`, no-tool, bad-JSON and write-path cases).
|
|
69
|
+
|
|
70
|
+
### Fixed
|
|
71
|
+
|
|
72
|
+
- **The rule-test panel reported `allow` for calls the rules send to a human.** The first version
|
|
73
|
+
evaluated through the host-facing path, which applies the session layers — the preset stand-down
|
|
74
|
+
and the `approval: never` ask degradation. A session-less dry-run has no session, so the policy
|
|
75
|
+
reader answered "never", every `ask` degraded to a passthrough, and the panel showed **allow** for
|
|
76
|
+
exactly the rules someone opens it to review. Measured on the live host: `shell ls -la` showed
|
|
77
|
+
`allow` while the rule layer on the same card said `ask`.
|
|
78
|
+
|
|
79
|
+
`runDryRun` now reports the **pure policy** verdict from `explainCall`: the same
|
|
80
|
+
P0 → deny-keyword → P1 → P2 → P4 chain with the session-state layers excluded rather than guessed,
|
|
81
|
+
and with `reason` never collapsed to a bare `(default/passthrough)`. The host-facing view remains
|
|
82
|
+
available under `input.hostView`; the CLI asks for it, so its historic output stays byte for byte
|
|
83
|
+
identical (re-verified: 9/9 cases, SHA256 per case).
|
|
84
|
+
|
|
85
|
+
- **The "read-only" route was not read-only.** It ran the host-facing decision path against the live
|
|
86
|
+
runtime, which appends audit entries and records decision events — opening the rule-test panel
|
|
87
|
+
wrote to the live decision feed. The pure path appends nothing, and `test/dry-run.spec.ts` now
|
|
88
|
+
asserts that a call the host path *would* record leaves the audit mirror at zero.
|
|
89
|
+
|
|
90
|
+
## [2.5.0] - 2026-09-17
|
|
91
|
+
|
|
92
|
+
### Added
|
|
93
|
+
|
|
94
|
+
- **`branch` rule dimension** — git branch / remote / protected-branch matching, so a rule can
|
|
95
|
+
finally say "no push to a protected branch" without catching unrelated commands. The decisive
|
|
96
|
+
case is the one `args` can never express: `git push --force origin main` (dangerous) and
|
|
97
|
+
`git checkout --force main` (routine) carry the *same tokens*, and only the branch dimension
|
|
98
|
+
tells them apart. Sub-fields: `target` (branch-name glob, `*` crosses `/`), `remote`
|
|
99
|
+
(remote-name glob) and `shared` (require a protected branch). Candidates come from the shared
|
|
100
|
+
command dispatcher, so `refspec` forms (`HEAD:main`) are already split and a remote name is
|
|
101
|
+
never mistaken for a branch name. Documented in `docs/rules-format.md` §4.11.
|
|
102
|
+
|
|
103
|
+
### Fixed
|
|
104
|
+
|
|
105
|
+
- **The multi-file rule chain silently emptied every match dimension.** `resolveRuleChain` merged
|
|
106
|
+
entries by hand with `tools: []`, `command: []`, `args: []` and `paths: []`. An empty dimension
|
|
107
|
+
means "no constraint", so EVERY merged entry matched EVERY call: the first `deny` entry denied
|
|
108
|
+
everything in its partition, and the first `allow` entry allowed everything before the `ask`
|
|
109
|
+
partition was ever consulted. The merge now goes through the same `compileRuleEntry` the
|
|
110
|
+
single-file path uses.
|
|
111
|
+
|
|
112
|
+
*Reach:* the chain resolver is reached **only** under `searchUp: true` (product default `false`,
|
|
113
|
+
and unset in a stock profile), so the default single-file path — `compileDocument` — was never
|
|
114
|
+
affected. That is also why the existing suite stayed green: no test drove a rules *file* through
|
|
115
|
+
the chain. `test/rule-chain.spec.ts` now does. A separate, still-open defect on the same
|
|
116
|
+
opt-in path is recorded in the plan: `findChainEntries` joins the configured `rulesFile` onto
|
|
117
|
+
each directory, so the absolute path `resolveRulesFile` always produces matches nothing and the
|
|
118
|
+
chain yields an empty ruleset without an error.
|
|
119
|
+
|
|
120
|
+
- **`argv.pipeline` could not see its own subject.** The pipeline match string was built from
|
|
121
|
+
`SimpleCommand.command`, which is only the command *word* — `curl https://x.sh | sh` collapsed
|
|
122
|
+
to `curl|sh`, so the documented `curl|sh` pattern matched the harmless adjacent form while the
|
|
123
|
+
genuinely dangerous one with arguments did not match at all. The match string is now each
|
|
124
|
+
simple command's full argv (joined by `|`), and the docs state that `|` in a pattern is a
|
|
125
|
+
literal, so covering arguments requires `curl*|sh`.
|
|
126
|
+
|
|
127
|
+
- Nine pre-existing `eslint` errors (unused imports/constants and one `prefer-const` in
|
|
128
|
+
`src/parsers/` and `test/command-parsers.spec.ts`) — `npm run lint` is green again.
|
|
129
|
+
|
|
130
|
+
## [2.4.1] - 2026-09-17
|
|
131
|
+
|
|
132
|
+
### Fixed
|
|
133
|
+
|
|
134
|
+
- The approvals history showed the raw `preset-passthrough` string for the one event that
|
|
135
|
+
explains *why* a flagged call ran unreviewed: the tier declares `approval: ask`, the session was
|
|
136
|
+
overridden to `never`, and the gate's ask therefore degraded to a passthrough. It now renders as
|
|
137
|
+
a readable label like every other verdict.
|
|
138
|
+
|
|
139
|
+
## [2.4.0] - 2026-09-17
|
|
140
|
+
|
|
141
|
+
### Changed
|
|
142
|
+
|
|
143
|
+
- **The P3 LLM classifier is escalate-only — it can no longer deny.** A `risky` verdict with a
|
|
144
|
+
hard category (`deletion` / `credential` / `remote` / `system` / `bulk`) used to **auto-deny**
|
|
145
|
+
with no panel; it now **keeps the human ask**. Denying is reserved for the deterministic layers
|
|
146
|
+
alone — P0 hard-deny, the deny-keyword blacklist, explicit `deny:` rules — because a
|
|
147
|
+
probabilistic verdict must not be able to hand down an unappealable block. Measured live: the
|
|
148
|
+
grader called a benign `git commit -F …` **`remote`** and the auto-deny left no panel to approve
|
|
149
|
+
and no grant to retry with; only a manual retry (which happened to grade `safe`) got past it.
|
|
150
|
+
This also aligns the code with the project's own rule that high-risk operations are intercepted
|
|
151
|
+
**deterministically, never on the LLM's judgement**.
|
|
152
|
+
|
|
153
|
+
### Added
|
|
154
|
+
|
|
155
|
+
- Hard risk categories keep one meaningful distinction from `neutral`: they are **never
|
|
156
|
+
learnable**. Repeated human approvals cannot sediment a `deletion`/`credential`/`remote`/
|
|
157
|
+
`system`/`bulk` verdict into an auto-allow (previously this was true only as a side effect of
|
|
158
|
+
the auto-deny; it is now an explicit property).
|
|
159
|
+
|
|
160
|
+
### Behaviour notes
|
|
161
|
+
|
|
162
|
+
- Under `approval: never` an ask the gate cannot deliver still degrades to passthrough, so a
|
|
163
|
+
classifier-flagged call now **runs** where it used to be auto-denied. That is the direct
|
|
164
|
+
consequence of "deny only what is deterministically dangerous, negotiate everything else": a
|
|
165
|
+
negotiation needs a human, and `never` means there is none. Run the gate in a tier whose
|
|
166
|
+
`approval` is `ask` for the flags to reach you.
|
|
167
|
+
|
|
168
|
+
## [2.3.0] - 2026-09-17
|
|
169
|
+
|
|
170
|
+
### Added
|
|
171
|
+
|
|
172
|
+
- **A stand-down is no longer silent.** When the session's permission preset is outside
|
|
173
|
+
`gatePresets`, the gate used to stand down and record nothing — so the tool call looked
|
|
174
|
+
exactly like one the gate had inspected and allowed. It now records **one** `stand-down`
|
|
175
|
+
notice per (session, preset) transition (never per call), naming the preset, the scope and
|
|
176
|
+
the fact that P0 hard-deny is inactive. The browser renders it as a sticky **GATE OFF**
|
|
177
|
+
strip above the input, and the approvals history shows a `Gate off` tag.
|
|
178
|
+
- The 自动审查 settings card states the gate's own preset scope (`gatePresets`, default
|
|
179
|
+
`permissive` / `permissive-full`) and what happens outside it, so the scope is visible
|
|
180
|
+
where the tier is configured.
|
|
181
|
+
|
|
182
|
+
### Changed
|
|
183
|
+
|
|
184
|
+
- **P0's documented positioning is scoped, not global.** P0 hard-deny is monotonic and
|
|
185
|
+
non-negotiable *within the gate's preset scope*; across presets the gate stands down
|
|
186
|
+
entirely — P0 included — because the selected tier's own policy owns that session. The
|
|
187
|
+
code always behaved this way; `AGENTS.md` and the four READMEs claimed otherwise, which
|
|
188
|
+
made `danger-full-access` read as "P0 still applies". Set `gatePresets: ['*']` to make P0
|
|
189
|
+
global again.
|
|
190
|
+
|
|
191
|
+
### Fixed
|
|
192
|
+
|
|
193
|
+
- `DEFAULT_GATE_PRESETS` / `resolveGatePresets` moved from `config.ts` (which imports
|
|
194
|
+
schemastery) into the dependency-free `preset.ts`, so the browser half can render the scope
|
|
195
|
+
without pulling a node-only dependency into the client bundle. `config.ts` re-exports both,
|
|
196
|
+
so existing imports are unchanged.
|
|
197
|
+
|
|
198
|
+
## [2.2.0] - 2026-09-17
|
|
199
|
+
|
|
200
|
+
### Fixed
|
|
201
|
+
|
|
202
|
+
- **P0 hard-deny skipped every shell tool but four.** `hardDenyReason` gated its shell
|
|
203
|
+
inspection on a locally defined regex, `/^(?:bash|pwsh|sh|cmd)$/`, which does **not**
|
|
204
|
+
match `shell`, `terminal` or `powershell`. `shell` is DSH's primary shell tool, so the
|
|
205
|
+
whole P0 shell check — the protected-path redirect guard — was **inert for the most
|
|
206
|
+
common call shape**. Measured, same command `echo x > /etc/passwd`: blocked via `bash`,
|
|
207
|
+
**allowed** via `shell`. `engine.ts` now imports `SHELL_TOOLS` from `evaluate.ts`; the
|
|
208
|
+
gate held three copies of that one fact and only one of them was complete.
|
|
209
|
+
- **`git push --delete` was parsed as an ordinary push.** `hasDelete` was computed in
|
|
210
|
+
`analyzeSubcommand` but only ever read in the `branch` case, so `git push --delete
|
|
211
|
+
origin main` fell through to the plain-push branch (`destructiveness: 4`) and the
|
|
212
|
+
`DESTRUCTIVENESS_MAP` entry `'push-delete': 5` was dead data.
|
|
213
|
+
- **Protected-branch detection mis-fired on any name containing a slash.** The predicate
|
|
214
|
+
stripped everything before the last `/`, so `backup/main` and `feat/release` were read
|
|
215
|
+
as protected. It now strips only known ref prefixes (`refs/heads/`, `refs/tags/`,
|
|
216
|
+
`refs/remotes/<remote>/`). The direction matters: this predicate feeds an **unremovable**
|
|
217
|
+
P0, where a miss still has the keyword / rule / LLM layers behind it, while a false
|
|
218
|
+
positive blocks a legitimate workflow with no recourse.
|
|
219
|
+
|
|
220
|
+
### Added
|
|
221
|
+
|
|
222
|
+
- **P0 hard-deny for remote-history rewrite on a protected branch.** A `git push` that
|
|
223
|
+
force-overwrites or deletes `main` / `master` / `production` / `release` / `stable` is
|
|
224
|
+
rejected deterministically, before any LLM call. The pre-existing protection was the
|
|
225
|
+
flat, branch-blind, namespace-overridable keyword `'push --force'`; this is the
|
|
226
|
+
non-negotiable floor beneath it.
|
|
227
|
+
- **Escalate-only by construction, not by convention**: the helper returns
|
|
228
|
+
`string | undefined`, which the caller reads as deny / undecided. It has no way to
|
|
229
|
+
express allow, so wiring it into P0 cannot widen what the gate permits.
|
|
230
|
+
- Deliberately **not** covered (still left to the keyword / rule / LLM layers):
|
|
231
|
+
force-push to a non-protected branch, `git push --force` with no branch named, and
|
|
232
|
+
ordinary pushes.
|
|
233
|
+
- First production use of the command parser (`command-dispatcher`, `command-semantics`,
|
|
234
|
+
`parsers/git`, `parsers/shell-cmds`), which until now was referenced only by its own
|
|
235
|
+
test file.
|
|
236
|
+
|
|
237
|
+
### Tests
|
|
238
|
+
|
|
239
|
+
- New suite `test/git-protected-push.spec.ts` (13 cases): parser variants, the
|
|
240
|
+
protected-branch predicate in both directions, full `SHELL_TOOLS` coverage, the four
|
|
241
|
+
deliberately-allowed shapes, and compound-command segmentation
|
|
242
|
+
(`git status && git push --force origin main`).
|
|
243
|
+
- **Falsified**: reverting each of the three fixes turns exactly the cases guarding it red
|
|
244
|
+
(7 failures in total), while the deliberate-allow cases stay green.
|
|
245
|
+
- Full suite **40 files / 459 cases**; `typecheck` clean.
|
|
246
|
+
|
|
8
247
|
## [2.1.2] - 2026-09-15
|
|
9
248
|
|
|
10
249
|
### Added
|
package/INSTALL.ja.md
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
- [日本語 changelog](./CHANGELOG.ja.md)
|
|
13
13
|
- [한국어 changelog](./CHANGELOG.ko.md)
|
|
14
14
|
|
|
15
|
-
`dsh-perm-gate` バージョン **2.1
|
|
15
|
+
`dsh-perm-gate` バージョン **2.6.1**。判定チェーン、ルールファイル形式、自动审查
|
|
16
16
|
ティアについては [日本語 README](./README.ja.md) を参照してください。
|
|
17
17
|
|
|
18
18
|
## 必要条件
|
|
@@ -23,19 +23,20 @@
|
|
|
23
23
|
- インストール先の profile。ブラウザ側は `web` profile 向けにのみビルドされます
|
|
24
24
|
(`package.json` の `dsh.client.platform = "web"`)。
|
|
25
25
|
|
|
26
|
-
## DSH
|
|
26
|
+
## DSH のバージョンに合うラインを選ぶ
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
2 つの長期ブランチが 2 つの DSH ラインを担当します:`main`(2.x)と
|
|
29
|
+
`sync/0.1.5-from-main`(3.x、DSH 0.1.5 のパッケージング変体 —— `compat/0.1.5` は
|
|
30
|
+
同じコードツリーを指します)。
|
|
31
31
|
|
|
32
32
|
| DSH のバージョン | インストール方法 |
|
|
33
33
|
|-----------------|-----------------|
|
|
34
|
-
| `0.1.2-alpha.1`
|
|
34
|
+
| `0.1.2-alpha.1` – `0.1.5-rc.0` | `dsh plugin --profile web add dsh-perm-gate`(tag `latest`、2.x ライン) |
|
|
35
|
+
| `0.1.5-rc.1` 以上 | `dsh plugin --profile web add github:drscrewdriver/dsh-perm-gate#sync/0.1.5-from-main`(3.x ライン;npm の dist-tag は未公開 —— `@latest` は同じコードの 2.x パッケージングをインストールします) |
|
|
35
36
|
| `0.1.1-rc.2` まで | `dsh plugin --profile web add dsh-perm-gate@legacy` |
|
|
36
37
|
|
|
37
|
-
DSH は `engines.dsh` を強制しないため、tag
|
|
38
|
-
|
|
38
|
+
DSH は `engines.dsh` を強制しないため、tag と ref は選択メカニズムであり、互換性
|
|
39
|
+
ゲートそのものではありません。ラインと tag の公開レイアウトについては
|
|
39
40
|
[RELEASING.md](./RELEASING.md) を参照してください。
|
|
40
41
|
|
|
41
42
|
## 公式 CLI でインストール
|
|
@@ -72,6 +73,11 @@ profile の `cordis.yml` にプラグインを追加します:
|
|
|
72
73
|
defaultAction: ask # allow | ask | deny
|
|
73
74
|
```
|
|
74
75
|
|
|
76
|
+
2.6.0 から、ルール本体は `dsh-perm-gate-rules` settings 名前空間に保存され、変更の
|
|
77
|
+
たびにゲートがその場で再コンパイルされます。上記の YAML ファイルは任意になりました。
|
|
78
|
+
名前空間が未設定の間だけ読み込まれ、プラグインが最初にルールを書き込む際(許可リスト
|
|
79
|
+
の承認、または設定 UI での編集)にファイルのルールが自動的に取り込まれます。
|
|
80
|
+
|
|
75
81
|
まずは [examples/permissions.example.yaml](./examples/permissions.example.yaml)
|
|
76
82
|
から始め、profile を再読込してください。
|
|
77
83
|
|
|
@@ -89,13 +95,21 @@ dsh profile reload --profile web
|
|
|
89
95
|
|
|
90
96
|
## **DSH** アップグレード後:入力欄アイコンのパッチを再適用
|
|
91
97
|
|
|
92
|
-
|
|
98
|
+
「自动审查」と「自动审查(高权限)」の 2 つのティアにアイコンが付くのは、
|
|
93
99
|
`scripts/patch-permission-glyph.mjs` が **DSH ホストパッケージ内の閉じた Map** に
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
100
|
+
それらを追加したからにすぎません ——「自动审查」は `workspace-write` の盾+鉛筆を、
|
|
101
|
+
「自动审查(高权限)」は `danger-full-access` の盾+感嘆符をコピーしており、それぞれが
|
|
102
|
+
実際に共有するファイルサンドボックスのグリフになります。DSH はプラグイン提供ティアに
|
|
103
|
+
**設計上アイコンを与えません** —— その Map 自身のコメントが *"host-configured names
|
|
104
|
+
outside the design set get none"* と述べています。プラグインが影響できる option
|
|
105
|
+
オブジェクトは `{value, name, description}` しか運ばないため、代わりに使えるプラグイン側の
|
|
106
|
+
シームは存在しません。
|
|
107
|
+
|
|
108
|
+
スクリプトは取得元のキーを**解決**します(`["<key>",` を先に、次にホストが使う定数別名 ——
|
|
109
|
+
0.1.5 の `danger-full-access` は `[FULL_ACCESS,`)。DSH はこの設計セットを一度変更して
|
|
110
|
+
います —— 0.1.2 には `permissive` のグリフがあり、0.1.5 には `read-only` /
|
|
111
|
+
`workspace-write` / `danger-full-access` しかありません。解決できないキーは、Map が実際に
|
|
112
|
+
持つキーと一緒に報告されるため、ティアの付け替えは `GLYPH_TARGETS` の 1 行で済みます。
|
|
99
113
|
|
|
100
114
|
このパッチは**ホスト**のファイルを書き換えるので、**DSH のアップグレードや再インストールで
|
|
101
115
|
失われます**。*本プラグイン*のアップグレードでは失われません。アイコンは元々プラグインの
|
|
@@ -114,11 +128,15 @@ dsh profile reload --profile web
|
|
|
114
128
|
プラグインが許可なく自分の harness を書き換えてよい理由はありません。適用の有無にかかわらず
|
|
115
129
|
DSH の動作に影響はなく、ティア自体はそのまま機能します。
|
|
116
130
|
|
|
117
|
-
このスクリプトは冪等(2 回目は no-op)で、バックアップは 1
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
131
|
+
このスクリプトは冪等(2 回目は no-op)で、バックアップは 1 度だけ取り、2 つのグリフは
|
|
132
|
+
両方適用するか両方とも適用しないか(解決できない取得元キーは拒否であり、部分適用では
|
|
133
|
+
ありません)のいずれかで、壊れたスライスを書き込もうとしません —— 結果に対して
|
|
134
|
+
`node --check` を実行し、失敗すればバックアップを復元します —— したがって DSH
|
|
135
|
+
アップグレードのたびに無条件で再実行して安全です。呼び出し元の profile スコープを先に、
|
|
136
|
+
CLI インストールを次に探すため、nvm のバージョン変更やインストール symlink の貼り替えでも
|
|
137
|
+
壊れません。マシンに複数の DSH インストールがある場合、**パッチを当てなかった**方は出力に
|
|
138
|
+
列挙されます —— 目の前の UI を提供しているのはそのうち 1 つだけなので、必要ならそのパスを
|
|
139
|
+
引数で明示してください。
|
|
122
140
|
|
|
123
141
|
**プラグインを再インストールしてもアイコンは戻りません。**
|
|
124
142
|
`dsh plugin --profile web add …` は profile ディレクトリ内の pnpm に引数を転送し、
|
|
@@ -194,10 +212,12 @@ DSH の bundle patch は `permission.config.presets` を key 単位でマージ
|
|
|
194
212
|
します。profile を再読込して `cordis.patch.yml` を再適用し、後から読み込まれる
|
|
195
213
|
プラグインが `presets` を上書きしていないか確認してください。
|
|
196
214
|
|
|
197
|
-
|
|
215
|
+
**「自动审查」ティアの入力欄アイコンが消えた。**
|
|
198
216
|
DSH のアップグレードまたは再インストールが、パッチを当てたホスト bundle を置き換え
|
|
199
217
|
ました。プラグインを再インストールしても戻りません。
|
|
200
|
-
`npx dsh-perm-gate-patch-glyph`
|
|
218
|
+
`npx dsh-perm-gate-patch-glyph` を実行して再読込してください。取得元キーが無いと
|
|
219
|
+
報告された場合は DSH がグリフ表を再度変更しています —— エラーが現在のキー一覧を
|
|
220
|
+
示すので、`GLYPH_TARGETS` の該当ティアを付け替えてください。
|
|
201
221
|
ティア自体は影響を受けません —— パッチなしでもラベルとゲートは動作します。
|
|
202
222
|
|
|
203
223
|
**ルールファイルがあるのに `--list` が `ruleCount: 0` を返す。**
|