@ai-dossier/cli 0.3.0 → 0.4.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.
Files changed (119) hide show
  1. package/README.md +56 -25
  2. package/bin/dossier-verify +6 -432
  3. package/dist/cli.js +82 -21
  4. package/dist/cli.js.map +1 -1
  5. package/dist/commands/cache.d.ts.map +1 -1
  6. package/dist/commands/cache.js +5 -1
  7. package/dist/commands/cache.js.map +1 -1
  8. package/dist/commands/checksum.d.ts.map +1 -1
  9. package/dist/commands/checksum.js +6 -8
  10. package/dist/commands/checksum.js.map +1 -1
  11. package/dist/commands/commands.d.ts +3 -0
  12. package/dist/commands/commands.d.ts.map +1 -0
  13. package/dist/commands/commands.js +73 -0
  14. package/dist/commands/commands.js.map +1 -0
  15. package/dist/commands/config-cmd.d.ts.map +1 -1
  16. package/dist/commands/config-cmd.js +170 -7
  17. package/dist/commands/config-cmd.js.map +1 -1
  18. package/dist/commands/create.d.ts.map +1 -1
  19. package/dist/commands/create.js +25 -10
  20. package/dist/commands/create.js.map +1 -1
  21. package/dist/commands/export.d.ts.map +1 -1
  22. package/dist/commands/export.js +16 -6
  23. package/dist/commands/export.js.map +1 -1
  24. package/dist/commands/format.d.ts.map +1 -1
  25. package/dist/commands/format.js.map +1 -1
  26. package/dist/commands/from-file.d.ts.map +1 -1
  27. package/dist/commands/from-file.js +4 -3
  28. package/dist/commands/from-file.js.map +1 -1
  29. package/dist/commands/get.d.ts.map +1 -1
  30. package/dist/commands/get.js +19 -6
  31. package/dist/commands/get.js.map +1 -1
  32. package/dist/commands/info.d.ts.map +1 -1
  33. package/dist/commands/info.js +28 -58
  34. package/dist/commands/info.js.map +1 -1
  35. package/dist/commands/install-skill.d.ts.map +1 -1
  36. package/dist/commands/install-skill.js +98 -29
  37. package/dist/commands/install-skill.js.map +1 -1
  38. package/dist/commands/keys.js +1 -1
  39. package/dist/commands/keys.js.map +1 -1
  40. package/dist/commands/lint.d.ts.map +1 -1
  41. package/dist/commands/lint.js.map +1 -1
  42. package/dist/commands/list.d.ts.map +1 -1
  43. package/dist/commands/list.js +58 -41
  44. package/dist/commands/list.js.map +1 -1
  45. package/dist/commands/login.d.ts.map +1 -1
  46. package/dist/commands/login.js +32 -6
  47. package/dist/commands/login.js.map +1 -1
  48. package/dist/commands/logout.d.ts.map +1 -1
  49. package/dist/commands/logout.js +22 -5
  50. package/dist/commands/logout.js.map +1 -1
  51. package/dist/commands/prompt-hook.d.ts.map +1 -1
  52. package/dist/commands/prompt-hook.js +4 -13
  53. package/dist/commands/prompt-hook.js.map +1 -1
  54. package/dist/commands/publish.d.ts.map +1 -1
  55. package/dist/commands/publish.js +131 -51
  56. package/dist/commands/publish.js.map +1 -1
  57. package/dist/commands/pull.d.ts.map +1 -1
  58. package/dist/commands/pull.js +23 -8
  59. package/dist/commands/pull.js.map +1 -1
  60. package/dist/commands/remove.d.ts.map +1 -1
  61. package/dist/commands/remove.js +61 -11
  62. package/dist/commands/remove.js.map +1 -1
  63. package/dist/commands/reset-hooks.js +1 -1
  64. package/dist/commands/reset-hooks.js.map +1 -1
  65. package/dist/commands/run.d.ts.map +1 -1
  66. package/dist/commands/run.js +88 -73
  67. package/dist/commands/run.js.map +1 -1
  68. package/dist/commands/search.d.ts.map +1 -1
  69. package/dist/commands/search.js +30 -28
  70. package/dist/commands/search.js.map +1 -1
  71. package/dist/commands/sign.d.ts.map +1 -1
  72. package/dist/commands/sign.js +71 -45
  73. package/dist/commands/sign.js.map +1 -1
  74. package/dist/commands/skill-export.d.ts +3 -0
  75. package/dist/commands/skill-export.d.ts.map +1 -0
  76. package/dist/commands/skill-export.js +218 -0
  77. package/dist/commands/skill-export.js.map +1 -0
  78. package/dist/commands/validate.d.ts.map +1 -1
  79. package/dist/commands/validate.js +12 -33
  80. package/dist/commands/validate.js.map +1 -1
  81. package/dist/commands/verify.d.ts.map +1 -1
  82. package/dist/commands/verify.js +6 -8
  83. package/dist/commands/verify.js.map +1 -1
  84. package/dist/commands/whoami.d.ts.map +1 -1
  85. package/dist/commands/whoami.js +115 -12
  86. package/dist/commands/whoami.js.map +1 -1
  87. package/dist/config.d.ts +38 -1
  88. package/dist/config.d.ts.map +1 -1
  89. package/dist/config.js +128 -1
  90. package/dist/config.js.map +1 -1
  91. package/dist/credentials.d.ts +14 -8
  92. package/dist/credentials.d.ts.map +1 -1
  93. package/dist/credentials.js +119 -39
  94. package/dist/credentials.js.map +1 -1
  95. package/dist/help.d.ts +7 -0
  96. package/dist/help.d.ts.map +1 -0
  97. package/dist/help.js +86 -0
  98. package/dist/help.js.map +1 -0
  99. package/dist/helpers.d.ts +4 -18
  100. package/dist/helpers.d.ts.map +1 -1
  101. package/dist/helpers.js +35 -147
  102. package/dist/helpers.js.map +1 -1
  103. package/dist/multi-registry.d.ts +71 -0
  104. package/dist/multi-registry.d.ts.map +1 -0
  105. package/dist/multi-registry.js +136 -0
  106. package/dist/multi-registry.js.map +1 -0
  107. package/dist/oauth.d.ts +1 -0
  108. package/dist/oauth.d.ts.map +1 -1
  109. package/dist/oauth.js +1 -0
  110. package/dist/oauth.js.map +1 -1
  111. package/dist/registry-client.d.ts +64 -8
  112. package/dist/registry-client.d.ts.map +1 -1
  113. package/dist/registry-client.js +13 -0
  114. package/dist/registry-client.js.map +1 -1
  115. package/dist/verify-dossier.d.ts +28 -0
  116. package/dist/verify-dossier.d.ts.map +1 -0
  117. package/dist/verify-dossier.js +329 -0
  118. package/dist/verify-dossier.js.map +1 -0
  119. package/package.json +3 -3
package/README.md CHANGED
@@ -36,22 +36,48 @@ cd cli
36
36
  npm link # Links the CLI globally for development
37
37
 
38
38
  # Or use directly
39
- chmod +x bin/dossier-verify
40
- ./bin/dossier-verify <file-or-url>
39
+ chmod +x bin/ai-dossier
40
+ ./bin/ai-dossier verify <file-or-url>
41
41
  ```
42
42
 
43
43
  ---
44
44
 
45
+ ## Authentication
46
+
47
+ ### Interactive (Browser OAuth)
48
+
49
+ ```bash
50
+ dossier login
51
+ ```
52
+
53
+ ### Non-Interactive (CI/CD, Agents)
54
+
55
+ Set the `DOSSIER_REGISTRY_TOKEN` environment variable:
56
+
57
+ ```bash
58
+ export DOSSIER_REGISTRY_TOKEN=<your-token>
59
+
60
+ # Optional: set user/org context
61
+ export DOSSIER_REGISTRY_USER=<username>
62
+ export DOSSIER_REGISTRY_ORGS=org1,org2
63
+ ```
64
+
65
+ When `DOSSIER_REGISTRY_TOKEN` is set, it takes precedence over stored credentials. This is recommended for CI/CD pipelines, Docker containers, and AI agent contexts where interactive login is not possible.
66
+
67
+ Commands that require confirmation (`publish`, `remove`, `cache clean`) will fail with a clear error in non-interactive sessions. Use `-y`/`--yes` to skip confirmation prompts.
68
+
69
+ ---
70
+
45
71
  ## Usage
46
72
 
47
73
  ### Basic Verification
48
74
 
49
75
  ```bash
50
76
  # Verify local file
51
- dossier-verify path/to/dossier.ds.md
77
+ ai-dossier verify path/to/dossier.ds.md
52
78
 
53
79
  # Verify remote dossier
54
- dossier-verify https://example.com/dossier.ds.md
80
+ ai-dossier verify https://example.com/dossier.ds.md
55
81
  ```
56
82
 
57
83
  **Exit codes**:
@@ -62,7 +88,7 @@ dossier-verify https://example.com/dossier.ds.md
62
88
  ### Verbose Mode
63
89
 
64
90
  ```bash
65
- dossier-verify --verbose path/to/dossier.ds.md
91
+ ai-dossier verify --verbose path/to/dossier.ds.md
66
92
  ```
67
93
 
68
94
  Shows:
@@ -77,7 +103,7 @@ Shows:
77
103
  ```bash
78
104
  # Shell function wrapper
79
105
  claude-run-dossier() {
80
- if dossier-verify "$1"; then
106
+ if ai-dossier verify "$1"; then
81
107
  claude-code "The dossier at $1 has been verified. Please execute it."
82
108
  else
83
109
  echo "❌ Security verification failed. Not executing."
@@ -91,7 +117,7 @@ claude-run-dossier https://example.com/dossier.ds.md
91
117
  **Cursor**:
92
118
  ```bash
93
119
  cursor-run-dossier() {
94
- if dossier-verify "$1"; then
120
+ if ai-dossier verify "$1"; then
95
121
  cursor "Execute the verified dossier at $1"
96
122
  else
97
123
  echo "❌ Verification failed"
@@ -106,7 +132,7 @@ safe-run-dossier() {
106
132
  local url="$1"
107
133
  local tool="${2:-claude-code}"
108
134
 
109
- if dossier-verify "$url"; then
135
+ if ai-dossier verify "$url"; then
110
136
  echo "✅ Dossier verified. Passing to $tool..."
111
137
  "$tool" "run $url"
112
138
  else
@@ -173,7 +199,7 @@ safe-run-dossier https://example.com/dossier.ds.md cursor
173
199
  ### Example 1: Legitimate Dossier (Passes)
174
200
 
175
201
  ```bash
176
- $ dossier-verify examples/data-science/train-ml-model.ds.md
202
+ $ ai-dossier verify examples/data-science/train-ml-model.ds.md
177
203
 
178
204
  🔐 Dossier Verification Tool
179
205
 
@@ -202,7 +228,7 @@ $ echo $?
202
228
  ### Example 2: Malicious Dossier (Blocked)
203
229
 
204
230
  ```bash
205
- $ dossier-verify https://raw.githubusercontent.com/imboard-ai/ai-dossier/main/examples/security/validate-project-config.ds.md
231
+ $ ai-dossier verify https://raw.githubusercontent.com/imboard-ai/ai-dossier/main/examples/security/validate-project-config.ds.md
206
232
 
207
233
  🔐 Dossier Verification Tool
208
234
 
@@ -242,7 +268,7 @@ $ echo $?
242
268
  # Wrapper function for Claude Code
243
269
  claude-run-dossier() {
244
270
  echo "Verifying dossier security..."
245
- if ~/projects/dossier/cli/bin/dossier-verify "$1"; then
271
+ if ai-dossier verify "$1"; then
246
272
  echo ""
247
273
  echo "✅ Verification passed. Executing with Claude Code..."
248
274
  claude-code "Execute the verified dossier at $1"
@@ -266,7 +292,7 @@ claude-run-dossier https://example.com/dossier.ds.md
266
292
 
267
293
  ```
268
294
  User Command:
269
- dossier-verify https://example.com/dossier.ds.md
295
+ ai-dossier verify https://example.com/dossier.ds.md
270
296
 
271
297
  Download/Read File
272
298
 
@@ -331,23 +357,28 @@ Exit 0 (safe) or 1 (unsafe)
331
357
 
332
358
  ## Roadmap
333
359
 
334
- ### v0.1.0 (Current)
360
+ ### v0.1.0
335
361
  - ✅ Basic checksum verification
336
362
  - ✅ Signature presence detection
337
363
  - ✅ Exit code support
338
364
  - ✅ URL download support
339
365
 
340
- ### v0.2.0 (Next)
341
- - Full minisign signature verification
342
- - Trusted keys management (~/.dossier/trusted-keys.txt)
343
- - --run flag implementation
344
- - ⏳ Better error messages
366
+ ### v0.2.0
367
+ - Multi-command CLI structure (`ai-dossier <command>`)
368
+ - `dossier run` command with 5-stage verification pipeline
369
+ - LLM auto-detection and execution integration
370
+
371
+ ### v0.3.0
372
+ - ✅ Modular TypeScript migration
373
+ - ✅ Comprehensive test suite (261+ tests)
374
+ - ✅ CLI parity with dossier-tools
375
+ - ✅ `@ai-dossier` npm scope and CI/CD publishing
345
376
 
346
- ### v0.3.0 (Future)
347
- - Interactive trust prompts
348
- - Key import/export
349
- - Signature verification caching
350
- - JSON output mode (for tooling)
377
+ ### v0.4.0 (Current)
378
+ - Unified dossier parser across core/cli/mcp
379
+ - JSON output mode (`--json` flag on commands)
380
+ - Registry integration (publish, remove, install-skill)
381
+ - Non-TTY stdin detection
351
382
 
352
383
  ### v1.0.0 (Stable)
353
384
  - ⏳ Complete signature verification
@@ -366,10 +397,10 @@ cd cli
366
397
  npm link # For local testing
367
398
 
368
399
  # Test
369
- dossier-verify ../examples/devops/deploy-to-aws.ds.md
400
+ ai-dossier verify ../examples/devops/deploy-to-aws.ds.md
370
401
 
371
402
  # Test with malicious example
372
- dossier-verify ../examples/security/validate-project-config.ds.md
403
+ ai-dossier verify ../examples/security/validate-project-config.ds.md
373
404
  ```
374
405
 
375
406
  ### Adding Features
@@ -1,435 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- /**
4
- * Dossier Verification CLI
5
- *
6
- * Enforces security verification of dossier files before execution.
7
- * Provides command-line interface for checksum and signature validation.
8
- *
9
- * Usage:
10
- * dossier-verify <file-or-url>
11
- * dossier-verify --run <file-or-url>
12
- * dossier-verify --verbose <file-or-url>
13
- */
3
+ // Thin shim — all logic lives in src/verify-dossier.ts
4
+ const { main } = require('../dist/verify-dossier');
14
5
 
15
- const fs = require('fs');
16
- const path = require('path');
17
- const crypto = require('crypto');
18
- const https = require('https');
19
- const http = require('http');
20
- const { execSync } = require('child_process');
21
- const {
22
- parseDossierContent,
23
- verifyIntegrity,
24
- loadTrustedKeys,
25
- verifySignature
26
- } = require('@ai-dossier/core');
27
- const { convertGitHubBlobToRaw } = require('../dist/github-url');
28
-
29
- // Colors for terminal output
30
- const colors = {
31
- reset: '\x1b[0m',
32
- bright: '\x1b[1m',
33
- red: '\x1b[31m',
34
- green: '\x1b[32m',
35
- yellow: '\x1b[33m',
36
- blue: '\x1b[34m',
37
- cyan: '\x1b[36m',
38
- };
39
-
40
- function log(message, color = 'reset') {
41
- console.log(`${colors[color]}${message}${colors.reset}`);
42
- }
43
-
44
- function error(message) {
45
- log(`❌ ${message}`, 'red');
46
- }
47
-
48
- function success(message) {
49
- log(`✅ ${message}`, 'green');
50
- }
51
-
52
- function warning(message) {
53
- log(`⚠️ ${message}`, 'yellow');
54
- }
55
-
56
- function info(message) {
57
- log(`ℹ️ ${message}`, 'cyan');
58
- }
59
-
60
- // Parse command line arguments
61
- function parseArgs() {
62
- const args = process.argv.slice(2);
63
- const options = {
64
- verbose: false,
65
- run: false,
66
- outputPath: false,
67
- help: false,
68
- input: null,
69
- };
70
-
71
- for (let i = 0; i < args.length; i++) {
72
- const arg = args[i];
73
- if (arg === '--verbose' || arg === '-v') {
74
- options.verbose = true;
75
- } else if (arg === '--run') {
76
- options.run = true;
77
- } else if (arg === '--output-path') {
78
- options.outputPath = true;
79
- } else if (arg === '--help' || arg === '-h') {
80
- options.help = true;
81
- } else if (!options.input) {
82
- options.input = arg;
83
- }
84
- }
85
-
86
- return options;
87
- }
88
-
89
- function showHelp() {
90
- console.log(`
91
- ${colors.bright}Dossier Verification CLI${colors.reset}
92
-
93
- ${colors.bright}Usage:${colors.reset}
94
- dossier-verify <file-or-url> Verify dossier security
95
- dossier-verify --run <file-or-url> Verify and execute if safe
96
- dossier-verify --verbose <file-or-url> Show detailed verification
97
- dossier-verify --output-path <url> Download and output path
98
- dossier-verify --help Show this help
99
-
100
- ${colors.bright}Exit Codes:${colors.reset}
101
- 0 - Verification passed (safe to execute)
102
- 1 - Verification failed (do not execute)
103
- 2 - Error occurred (cannot verify)
104
-
105
- ${colors.bright}Examples:${colors.reset}
106
- # Verify local file
107
- dossier-verify path/to/dossier.ds.md
108
-
109
- # Verify remote dossier
110
- dossier-verify https://example.com/dossier.ds.md
111
-
112
- # Verify and run if safe
113
- dossier-verify --run https://example.com/dossier.ds.md
114
-
115
- # Use in shell script
116
- if dossier-verify "$URL"; then
117
- claude-code "run $URL"
118
- else
119
- echo "Security verification failed"
120
- fi
121
-
122
- ${colors.bright}Security Checks:${colors.reset}
123
- ✓ SHA256 checksum verification (required)
124
- ✓ Signature verification (if present)
125
- ✓ Trusted keys check
126
- ✓ Risk level assessment
127
-
128
- ${colors.bright}More Information:${colors.reset}
129
- Documentation: https://github.com/imboard/ai-dossier
130
- Security: SECURITY_STATUS.md
131
- Protocol: PROTOCOL.md
132
- `);
133
- }
134
-
135
- // Download file from URL
136
- async function downloadFile(url) {
137
- // Convert GitHub blob URLs to raw URLs
138
- const resolvedUrl = convertGitHubBlobToRaw(url);
139
-
140
- return new Promise((resolve, reject) => {
141
- const protocol = resolvedUrl.startsWith('https://') ? https : http;
142
-
143
- protocol.get(resolvedUrl, (res) => {
144
- if (res.statusCode === 301 || res.statusCode === 302) {
145
- // Follow redirect
146
- return downloadFile(res.headers.location).then(resolve).catch(reject);
147
- }
148
-
149
- if (res.statusCode !== 200) {
150
- return reject(new Error(`HTTP ${res.statusCode}: ${res.statusMessage}`));
151
- }
152
-
153
- let data = '';
154
- res.on('data', (chunk) => { data += chunk; });
155
- res.on('end', () => resolve(data));
156
- }).on('error', reject);
157
- });
158
- }
159
-
160
- // Parse dossier frontmatter (using @ai-dossier/core)
161
- function parseDossier(content) {
162
- const parsed = parseDossierContent(content);
163
- return {
164
- frontmatter: parsed.frontmatter,
165
- body: parsed.body
166
- };
167
- }
168
-
169
- // Verify checksum (using @ai-dossier/core)
170
- function verifyChecksum(body, declaredHash) {
171
- const result = verifyIntegrity(body, declaredHash);
172
- return {
173
- passed: result.status === 'valid',
174
- declared: result.expectedHash,
175
- actual: result.actualHash,
176
- };
177
- }
178
-
179
- // Verify signature (using @ai-dossier/core for real cryptographic verification)
180
- async function checkSignature(body, frontmatter) {
181
- if (!frontmatter.signature) {
182
- return {
183
- present: false,
184
- verified: false,
185
- trusted: false,
186
- message: 'No signature present',
187
- };
188
- }
189
-
190
- const signature = frontmatter.signature;
191
- const trustedKeys = loadTrustedKeys();
192
- // Check both key_id and public_key for trusted status
193
- // key_id is easier to manage in trusted-keys.txt file
194
- const isTrusted = trustedKeys.has(signature.key_id) || trustedKeys.has(signature.public_key);
195
-
196
- try {
197
- const isValid = await verifySignature(body, signature);
198
-
199
- if (!isValid) {
200
- return {
201
- present: true,
202
- verified: false,
203
- trusted: isTrusted,
204
- message: 'Signature verification FAILED',
205
- };
206
- }
207
-
208
- return {
209
- present: true,
210
- verified: true,
211
- trusted: isTrusted,
212
- message: isTrusted
213
- ? `Verified signature from trusted source: ${trustedKeys.get(signature.key_id) || trustedKeys.get(signature.public_key)}`
214
- : 'Valid signature but key is not in trusted list',
215
- };
216
- } catch (err) {
217
- return {
218
- present: true,
219
- verified: false,
220
- trusted: false,
221
- message: `Verification error: ${err.message}`,
222
- };
223
- }
224
- }
225
-
226
- // Assess risk
227
- function assessRisk(frontmatter, checksumResult, signatureResult) {
228
- const issues = [];
229
- let riskLevel = 'low';
230
- let shouldBlock = false;
231
-
232
- // Checksum failure is critical
233
- if (!checksumResult.passed) {
234
- issues.push('Checksum verification FAILED - content has been tampered with');
235
- riskLevel = 'critical';
236
- shouldBlock = true;
237
- }
238
-
239
- // Signature issues
240
- if (signatureResult.present && !signatureResult.verified) {
241
- issues.push('Signature verification FAILED or could not be verified');
242
- if (riskLevel !== 'critical') riskLevel = 'high';
243
- shouldBlock = true;
244
- }
245
-
246
- // Valid signature but not trusted - BLOCK execution
247
- if (signatureResult.present && signatureResult.verified && !signatureResult.trusted) {
248
- issues.push('Signature is valid but signer is not in your trusted keys list');
249
- issues.push('Add the public key to ~/.dossier/trusted-keys.txt to trust this signer');
250
- if (riskLevel === 'low') riskLevel = 'medium';
251
- shouldBlock = true;
252
- }
253
-
254
- // No signature on high-risk dossier
255
- if (!signatureResult.present && frontmatter.risk_level === 'high') {
256
- issues.push('High-risk dossier without signature');
257
- if (riskLevel === 'low') riskLevel = 'medium';
258
- }
259
-
260
- if (!signatureResult.present && frontmatter.risk_level === 'critical') {
261
- issues.push('Critical-risk dossier without signature');
262
- if (riskLevel !== 'critical') riskLevel = 'high';
263
- }
264
-
265
- return {
266
- level: riskLevel,
267
- issues,
268
- recommendation: shouldBlock ? 'BLOCK' : 'ALLOW',
269
- };
270
- }
271
-
272
- // Main verification function
273
- async function verifyDossier(input, options) {
274
- try {
275
- log(`\n${colors.bright}🔐 Dossier Verification Tool${colors.reset}\n`);
276
-
277
- // Determine if input is URL or file
278
- const isUrl = input.startsWith('http://') || input.startsWith('https://');
279
- let content;
280
- let displayPath = input;
281
-
282
- if (isUrl) {
283
- info(`Downloading: ${input}`);
284
- content = await downloadFile(input);
285
- success('Downloaded successfully');
286
- } else {
287
- info(`Reading: ${input}`);
288
- content = fs.readFileSync(input, 'utf8');
289
- displayPath = path.resolve(input);
290
- success('File read successfully');
291
- }
292
-
293
- // Parse dossier
294
- info('Parsing dossier...');
295
- const { frontmatter, body } = parseDossier(content);
296
- success(`Parsed: ${frontmatter.title} v${frontmatter.version}`);
297
-
298
- if (options.verbose) {
299
- console.log(`\n${colors.bright}Dossier Metadata:${colors.reset}`);
300
- console.log(` Title: ${frontmatter.title}`);
301
- console.log(` Version: ${frontmatter.version}`);
302
- console.log(` Risk Level: ${frontmatter.risk_level}`);
303
- console.log(` Protocol: ${frontmatter.protocol_version}`);
304
- }
305
-
306
- // Verify checksum
307
- console.log(`\n${colors.bright}📊 Integrity Check:${colors.reset}`);
308
- const checksumResult = verifyChecksum(body, frontmatter.checksum?.hash);
309
-
310
- if (checksumResult.passed) {
311
- success('Checksum VALID - content has not been tampered with');
312
- if (options.verbose) {
313
- console.log(` Hash: ${checksumResult.actual}`);
314
- }
315
- } else {
316
- error('Checksum INVALID - content has been modified!');
317
- if (options.verbose) {
318
- console.log(` Declared: ${checksumResult.declared}`);
319
- console.log(` Actual: ${checksumResult.actual}`);
320
- }
321
- }
322
-
323
- // Verify signature
324
- console.log(`\n${colors.bright}🔏 Authenticity Check:${colors.reset}`);
325
- const signatureResult = await checkSignature(body, frontmatter);
326
-
327
- if (signatureResult.present) {
328
- if (signatureResult.verified && signatureResult.trusted) {
329
- success('Signature VERIFIED - from trusted author');
330
- } else if (signatureResult.verified && !signatureResult.trusted) {
331
- warning(signatureResult.message);
332
- if (frontmatter.signature?.signed_by) {
333
- console.log(` Signed by: ${frontmatter.signature.signed_by}`);
334
- }
335
- console.log(`\n ${colors.cyan}To trust this signer, run:${colors.reset}`);
336
- const publicKey = frontmatter.signature.public_key || frontmatter.signature.key_id;
337
- const identifier = frontmatter.signature.signed_by
338
- ? frontmatter.signature.signed_by.split('<')[0].trim().toLowerCase().replace(/\s+/g, '-')
339
- : 'unknown-signer';
340
- console.log(` ${colors.bright}dossier keys add "${publicKey}" "${identifier}"${colors.reset}\n`);
341
- } else {
342
- warning(signatureResult.message);
343
- if (frontmatter.signature?.signed_by) {
344
- console.log(` Signed by: ${frontmatter.signature.signed_by}`);
345
- }
346
- }
347
- } else {
348
- warning('No signature present (dossier is unsigned)');
349
- }
350
-
351
- // Assess risk
352
- console.log(`\n${colors.bright}🔴 Risk Assessment:${colors.reset}`);
353
- const risk = assessRisk(frontmatter, checksumResult, signatureResult);
354
-
355
- const riskColors = {
356
- low: 'green',
357
- medium: 'yellow',
358
- high: 'yellow',
359
- critical: 'red',
360
- };
361
-
362
- log(` Risk Level: ${risk.level.toUpperCase()}`, riskColors[risk.level]);
363
-
364
- if (risk.issues.length > 0) {
365
- console.log(`\n Issues Found:`);
366
- risk.issues.forEach(issue => {
367
- console.log(` - ${issue}`);
368
- });
369
- }
370
-
371
- // Recommendation
372
- console.log(`\n${colors.bright}Recommendation:${colors.reset}`, risk.recommendation);
373
-
374
- if (risk.recommendation === 'BLOCK') {
375
- error('\nDO NOT EXECUTE this dossier');
376
- console.log(' Security verification failed.');
377
- console.log(' This dossier may have been tampered with or is from an untrusted source.\n');
378
- return false;
379
- } else if (risk.level === 'medium' || risk.level === 'high') {
380
- warning('\nProceed with CAUTION');
381
- console.log(' Review the dossier code before executing.');
382
- console.log(' Consider the risk level and your trust in the source.\n');
383
- return true;
384
- } else {
385
- success('\nSafe to execute');
386
- console.log(' Dossier passed security verification.\n');
387
- return true;
388
- }
389
-
390
- } catch (err) {
391
- error(`\nVerification failed: ${err.message}`);
392
- if (options.verbose) {
393
- console.error(err);
394
- }
395
- return false;
396
- }
397
- }
398
-
399
- // Main entry point
400
- async function main() {
401
- const options = parseArgs();
402
-
403
- if (options.help || !options.input) {
404
- showHelp();
405
- process.exit(options.help ? 0 : 2);
406
- }
407
-
408
- const passed = await verifyDossier(options.input, options);
409
-
410
- if (!passed) {
411
- process.exit(1); // Verification failed
412
- }
413
-
414
- if (options.run) {
415
- warning('\n--run flag not yet implemented');
416
- warning('For now, manually execute the dossier if verification passed');
417
- }
418
-
419
- if (options.outputPath) {
420
- // Would output the path to downloaded file
421
- warning('\n--output-path flag not yet implemented');
422
- }
423
-
424
- process.exit(0); // Success
425
- }
426
-
427
- // Run if called directly
428
- if (require.main === module) {
429
- main().catch(err => {
430
- error(`Fatal error: ${err.message}`);
431
- process.exit(2);
432
- });
433
- }
434
-
435
- module.exports = { verifyDossier, parseDossier, verifyChecksum };
6
+ main().catch((err) => {
7
+ console.error(`Fatal error: ${err.message}`);
8
+ process.exit(2);
9
+ });