trustdex 0.6.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/README.ko.md +74 -6
  2. package/README.md +79 -7
  3. package/bin/trustdex.mjs +152 -9
  4. package/docs/ARCHITECTURE.ko.md +4 -2
  5. package/docs/ARCHITECTURE.md +9 -4
  6. package/docs/CLAUDE.ko.md +48 -2
  7. package/docs/CLAUDE.md +56 -5
  8. package/docs/CODEX.ko.md +53 -5
  9. package/docs/CODEX.md +63 -11
  10. package/docs/DEFENSIVE_USE_CASE.ko.md +2 -2
  11. package/docs/DEFENSIVE_USE_CASE.md +5 -3
  12. package/docs/EVALUATION.ko.md +29 -1
  13. package/docs/EVALUATION.md +28 -1
  14. package/docs/ROADMAP.ko.md +12 -4
  15. package/docs/ROADMAP.md +12 -4
  16. package/docs/SECURITY_RESEARCH.ko.md +2 -0
  17. package/docs/SECURITY_RESEARCH.md +2 -0
  18. package/docs/THREAT_MODEL.ko.md +5 -1
  19. package/docs/THREAT_MODEL.md +5 -1
  20. package/examples/adversarial/permissions/README.ko.md +15 -0
  21. package/examples/adversarial/permissions/README.md +15 -0
  22. package/examples/adversarial/permissions/claude-expanded.settings.json +30 -0
  23. package/examples/adversarial/permissions/claude-restrictive.settings.json +16 -0
  24. package/examples/adversarial/permissions/codex-expanded.toml +13 -0
  25. package/examples/adversarial/permissions/codex-restrictive.toml +13 -0
  26. package/examples/adversarial/permissions/expected.json +18 -0
  27. package/package.json +4 -3
  28. package/schemas/permission-posture-diff-v1.schema.json +78 -0
  29. package/schemas/permission-posture-diff-v2.schema.json +37 -0
  30. package/schemas/permission-posture-v1.schema.json +162 -0
  31. package/schemas/permission-posture-v2.schema.json +117 -0
  32. package/src/claude-settings.mjs +81 -0
  33. package/src/codex-settings.mjs +81 -3
  34. package/src/posture.mjs +601 -0
package/README.ko.md CHANGED
@@ -82,8 +82,8 @@ Node.js 20 이상이 필요합니다.
82
82
  npm에서 직접 실행:
83
83
 
84
84
  ```bash
85
- npx trustdex@0.6.0 --help
86
- npx trustdex@0.6.0 inspect ./mcp.json --pack strict --policy ./trustdex.policy.json
85
+ npx trustdex@0.8.0 --help
86
+ npx trustdex@0.8.0 inspect ./mcp.json --pack strict --policy ./trustdex.policy.json
87
87
  ```
88
88
 
89
89
  또는 소스 체크아웃에서 작업:
@@ -179,7 +179,17 @@ node ./bin/trustdex.mjs inspect-codex-settings \
179
179
  --pack strict
180
180
  ```
181
181
 
182
- 이 명령은 승인 정책, 샌드박스 종류, 네트워크 접근, 추가 쓰기 가능 루트 개수, 웹 검색 모드 및 명시적인 서브에이전트 활성화를 보고합니다. 쓰기 가능 경로나 관련 없는 설정의 값은 보관하지 않으며, 모든 설정 범위를 하나의 실제 런타임 정책으로 병합하지 않습니다.
182
+ 이 명령은 승인 정책, 샌드박스 종류, 네트워크 접근, 추가 쓰기 가능 루트 개수, 웹 검색 모드 및 명시적인 서브에이전트 활성화를 보고합니다. 쓰기 가능 경로나 관련 없는 설정의 값은 보관하지 않습니다. 문서화된 병합 우선순위를 검사하려면 활성 소스를 명시적으로 제공하세요. `--project`는 저장소 루트에서 현재 디렉터리 방향으로 반복합니다.
183
+
184
+ ```bash
185
+ node ./bin/trustdex.mjs inspect-codex-settings-stack \
186
+ --system ./review/system.toml \
187
+ --user ./review/user.toml \
188
+ --project ./review/root-project.toml \
189
+ --project ./review/closest-project.toml \
190
+ --cli ./review/cli-overrides.toml \
191
+ --pack strict
192
+ ```
183
193
 
184
194
  단계별 게이트 워크플로와 이 프로젝트가 자체 검토 과정에서 Codex를 사용하는 방법은 [Codex에서 TrustDex 사용하기](docs/CODEX.ko.md)를 참고하세요.
185
195
 
@@ -193,7 +203,7 @@ node ./bin/trustdex.mjs gate-claude ./.mcp.json --pack strict --out .trustdex/cl
193
203
  claude --strict-mcp-config --mcp-config .trustdex/claude-mcp.json
194
204
  ```
195
205
 
196
- Claude를 시작하기 전에 게이트의 결과를 확인하세요. 기본적으로 `ALLOW` 항목만 노출되며, `ASK` 항목에는 검토가 필요합니다. 어댑터는 검사할 수 없는 설정을 조용히 신뢰하지 않고 거부합니다. 이 명령은 npm 0.5.0 이상과 소스 체크아웃에서 사용할 수 있습니다. 게시된 CLI를 실행하려면 `node ./bin/trustdex.mjs` 대신 `npx trustdex@0.6.0`을 사용하세요.
206
+ Claude를 시작하기 전에 게이트의 결과를 확인하세요. 기본적으로 `ALLOW` 항목만 노출되며, `ASK` 항목에는 검토가 필요합니다. 어댑터는 검사할 수 없는 설정을 조용히 신뢰하지 않고 거부합니다. 이 명령은 npm 0.5.0 이상과 소스 체크아웃에서 사용할 수 있습니다. 게시된 CLI를 실행하려면 `node ./bin/trustdex.mjs` 대신 `npx trustdex@0.8.0`을 사용하세요.
197
207
 
198
208
  지원되는 설정, PowerShell 오류 처리, 서명된 검토 및 Claude Desktop 워크플로는 [Claude에서 TrustDex 사용하기](docs/CLAUDE.ko.md)를 참고하세요.
199
209
 
@@ -205,7 +215,65 @@ node ./bin/trustdex.mjs inspect-claude-settings \
205
215
  --pack strict
206
216
  ```
207
217
 
208
- 이 명령은 구조적인 기능 증거만 보고합니다. 권한 지정자, 디렉터리 값, 훅 명령, 도메인, 자격 증명 도우미 값 또는 환경 값을 출력하거나 지문에 포함하지 않습니다. 이는 정적 검토 도구이며 설정 병합기나 런타임 샌드박스가 아닙니다.
218
+ 이 명령은 구조적인 기능 증거만 보고합니다. 권한 지정자, 디렉터리 값, 훅 명령, 도메인, 자격 증명 도우미 값 또는 환경 값을 출력하거나 지문에 포함하지 않습니다. 문서화된 파일 우선순위와 목록 병합을 계산하려면 소스를 명시적으로 제공하세요.
219
+
220
+ ```bash
221
+ node ./bin/trustdex.mjs inspect-claude-settings-stack \
222
+ --user ./review/user.settings.json \
223
+ --project ./.claude/settings.json \
224
+ --local ./.claude/settings.local.json \
225
+ --session ./review/session.settings.json \
226
+ --managed ./review/managed-settings.json \
227
+ --pack strict
228
+ ```
229
+
230
+ 스택 검사는 여전히 정적 검토 도구이며 런타임 샌드박스가 아닙니다. 파일, 환경 변수 재정의 또는 조직 정책을 자동으로 찾지 않으며, 전달된 소스만 평가합니다.
231
+
232
+ ## 권한 상태 드리프트
233
+
234
+ 각 런타임에 대해 값이 제거된 구조적 기준 상태를 기록한 뒤 에이전트 또는 정책
235
+ 변경 후 상태와 비교할 수 있습니다.
236
+
237
+ ```bash
238
+ node ./bin/trustdex.mjs snapshot-claude-settings \
239
+ ./.claude/settings.json \
240
+ --pack strict \
241
+ --out .trustdex/claude-posture.json
242
+
243
+ node ./bin/trustdex.mjs snapshot-codex-settings \
244
+ ~/.codex/config.toml \
245
+ --pack strict \
246
+ --out .trustdex/codex-posture.json
247
+
248
+ node ./bin/trustdex.mjs snapshot-claude-settings-stack \
249
+ --user ./review/user.settings.json \
250
+ --project ./.claude/settings.json \
251
+ --managed ./review/managed-settings.json \
252
+ --pack strict \
253
+ --out .trustdex/claude-stack-posture.json
254
+
255
+ node ./bin/trustdex.mjs diff-permissions \
256
+ .trustdex/approved-posture.json \
257
+ .trustdex/current-posture.json \
258
+ --json
259
+ ```
260
+
261
+ 자동 허용, 쓰기 범위, 네트워크 접근, 서브에이전트, 정책 의미 또는 분류되지 않은
262
+ 설정이 확대되면 `diff-permissions`는 종료 코드 `1`을 반환합니다. 더 제한적으로만
263
+ 변한 경우에는 종료 코드 `0`과 `restricted` 상태를 반환합니다. 스냅샷에는 개수,
264
+ 문서화된 열거 값, 기능 범주, 위험 신호 및 정책 의미 다이제스트만 포함됩니다.
265
+ 입력 경로, 규칙 지정자, 쓰기 가능 경로, 도메인, 명령, 환경 변수 이름과 값 및
266
+ 관련 없는 설정 값은 제외합니다. 값이 제거된 규칙 내용이 같은 개수 안에서 바뀌면
267
+ 감지할 수 없으므로 이는 런타임 강제가 아니라 권고형 CI 검토 게이트입니다.
268
+
269
+ 기계 소비자는 `--json` 출력을
270
+ [`schemas/permission-posture-v1.schema.json`](schemas/permission-posture-v1.schema.json)과
271
+ [`schemas/permission-posture-diff-v1.schema.json`](schemas/permission-posture-diff-v1.schema.json)으로
272
+ 검증할 수 있습니다. 병합 소스 스냅샷과 차이는
273
+ [`schemas/permission-posture-v2.schema.json`](schemas/permission-posture-v2.schema.json)과
274
+ [`schemas/permission-posture-diff-v2.schema.json`](schemas/permission-posture-diff-v2.schema.json)을
275
+ 사용합니다. 스키마가 바뀌면 새로운 정수 `schemaVersion`을 사용하며 기존
276
+ 필드의 의미를 조용히 바꾸지 않습니다.
209
277
 
210
278
  ## 온라인 출처 관찰
211
279
 
@@ -352,7 +420,7 @@ node ./bin/trustdex.mjs verify .trustdex/trust-record.json \
352
420
  ```yaml
353
421
  - uses: actions/checkout@v4
354
422
 
355
- - uses: grigent/trustdex@v0.6.0
423
+ - uses: grigent/trustdex@v0.8.0
356
424
  with:
357
425
  config: ./mcp.json
358
426
  pack: official-first
package/README.md CHANGED
@@ -82,8 +82,8 @@ Requires Node.js 20+.
82
82
  Run directly from npm:
83
83
 
84
84
  ```bash
85
- npx trustdex@0.6.0 --help
86
- npx trustdex@0.6.0 inspect ./mcp.json --pack strict --policy ./trustdex.policy.json
85
+ npx trustdex@0.8.0 --help
86
+ npx trustdex@0.8.0 inspect ./mcp.json --pack strict --policy ./trustdex.policy.json
87
87
  ```
88
88
 
89
89
  Or work from a source checkout:
@@ -181,8 +181,19 @@ node ./bin/trustdex.mjs inspect-codex-settings \
181
181
 
182
182
  This reports approval policy, sandbox class, network access, additional writable-root
183
183
  count, web-search mode, and explicit subagent enablement. It does not retain writable
184
- paths or values from unrelated settings, and it does not merge every configuration
185
- scope into an effective runtime policy.
184
+ paths or values from unrelated settings. To inspect the documented merged precedence,
185
+ provide each active source explicitly; repeat `--project` from repository root toward
186
+ the current directory:
187
+
188
+ ```bash
189
+ node ./bin/trustdex.mjs inspect-codex-settings-stack \
190
+ --system ./review/system.toml \
191
+ --user ./review/user.toml \
192
+ --project ./review/root-project.toml \
193
+ --project ./review/closest-project.toml \
194
+ --cli ./review/cli-overrides.toml \
195
+ --pack strict
196
+ ```
186
197
 
187
198
  See [Using TrustDex with Codex](docs/CODEX.md) for a step-by-step gating workflow and for how this project uses Codex in its own review process.
188
199
 
@@ -201,7 +212,7 @@ claude --strict-mcp-config --mcp-config .trustdex/claude-mcp.json
201
212
  Check the gate's result before starting Claude. Only `ALLOW` entries are exposed by
202
213
  default; `ASK` entries require review. The adapter rejects settings it cannot
203
214
  inspect instead of silently trusting them. These commands are available in npm 0.5.0 and later, and from a source checkout.
204
- Use `npx trustdex@0.6.0` in place of `node ./bin/trustdex.mjs` to run the published CLI.
215
+ Use `npx trustdex@0.8.0` in place of `node ./bin/trustdex.mjs` to run the published CLI.
205
216
 
206
217
  See [Using TrustDex with Claude](docs/CLAUDE.md) for supported settings, PowerShell
207
218
  error handling, signed review, and the Claude Desktop workflow.
@@ -217,7 +228,68 @@ node ./bin/trustdex.mjs inspect-claude-settings \
217
228
 
218
229
  The command reports structural capability evidence only. It does not print or fingerprint
219
230
  permission specifiers, directory values, hook commands, domains, credential-helper values,
220
- or environment values. It is a static review aid, not a settings merger or runtime sandbox.
231
+ or environment values. To calculate the documented file precedence and list merging, pass
232
+ the sources explicitly:
233
+
234
+ ```bash
235
+ node ./bin/trustdex.mjs inspect-claude-settings-stack \
236
+ --user ./review/user.settings.json \
237
+ --project ./.claude/settings.json \
238
+ --local ./.claude/settings.local.json \
239
+ --session ./review/session.settings.json \
240
+ --managed ./review/managed-settings.json \
241
+ --pack strict
242
+ ```
243
+
244
+ The stack inspector remains a static review aid, not a runtime sandbox. It does not discover
245
+ files, environment overrides, or organization policy automatically; only supplied sources
246
+ are evaluated.
247
+
248
+ ## Permission posture drift
249
+
250
+ Record a value-free structural baseline for either runtime, then compare it after an
251
+ agent or policy change:
252
+
253
+ ```bash
254
+ node ./bin/trustdex.mjs snapshot-claude-settings \
255
+ ./.claude/settings.json \
256
+ --pack strict \
257
+ --out .trustdex/claude-posture.json
258
+
259
+ node ./bin/trustdex.mjs snapshot-codex-settings \
260
+ ~/.codex/config.toml \
261
+ --pack strict \
262
+ --out .trustdex/codex-posture.json
263
+
264
+ node ./bin/trustdex.mjs snapshot-claude-settings-stack \
265
+ --user ./review/user.settings.json \
266
+ --project ./.claude/settings.json \
267
+ --managed ./review/managed-settings.json \
268
+ --pack strict \
269
+ --out .trustdex/claude-stack-posture.json
270
+
271
+ node ./bin/trustdex.mjs diff-permissions \
272
+ .trustdex/approved-posture.json \
273
+ .trustdex/current-posture.json \
274
+ --json
275
+ ```
276
+
277
+ `diff-permissions` exits `1` when automatic grants, writable scope, network access,
278
+ subagents, policy semantics, or another unclassified setting expands. A change that
279
+ is only more restrictive exits `0` with status `restricted`. Snapshots contain counts,
280
+ documented enums, capability categories, risk signals, and a policy-semantics digest;
281
+ they omit input paths, rule specifiers, writable paths, domains, commands, environment
282
+ names and values, and unrelated setting values. Same-count changes to redacted rule
283
+ contents cannot be detected, so this is an advisory CI review gate rather than runtime
284
+ enforcement.
285
+
286
+ Machine consumers can validate `--json` output against
287
+ [`schemas/permission-posture-v1.schema.json`](schemas/permission-posture-v1.schema.json)
288
+ and [`schemas/permission-posture-diff-v1.schema.json`](schemas/permission-posture-diff-v1.schema.json).
289
+ Merged-source snapshots and their diffs use
290
+ [`schemas/permission-posture-v2.schema.json`](schemas/permission-posture-v2.schema.json)
291
+ and [`schemas/permission-posture-diff-v2.schema.json`](schemas/permission-posture-diff-v2.schema.json).
292
+ Schema changes require a new integer `schemaVersion`; fields are not silently repurposed.
221
293
 
222
294
  ## Online provenance observations
223
295
 
@@ -364,7 +436,7 @@ The private key should never be committed.
364
436
  ```yaml
365
437
  - uses: actions/checkout@v4
366
438
 
367
- - uses: grigent/trustdex@v0.6.0
439
+ - uses: grigent/trustdex@v0.8.0
368
440
  with:
369
441
  config: ./mcp.json
370
442
  pack: official-first
package/bin/trustdex.mjs CHANGED
@@ -6,9 +6,9 @@ import process from 'node:process';
6
6
 
7
7
  import { inspectMcpConfig } from '../src/inspect.mjs';
8
8
  import { parseCodexMcpToml, gateCodexToml } from '../src/codex-config.mjs';
9
- import { inspectCodexSettingsText } from '../src/codex-settings.mjs';
9
+ import { inspectCodexSettingsStack, inspectCodexSettingsText } from '../src/codex-settings.mjs';
10
10
  import { parseClaudeMcpConfig } from '../src/claude-config.mjs';
11
- import { inspectClaudeSettings } from '../src/claude-settings.mjs';
11
+ import { inspectClaudeSettings, inspectClaudeSettingsStack } from '../src/claude-settings.mjs';
12
12
  import { inspectSkillText, inspectPluginManifest } from '../src/extensions.mjs';
13
13
  import { evaluateAll } from '../src/policy.mjs';
14
14
  import { getPolicyPack, mergePolicy } from '../src/policy-packs.mjs';
@@ -23,6 +23,10 @@ import {
23
23
  verifyArtifactBytes
24
24
  } from '../src/provenance-online.mjs';
25
25
  import { createSnapshot, diffSnapshots } from '../src/snapshot.mjs';
26
+ import {
27
+ createPermissionPostureSnapshot,
28
+ diffPermissionPostureSnapshots
29
+ } from '../src/posture.mjs';
26
30
  import { gateMcpConfig } from '../src/gate.mjs';
27
31
  import { assessReview } from '../src/review.mjs';
28
32
  import {
@@ -46,6 +50,10 @@ Usage:
46
50
  trustdex inspect-codex <config.toml> [--pack ...] [--policy policy.json]
47
51
  [--trust-store trust-store.json] [--json]
48
52
  trustdex inspect-codex-settings <config.toml> [--pack ...] [--policy policy.json] [--json]
53
+ trustdex snapshot-codex-settings <config.toml> --out posture.json [--pack ...]
54
+ trustdex inspect-codex-settings-stack [--system file] [--managed file] [--user file]
55
+ [--profile file] [--project file ...] [--cli file] [--json]
56
+ trustdex snapshot-codex-settings-stack [same source flags] --out posture.json
49
57
 
50
58
  trustdex gate <mcp.json> --out gated.json [--pack ...] [--policy policy.json]
51
59
  [--trust-store trust-store.json] [--include-ask] [--json]
@@ -54,6 +62,10 @@ Usage:
54
62
 
55
63
  trustdex inspect-claude <.mcp.json> [--pack ...] [--policy policy.json] [--json]
56
64
  trustdex inspect-claude-settings <settings.json> [--pack ...] [--policy policy.json] [--json]
65
+ trustdex snapshot-claude-settings <settings.json> --out posture.json [--pack ...]
66
+ trustdex inspect-claude-settings-stack [--user file] [--project file] [--local file]
67
+ [--session file] [--managed file] [--json]
68
+ trustdex snapshot-claude-settings-stack [same source flags] --out posture.json
57
69
  trustdex gate-claude <.mcp.json> --out gated.json [--pack ...] [--policy policy.json]
58
70
  trustdex snapshot-claude <.mcp.json> --out snapshot.json [--pack ...]
59
71
  trustdex approve-claude <.mcp.json> --private key.pem --dir review [--pack ...]
@@ -79,6 +91,7 @@ Usage:
79
91
  [--policy policy.json] [--trust-store trust-store.json]
80
92
 
81
93
  trustdex diff <before.json> <after.json> [--json]
94
+ trustdex diff-permissions <before.json> <after.json> [--json]
82
95
  trustdex policy <strict|official-first|development>
83
96
 
84
97
  trustdex keygen --private trustdex-private.pem --public trustdex-public.pem
@@ -115,6 +128,17 @@ function getFlag(args, name) {
115
128
  return index >= 0 ? args[index + 1] : null;
116
129
  }
117
130
 
131
+ function getFlags(args, name) {
132
+ const values = [];
133
+ for (let index = 0; index < args.length; index += 1) {
134
+ if (args[index] !== name) continue;
135
+ const value = args[index + 1];
136
+ if (!value || value.startsWith('--')) throw new Error(`${name} requires a file path.`);
137
+ values.push(value);
138
+ }
139
+ return values;
140
+ }
141
+
118
142
  function hasFlag(args, name) {
119
143
  return args.includes(name);
120
144
  }
@@ -199,6 +223,56 @@ async function inspectSingle(item, args) {
199
223
  setInspectExitCode(results);
200
224
  }
201
225
 
226
+ async function evaluateClaudeSettings(settingsPath, args) {
227
+ let settings;
228
+ try { settings = await readJson(settingsPath); } catch (error) {
229
+ if (error instanceof SyntaxError) throw new Error('Invalid Claude settings JSON.');
230
+ throw error;
231
+ }
232
+ const policy = await loadPolicy(args);
233
+ const [result] = evaluateAll([inspectClaudeSettings(settings, settingsPath)], policy);
234
+ return { policy, result };
235
+ }
236
+
237
+ async function evaluateCodexSettings(configPath, args) {
238
+ const policy = await loadPolicy(args);
239
+ const [result] = evaluateAll([
240
+ inspectCodexSettingsText(await fs.readFile(configPath, 'utf8'), configPath)
241
+ ], policy);
242
+ return { policy, result };
243
+ }
244
+
245
+ async function evaluateClaudeSettingsStack(args) {
246
+ const layers = [];
247
+ for (const scope of ['user', 'project', 'local', 'session', 'managed']) {
248
+ for (const file of getFlags(args, `--${scope}`)) {
249
+ let settings;
250
+ try { settings = await readJson(file); } catch (error) {
251
+ if (error instanceof SyntaxError) throw new Error(`Invalid Claude ${scope} settings JSON.`);
252
+ throw error;
253
+ }
254
+ layers.push({ scope, settings });
255
+ }
256
+ }
257
+ if (!layers.length) throw new Error('Claude settings stack requires at least one source flag.');
258
+ const policy = await loadPolicy(args);
259
+ const [result] = evaluateAll([inspectClaudeSettingsStack(layers)], policy);
260
+ return { policy, result };
261
+ }
262
+
263
+ async function evaluateCodexSettingsStack(args) {
264
+ const layers = [];
265
+ for (const scope of ['system', 'managed', 'user', 'profile', 'project', 'cli']) {
266
+ for (const file of getFlags(args, `--${scope}`)) {
267
+ layers.push({ scope, text: await fs.readFile(file, 'utf8') });
268
+ }
269
+ }
270
+ if (!layers.length) throw new Error('Codex settings stack requires at least one source flag.');
271
+ const policy = await loadPolicy(args);
272
+ const [result] = evaluateAll([inspectCodexSettingsStack(layers)], policy);
273
+ return { policy, result };
274
+ }
275
+
202
276
  function printObservation(observation, asJson) {
203
277
  if (asJson) {
204
278
  console.log(JSON.stringify(observation, null, 2));
@@ -224,6 +298,24 @@ function printChanges(changes) {
224
298
  }
225
299
  }
226
300
 
301
+ function printPermissionPostureDiff(diff) {
302
+ if (diff.status === 'unchanged') {
303
+ console.log('UNCHANGED: permission posture matches the baseline.');
304
+ return;
305
+ }
306
+ if (diff.status === 'restricted') {
307
+ console.log('RESTRICTED: permission posture became more restrictive.');
308
+ } else {
309
+ console.log('NEEDS_REVIEW: permission posture expanded or changed in an unclassified way.');
310
+ }
311
+ for (const change of diff.changes) {
312
+ const details = change.items
313
+ ? ` (${change.items.join(', ')})`
314
+ : Object.hasOwn(change, 'before') ? ` (${String(change.before)} -> ${String(change.after)})` : '';
315
+ console.log(`${change.impact.toUpperCase()} ${change.type}: ${change.field}${details}`);
316
+ }
317
+ }
318
+
227
319
  async function main() {
228
320
  const args = process.argv.slice(2);
229
321
  const command = args[0];
@@ -270,12 +362,16 @@ async function main() {
270
362
  if (command === 'inspect-claude-settings') {
271
363
  const settingsPath = args[1];
272
364
  if (!settingsPath) throw new Error('inspect-claude-settings requires a path to Claude settings JSON.');
273
- let settings;
274
- try { settings = await readJson(settingsPath); } catch (error) {
275
- if (error instanceof SyntaxError) throw new Error('Invalid Claude settings JSON.');
276
- throw error;
277
- }
278
- await inspectSingle(inspectClaudeSettings(settings, settingsPath), args);
365
+ const { result } = await evaluateClaudeSettings(settingsPath, args);
366
+ printResults([result], hasFlag(args, '--json'));
367
+ setInspectExitCode([result]);
368
+ return;
369
+ }
370
+
371
+ if (command === 'inspect-claude-settings-stack') {
372
+ const { result } = await evaluateClaudeSettingsStack(args);
373
+ printResults([result], hasFlag(args, '--json'));
374
+ setInspectExitCode([result]);
279
375
  return;
280
376
  }
281
377
 
@@ -291,7 +387,54 @@ async function main() {
291
387
  if (command === 'inspect-codex-settings') {
292
388
  const configPath = args[1];
293
389
  if (!configPath) throw new Error('inspect-codex-settings requires a path to Codex config.toml.');
294
- await inspectSingle(inspectCodexSettingsText(await fs.readFile(configPath, 'utf8'), configPath), args);
390
+ const { result } = await evaluateCodexSettings(configPath, args);
391
+ printResults([result], hasFlag(args, '--json'));
392
+ setInspectExitCode([result]);
393
+ return;
394
+ }
395
+
396
+ if (command === 'inspect-codex-settings-stack') {
397
+ const { result } = await evaluateCodexSettingsStack(args);
398
+ printResults([result], hasFlag(args, '--json'));
399
+ setInspectExitCode([result]);
400
+ return;
401
+ }
402
+
403
+ if (['snapshot-claude-settings', 'snapshot-codex-settings',
404
+ 'snapshot-claude-settings-stack', 'snapshot-codex-settings-stack'].includes(command)) {
405
+ const isStack = command.endsWith('-stack');
406
+ const configPath = isStack ? null : args[1];
407
+ const out = getFlag(args, '--out');
408
+ if ((!isStack && !configPath) || !out) {
409
+ throw new Error(isStack
410
+ ? `${command} requires source flags and --out <posture.json>.`
411
+ : `${command} requires a settings path and --out <posture.json>.`);
412
+ }
413
+ let evaluated;
414
+ if (command === 'snapshot-claude-settings') evaluated = await evaluateClaudeSettings(configPath, args);
415
+ else if (command === 'snapshot-codex-settings') evaluated = await evaluateCodexSettings(configPath, args);
416
+ else if (command === 'snapshot-claude-settings-stack') evaluated = await evaluateClaudeSettingsStack(args);
417
+ else evaluated = await evaluateCodexSettingsStack(args);
418
+ const { policy, result } = evaluated;
419
+ const snapshot = createPermissionPostureSnapshot(result, policy);
420
+ await writeFileSafe(out, `${JSON.stringify(snapshot, null, 2)}\n`);
421
+ console.log(`Wrote ${out}\nDigest: ${snapshot.digest}`);
422
+ return;
423
+ }
424
+
425
+ if (command === 'diff-permissions') {
426
+ const beforePath = args[1];
427
+ const afterPath = args[2];
428
+ if (!beforePath || !afterPath) {
429
+ throw new Error('diff-permissions requires <before.json> <after.json>.');
430
+ }
431
+ const diff = diffPermissionPostureSnapshots(
432
+ await readJson(beforePath),
433
+ await readJson(afterPath)
434
+ );
435
+ if (hasFlag(args, '--json')) console.log(JSON.stringify(diff, null, 2));
436
+ else printPermissionPostureDiff(diff);
437
+ if (diff.status === 'needs-review') process.exitCode = 1;
295
438
  return;
296
439
  }
297
440
 
@@ -13,7 +13,7 @@ TrustDex는 관찰, 정책, 게이트 및 검토 증거를 분리하는 의존
13
13
  3. 선택적인 신뢰 저장소 주장이 명시적인 출처 증거를 연결합니다.
14
14
  4. 정책 엔진이 ALLOW, ASK 또는 BLOCK을 생성합니다.
15
15
  5. 게이트가 승인된 설정만 출력합니다.
16
- 6. 스냅샷과 서명된 신뢰 기록을 통해 이후 드리프트를 검토할 수 있습니다.
16
+ 6. MCP 스냅샷, 권한 상태 스냅샷 및 서명된 신뢰 기록을 통해 이후 드리프트를 검토할 수 있습니다.
17
17
 
18
18
  게이트가 강제 적용 경계입니다. 보고서만으로는 에이전트가 검토되지 않은 확장을 불러오는 것을 막을 수 없습니다.
19
19
 
@@ -31,6 +31,7 @@ TrustDex는 관찰, 정책, 게이트 및 검토 증거를 분리하는 의존
31
31
  | `src/policy.mjs` | 단조로운 정책 평가. 신호는 심각도를 높일 수 있음 |
32
32
  | `src/gate.mjs` | 승인된 MCP 항목만 출력 |
33
33
  | `src/snapshot.mjs` | 결정론적 신뢰 관련 상태 및 드리프트 |
34
+ | `src/posture.mjs` | 값이 제거된 Claude/Codex 권한 상태 스냅샷 및 자율성 확대 분류 |
34
35
  | `src/trust-record.mjs` | Ed25519 검토 기록 서명 및 검증 |
35
36
  | `src/review.mjs` | 서명된 기준선 비교 |
36
37
 
@@ -39,13 +40,14 @@ TrustDex는 관찰, 정책, 게이트 및 검토 증거를 분리하는 의존
39
40
  - 검사는 검사 대상 확장을 실행해서는 안 됩니다.
40
41
  - 비밀 값은 출력하거나 스냅샷에 저장해서는 안 됩니다.
41
42
  - 권한 규칙 값, 쓰기 가능 루트 경로, 훅/도우미 본문 및 관련 없는 설정 값은 에이전트 설정 보고서에 보관해서는 안 됩니다.
43
+ - 권한 상태 스냅샷은 소스 경로와 설정 값을 제외해야 하며, 새 자율성 또는 정책 의미 변경은 검토를 요구해야 합니다.
42
44
  - 알 수 없는 증거가 신뢰로 바뀌어서는 안 됩니다.
43
45
  - 차단 신호의 심각도를 출처 정보로 낮춰서는 안 됩니다.
44
46
  - 지원하지 않는 보안 관련 구문은 안전하게 실패해야 합니다.
45
47
  - 네트워크 조회는 명시적으로 수행하며, 관찰은 승인이 아닙니다.
46
48
  - ALLOW는 정책 결과이지 안전 인증이 아닙니다.
47
49
 
48
- 설정 검사는 현재 권고 기능입니다. 한 번에 설정 소스 파일 하나를 읽으며 병합된 실제 런타임 정책을 계산하지 않습니다. MCP 게이트는 구현된 강제 적용 경계로 유지되고, 에이전트 작업은 운영체제 및 런타임 샌드박스가 강제합니다.
50
+ 설정 검사는 권고 기능입니다. 소스 파일 하나 또는 명시적으로 전달된 Claude/Codex 소스 스택을 읽고 지원되는 보안 필드에 대해 문서화된 우선순위를 계산할 수 있습니다. 활성 파일, 프로필, 환경 변수 재정의, 요구사항 또는 서버 전달 정책을 자동으로 찾지 않으며 보고된 정책이 실제로 활성화되었다는 것도 증명하지 않습니다. MCP 게이트는 구현된 강제 적용 경계로 유지되고, 에이전트 작업은 운영체제 및 런타임 샌드박스가 강제합니다.
49
51
 
50
52
  ## 의존성 정책
51
53
 
@@ -13,7 +13,7 @@ TrustDex is a dependency-free Node.js CLI that separates observation, policy, ga
13
13
  3. Optional trust-store claims attach explicit provenance evidence.
14
14
  4. The policy engine produces ALLOW, ASK, or BLOCK.
15
15
  5. A gate emits only the approved configuration.
16
- 6. Snapshots and signed trust records make later drift reviewable.
16
+ 6. MCP snapshots, permission posture snapshots, and signed trust records make later drift reviewable.
17
17
 
18
18
  The gate is the enforcement boundary. Reports alone do not prevent an agent from loading an unreviewed extension.
19
19
 
@@ -31,6 +31,7 @@ The gate is the enforcement boundary. Reports alone do not prevent an agent from
31
31
  | src/policy.mjs | Monotonic policy evaluation; signals may raise severity |
32
32
  | src/gate.mjs | Output only approved MCP entries |
33
33
  | src/snapshot.mjs | Deterministic trust-relevant state and drift |
34
+ | src/posture.mjs | Value-free Claude/Codex permission posture snapshots and autonomy-expansion classification |
34
35
  | src/trust-record.mjs | Ed25519 review-record signing and verification |
35
36
  | src/review.mjs | Signed-baseline comparison |
36
37
 
@@ -39,15 +40,19 @@ The gate is the enforcement boundary. Reports alone do not prevent an agent from
39
40
  - Inspection must not execute the inspected extension.
40
41
  - Secret values must not be printed or stored in snapshots.
41
42
  - Permission rule values, writable-root paths, hook/helper bodies, and unrelated settings values must not be retained in agent-settings reports.
43
+ - Permission posture snapshots must omit source paths and setting values; new autonomy or changed policy semantics must require review.
42
44
  - Unknown evidence must not become trust.
43
45
  - A blocking signal must not be lowered by provenance.
44
46
  - Unsupported security-relevant syntax must fail closed.
45
47
  - Network lookup is explicit and observation is not approval.
46
48
  - ALLOW is a policy result, not a safety certification.
47
49
 
48
- Settings inspection is currently advisory. It reads one source file at a time and does
49
- not calculate the merged effective runtime policy. MCP gates remain the implemented
50
- enforcement boundary; operating-system and runtime sandboxes enforce agent actions.
50
+ Settings inspection is advisory. It can read one source file or an explicitly supplied
51
+ Claude/Codex source stack and calculate the documented precedence for the supported
52
+ security fields. It does not discover active files, profiles, environment overrides,
53
+ requirements, or server-delivered policy, and it does not prove the reported policy is
54
+ active. MCP gates remain the implemented enforcement boundary; operating-system and
55
+ runtime sandboxes enforce agent actions.
51
56
 
52
57
  ## Dependency policy
53
58
 
package/docs/CLAUDE.ko.md CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  TrustDex는 Claude Code 또는 Claude Desktop이 MCP JSON을 받기 전에 이를 검사할 수 있습니다. 설정 증거를 로컬에서 평가하고, MCP 서버를 시작하지 않으며, 설정을 업로드하지 않습니다. ALLOW 결정은 증거가 정책과 일치한다는 뜻이며 서버 코드가 안전하다는 것을 증명하지 않습니다.
8
8
 
9
- Claude 전용 명령은 TrustDex 0.5.0 이상에서 사용할 수 있습니다. 게시된 CLI를 실행하려면 아래 예제의 `node ./bin/trustdex.mjs` 대신 `npx trustdex@0.6.0`을 사용하거나, 소스 체크아웃에서 명령을 직접 사용하세요.
9
+ Claude 전용 명령은 TrustDex 0.5.0 이상에서 사용할 수 있습니다. 게시된 CLI를 실행하려면 아래 예제의 `node ./bin/trustdex.mjs` 대신 `npx trustdex@0.8.0`을 사용하거나, 소스 체크아웃에서 명령을 직접 사용하세요.
10
10
 
11
11
  ## 지원되는 설정
12
12
 
@@ -89,7 +89,53 @@ node ./bin/trustdex.mjs inspect-claude-settings \
89
89
 
90
90
  `ask`와 `deny` 규칙은 제한적이므로 그 자체로 위험 신호를 추가하지 않습니다. 보고서에는 규칙 개수와 기능 범주가 포함되지만 명령 패턴, 경로, 도메인, 훅 본문, 도우미 명령, 디렉터리 값, 환경 값 또는 플러그인 식별자는 포함되지 않습니다. 같은 이유로 이 첫 번째 어댑터는 설정 파일용 콘텐츠 지문이나 서명된 검토 기록을 만들지 않습니다.
91
91
 
92
- 이 명령은 한 번에 파일 하나만 검사합니다. Claude Code는 여러 범위의 설정을 병합하고 명령줄 플래그가 세션 권한을 추가할 수 있으므로 이 결과가 실제 런타임 정책은 아닙니다. Claude Code에서 로드된 소스와 권한을 확인하고 강제 적용에는 운영체제 수준 샌드박싱을 사용하세요. 공식 참고 자료: [설정 파일 및 우선순위](https://code.claude.com/docs/en/settings), [권한](https://code.claude.com/docs/en/permissions), [보안](https://code.claude.com/docs/en/security).
92
+ 여러 활성 소스를 검토할 때는 스택 명령을 사용하세요.
93
+
94
+ ```bash
95
+ node ./bin/trustdex.mjs inspect-claude-settings-stack \
96
+ --user ./review/user.settings.json \
97
+ --project ./.claude/settings.json \
98
+ --local ./.claude/settings.local.json \
99
+ --session ./review/session.settings.json \
100
+ --managed ./review/managed-settings.json \
101
+ --pack strict --json
102
+ ```
103
+
104
+ TrustDex는 사용자, 공유 프로젝트, 프로젝트 로컬, 세션 및 관리형 우선순위를 적용합니다. 권한 목록은 결합하고 지원되는 스칼라 권한 제어는 가장 높은 유효 소스를 사용합니다. 문서화된 범위 제한에 맞춰 프로젝트 또는 로컬 소스의 `auto`와 `bypassPermissions` 기본 모드는 무시합니다. 보고서에는 고정된 소스 범위 이름과 개수만 저장하며 입력 경로나 설정 값은 저장하지 않습니다.
105
+
106
+ 소스는 명시적으로 전달해야 합니다. TrustDex는 설정, 셸 환경 변수 재정의, 임베딩 호스트 정책 또는 서버 전달 관리형 설정을 자동으로 찾지 않습니다. `/status`로 로드된 소스를 확인하고 강제 적용에는 운영체제 수준 샌드박싱을 사용하세요. 공식 참고 자료: [설정 파일 및 우선순위](https://code.claude.com/docs/en/settings), [권한](https://code.claude.com/docs/en/permissions), [보안](https://code.claude.com/docs/en/security).
107
+
108
+ ### 권한 상태 드리프트
109
+
110
+ 검토 후 기준 상태를 만들고 나중의 설정 상태와 비교하세요.
111
+
112
+ ```bash
113
+ node ./bin/trustdex.mjs snapshot-claude-settings \
114
+ ./.claude/settings.json \
115
+ --pack strict \
116
+ --out .trustdex/claude-posture.json
117
+
118
+ node ./bin/trustdex.mjs snapshot-claude-settings-stack \
119
+ --user ./review/user.settings.json \
120
+ --project ./.claude/settings.json \
121
+ --managed ./review/managed-settings.json \
122
+ --pack strict \
123
+ --out .trustdex/claude-stack-posture.json
124
+
125
+ node ./bin/trustdex.mjs diff-permissions \
126
+ .trustdex/approved-claude-posture.json \
127
+ .trustdex/current-claude-posture.json
128
+ ```
129
+
130
+ 새 자동 도구 범주, 추가 규칙이나 디렉터리, 완화된 보호 스위치, 더 위험한 기본
131
+ 모드, 새 위험 신호 또는 정책 의미 변경은 `NEEDS_REVIEW`와 종료 코드 `1`을
132
+ 반환합니다. 제한적으로만 변한 경우 `RESTRICTED`와 종료 코드 `0`을 반환합니다.
133
+ 스냅샷은 소스 경로와 모든 권한, 훅, 디렉터리, 도메인, 자격 증명 도우미,
134
+ 플러그인 및 환경 값을 제외합니다. 제거된 규칙 내용을 의도적으로 지문화하지
135
+ 않으므로 같은 기능 범주 안에서 규칙 개수는 같고 내용만 바뀌는 경우에는 소스
136
+ 검토나 런타임 제어가 계속 필요합니다.
137
+ 병합 소스 스냅샷은 스키마 v2를 사용하고 소스 범위 개수만 포함합니다. 최종 구조적
138
+ 권한 상태가 같더라도 소스 추가, 제거 또는 범위 변경에는 재검토가 필요합니다.
93
139
 
94
140
  ## 서명된 검토 및 드리프트
95
141