ship-safe 6.1.1 → 6.3.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 (49) hide show
  1. package/README.md +748 -641
  2. package/cli/agents/api-fuzzer.js +345 -345
  3. package/cli/agents/auth-bypass-agent.js +348 -348
  4. package/cli/agents/base-agent.js +272 -272
  5. package/cli/agents/cicd-scanner.js +236 -201
  6. package/cli/agents/config-auditor.js +521 -521
  7. package/cli/agents/deep-analyzer.js +6 -2
  8. package/cli/agents/git-history-scanner.js +170 -170
  9. package/cli/agents/html-reporter.js +568 -568
  10. package/cli/agents/index.js +85 -84
  11. package/cli/agents/injection-tester.js +500 -500
  12. package/cli/agents/legal-risk-agent.js +302 -0
  13. package/cli/agents/llm-redteam.js +251 -251
  14. package/cli/agents/mobile-scanner.js +231 -231
  15. package/cli/agents/orchestrator.js +322 -322
  16. package/cli/agents/pii-compliance-agent.js +301 -301
  17. package/cli/agents/scoring-engine.js +248 -248
  18. package/cli/agents/supabase-rls-agent.js +154 -154
  19. package/cli/agents/supply-chain-agent.js +650 -507
  20. package/cli/bin/ship-safe.js +464 -426
  21. package/cli/commands/agent.js +608 -608
  22. package/cli/commands/audit.js +1006 -980
  23. package/cli/commands/baseline.js +193 -193
  24. package/cli/commands/ci.js +342 -342
  25. package/cli/commands/deps.js +516 -516
  26. package/cli/commands/doctor.js +159 -159
  27. package/cli/commands/fix.js +218 -218
  28. package/cli/commands/hooks.js +268 -0
  29. package/cli/commands/init.js +407 -407
  30. package/cli/commands/legal.js +158 -0
  31. package/cli/commands/mcp.js +304 -304
  32. package/cli/commands/red-team.js +7 -1
  33. package/cli/commands/remediate.js +798 -798
  34. package/cli/commands/rotate.js +571 -571
  35. package/cli/commands/scan.js +569 -569
  36. package/cli/commands/score.js +449 -449
  37. package/cli/commands/watch.js +281 -281
  38. package/cli/hooks/patterns.js +313 -0
  39. package/cli/hooks/post-tool-use.js +140 -0
  40. package/cli/hooks/pre-tool-use.js +186 -0
  41. package/cli/index.js +73 -69
  42. package/cli/providers/llm-provider.js +397 -287
  43. package/cli/utils/autofix-rules.js +74 -74
  44. package/cli/utils/cache-manager.js +311 -311
  45. package/cli/utils/output.js +230 -230
  46. package/cli/utils/patterns.js +1121 -1121
  47. package/cli/utils/pdf-generator.js +94 -94
  48. package/package.json +69 -69
  49. package/configs/supabase/rls-templates.sql +0 -242
@@ -1,426 +1,464 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * Ship Safe CLI
5
- * =============
6
- *
7
- * Security toolkit for vibe coders and indie hackers.
8
- *
9
- * USAGE:
10
- * npx ship-safe scan [path] Scan for secrets in your codebase
11
- * npx ship-safe checklist Run the launch-day security checklist
12
- * npx ship-safe init Initialize security configs in your project
13
- * npx ship-safe fix Generate .env.example from found secrets
14
- * npx ship-safe guard Install pre-push git hook
15
- * npx ship-safe --help Show all commands
16
- */
17
-
18
- import { program } from 'commander';
19
- import chalk from 'chalk';
20
- import { readFileSync } from 'fs';
21
- import { fileURLToPath } from 'url';
22
- import { dirname, join } from 'path';
23
- import { scanCommand } from '../commands/scan.js';
24
- import { checklistCommand } from '../commands/checklist.js';
25
- import { initCommand } from '../commands/init.js';
26
- import { fixCommand } from '../commands/fix.js';
27
- import { guardCommand } from '../commands/guard.js';
28
- import { mcpCommand } from '../commands/mcp.js';
29
- import { remediateCommand } from '../commands/remediate.js';
30
- import { rotateCommand } from '../commands/rotate.js';
31
- import { agentCommand } from '../commands/agent.js';
32
- import { depsCommand } from '../commands/deps.js';
33
- import { scoreCommand } from '../commands/score.js';
34
- import { redTeamCommand } from '../commands/red-team.js';
35
- import { watchCommand } from '../commands/watch.js';
36
- import { auditCommand } from '../commands/audit.js';
37
- import { doctorCommand } from '../commands/doctor.js';
38
- import { baselineCommand } from '../commands/baseline.js';
39
- import { ciCommand } from '../commands/ci.js';
40
- import { diffCommand } from '../commands/diff.js';
41
- import { vibeCheckCommand } from '../commands/vibe-check.js';
42
- import { benchmarkCommand } from '../commands/benchmark.js';
43
- import { openclawCommand } from '../commands/openclaw.js';
44
- import { scanSkillCommand } from '../commands/scan-skill.js';
45
- import { abomCommand } from '../commands/abom.js';
46
- import { updateIntelCommand } from '../commands/update-intel.js';
47
- import { ABOMGenerator } from '../agents/abom-generator.js';
48
- import { PolicyEngine } from '../agents/policy-engine.js';
49
- import { SBOMGenerator } from '../agents/sbom-generator.js';
50
-
51
- // =============================================================================
52
- // CLI CONFIGURATION
53
- // =============================================================================
54
-
55
- const DEFAULT_MODEL = 'claude-haiku-4-5-20251001';
56
-
57
- // Read version from package.json
58
- const __filename = fileURLToPath(import.meta.url); // ship-safe-ignore — module's own path via import.meta.url, not user input
59
- const __dirname = dirname(__filename);
60
- const packageJson = JSON.parse(readFileSync(join(__dirname, '../../package.json'), 'utf8'));
61
- const VERSION = packageJson.version;
62
-
63
- // Banner shown on help
64
- const banner = `
65
- ${chalk.cyan('███████╗██╗ ██╗██╗██████╗ ███████╗ █████╗ ███████╗███████╗')}
66
- ${chalk.cyan('██╔════╝██║ ██║██║██╔══██╗ ██╔════╝██╔══██╗██╔════╝██╔════╝')}
67
- ${chalk.cyan('███████╗███████║██║██████╔╝ ███████╗███████║█████╗ █████╗ ')}
68
- ${chalk.cyan('╚════██║██╔══██║██║██╔═══╝ ╚════██║██╔══██║██╔══╝ ██╔══╝ ')}
69
- ${chalk.cyan('███████║██║ ██║██║██║ ███████║██║ ██║██║ ███████╗')}
70
- ${chalk.cyan('╚══════╝╚═╝ ╚═╝╚═╝╚═╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚══════╝')}
71
-
72
- ${chalk.gray('Security toolkit for vibe coders. Secure your MVP in 5 minutes.')}
73
- `;
74
-
75
- // =============================================================================
76
- // PROGRAM SETUP
77
- // =============================================================================
78
-
79
- program
80
- .name('ship-safe')
81
- .description('Security toolkit for vibe coders and indie hackers')
82
- .version(VERSION)
83
- .addHelpText('before', banner);
84
-
85
- // -----------------------------------------------------------------------------
86
- // SCAN COMMAND
87
- // -----------------------------------------------------------------------------
88
- program
89
- .command('scan [path]')
90
- .description('Scan your codebase for leaked secrets (API keys, passwords, etc.)')
91
- .option('-v, --verbose', 'Show all files being scanned')
92
- .option('--no-color', 'Disable colored output')
93
- .option('--json', 'Output results as JSON (useful for CI)')
94
- .option('--sarif', 'Output results in SARIF format (for GitHub Code Scanning)')
95
- .option('--include-tests', 'Also scan test files (excluded by default to reduce false positives)')
96
- .option('--no-cache', 'Force full rescan (ignore cached results)')
97
- .action(scanCommand);
98
-
99
- // -----------------------------------------------------------------------------
100
- // CHECKLIST COMMAND
101
- // -----------------------------------------------------------------------------
102
- program
103
- .command('checklist')
104
- .description('Run through the launch-day security checklist interactively')
105
- .option('--no-interactive', 'Print checklist without prompts')
106
- .action(checklistCommand);
107
-
108
- // -----------------------------------------------------------------------------
109
- // INIT COMMAND
110
- // -----------------------------------------------------------------------------
111
- program
112
- .command('init')
113
- .description('Initialize security configs in your project')
114
- .option('-f, --force', 'Overwrite existing files')
115
- .option('--gitignore', 'Only copy .gitignore')
116
- .option('--headers', 'Only copy security headers config')
117
- .option('--agents', 'Only add security rules to AI agent instruction files (CLAUDE.md, .cursor/rules/, .windsurfrules, copilot-instructions.md)')
118
- .option('--openclaw', 'Generate a hardened openclaw.json template')
119
- .action(initCommand);
120
-
121
- // -----------------------------------------------------------------------------
122
- // FIX COMMAND
123
- // -----------------------------------------------------------------------------
124
- program
125
- .command('fix')
126
- .description('Scan for secrets and generate a .env.example with placeholder values')
127
- .option('--dry-run', 'Preview generated .env.example without writing it')
128
- .action(fixCommand);
129
-
130
- // -----------------------------------------------------------------------------
131
- // GUARD COMMAND
132
- // -----------------------------------------------------------------------------
133
- program
134
- .command('guard [action]')
135
- .description('Install a git hook to block pushes if secrets are found')
136
- .option('--pre-commit', 'Install as pre-commit hook instead of pre-push')
137
- .option('--generate-hooks', 'Generate defensive Claude Code hooks (.claude/settings.json)')
138
- .action(guardCommand);
139
-
140
- // -----------------------------------------------------------------------------
141
- // MCP SERVER COMMAND
142
- // -----------------------------------------------------------------------------
143
- program
144
- .command('mcp')
145
- .description('Start ship-safe as an MCP server (for Claude Desktop, Cursor, Windsurf, etc.)')
146
- .action(mcpCommand);
147
-
148
- // -----------------------------------------------------------------------------
149
- // REMEDIATE COMMAND
150
- // -----------------------------------------------------------------------------
151
- program
152
- .command('remediate [path]')
153
- .description('Auto-fix hardcoded secrets: rewrite source code + write .env + update .gitignore')
154
- .option('--dry-run', 'Preview changes without writing any files')
155
- .option('--yes', 'Apply all fixes without prompting (for CI)')
156
- .option('--stage', 'Also run git add on modified files after fixing')
157
- .option('--all', 'Also fix common agent findings (debug mode, TLS bypass, shell injection)')
158
- .action(remediateCommand);
159
-
160
- // -----------------------------------------------------------------------------
161
- // ROTATE COMMAND
162
- // -----------------------------------------------------------------------------
163
- program
164
- .command('rotate [path]')
165
- .description('Revoke and rotate exposed secrets — opens provider dashboards with step-by-step guide')
166
- .option('--provider <name>', 'Only rotate secrets for a specific provider (e.g. github, stripe, openai)')
167
- .action(rotateCommand);
168
-
169
- // -----------------------------------------------------------------------------
170
- // AGENT COMMAND
171
- // -----------------------------------------------------------------------------
172
- program
173
- .command('agent [path]')
174
- .description('AI-powered security audit: scan, classify with Claude, auto-remediate confirmed secrets')
175
- .option('--dry-run', 'Show classification and plan without writing any files')
176
- .option('--model <model>', `Claude model to use (default: ${DEFAULT_MODEL})`)
177
- .action(agentCommand);
178
-
179
- // -----------------------------------------------------------------------------
180
- // DEPS COMMAND
181
- // -----------------------------------------------------------------------------
182
- program
183
- .command('deps [path]')
184
- .description('Audit dependencies for known CVEs (npm, yarn, pnpm, pip-audit, bundler-audit)')
185
- .option('--fix', 'Run the package manager fix command after auditing')
186
- .action(depsCommand);
187
-
188
- // -----------------------------------------------------------------------------
189
- // SCORE COMMAND
190
- // -----------------------------------------------------------------------------
191
- program
192
- .command('score [path]')
193
- .description('Compute a 0-100 security health score for your project')
194
- .option('--no-deps', 'Skip dependency audit')
195
- .action(scoreCommand);
196
-
197
- // -----------------------------------------------------------------------------
198
- // AUDIT COMMAND (v4.0 — Full Security Audit)
199
- // -----------------------------------------------------------------------------
200
- program
201
- .command('audit [path]')
202
- .description('Full security audit: secrets + 18 agents + deps + score + deep analysis + remediation plan')
203
- .option('--json', 'Output results as JSON')
204
- .option('--sarif', 'Output results in SARIF format')
205
- .option('--csv', 'Output results as CSV')
206
- .option('--md', 'Output results as Markdown')
207
- .option('--html [file]', 'HTML report path (default: ship-safe-report.html)')
208
- .option('--compare', 'Show detailed comparison with last scan')
209
- .option('--timeout <ms>', 'Per-agent timeout in milliseconds (default: 30000)', parseInt)
210
- .option('--no-deps', 'Skip dependency audit')
211
- .option('--no-ai', 'Skip AI classification')
212
- .option('--no-cache', 'Force full rescan (ignore cached results)')
213
- .option('--baseline', 'Only show findings not in the baseline')
214
- .option('--pdf [file]', 'Generate PDF report (requires Chrome/Chromium)')
215
- .option('--deep', 'LLM-powered taint analysis for critical/high findings')
216
- .option('--local', 'Use local Ollama model for deep analysis (default: llama3.2)')
217
- .option('--model <model>', 'LLM model to use for deep/AI analysis')
218
- .option('--budget <cents>', 'Max spend in cents for deep analysis (default: 50)', parseInt)
219
- .option('--verify', 'Check if leaked secrets are still active (probes provider APIs)')
220
- .option('-v, --verbose', 'Verbose output')
221
- .action(auditCommand);
222
-
223
- // -----------------------------------------------------------------------------
224
- // DIFF COMMAND (v6.0 Scan only changed files)
225
- // -----------------------------------------------------------------------------
226
- program
227
- .command('diff [ref]')
228
- .description('Scan only changed files (git diff) — fast pre-commit & PR scanning')
229
- .option('--staged', 'Scan only staged changes')
230
- .option('--json', 'Output results as JSON')
231
- .option('-p, --path <path>', 'Project path (default: cwd)')
232
- .option('--timeout <ms>', 'Per-agent timeout in milliseconds (default: 30000)', parseInt)
233
- .action(diffCommand);
234
-
235
- // -----------------------------------------------------------------------------
236
- // RED TEAM COMMAND (v4.0 — Multi-Agent Security Audit)
237
- // -----------------------------------------------------------------------------
238
- program
239
- .command('red-team [path]')
240
- .description('Multi-agent security audit: 18 agents scan for 80+ attack classes')
241
- .option('--agents <list>', 'Comma-separated list of agents to run')
242
- .option('--json', 'Output results as JSON')
243
- .option('--sarif', 'Output results in SARIF format')
244
- .option('--html [file]', 'Generate HTML security report')
245
- .option('--sbom [file]', 'Generate CycloneDX SBOM')
246
- .option('--no-deps', 'Skip dependency audit')
247
- .option('--no-ai', 'Skip AI classification')
248
- .option('--deep', 'LLM-powered taint analysis for critical/high findings')
249
- .option('--local', 'Use local Ollama model for deep analysis (default: llama3.2)')
250
- .option('--model <model>', 'LLM model for deep analysis')
251
- .option('--budget <cents>', 'Max spend in cents for deep analysis (default: 50)', parseInt)
252
- .option('-v, --verbose', 'Verbose output')
253
- .action(redTeamCommand);
254
-
255
- // -----------------------------------------------------------------------------
256
- // WATCH COMMAND
257
- // -----------------------------------------------------------------------------
258
- program
259
- .command('watch [path]')
260
- .description('Continuous monitoring: watch files for security issues in real-time')
261
- .option('--poll', 'Use polling mode (for network drives)')
262
- .option('--configs', 'Watch only agent config files (openclaw.json, .cursorrules, mcp.json, etc.)')
263
- .action(watchCommand);
264
-
265
- // -----------------------------------------------------------------------------
266
- // SBOM COMMAND
267
- // -----------------------------------------------------------------------------
268
- program
269
- .command('sbom [path]')
270
- .description('Generate Software Bill of Materials (CycloneDX SBOM)')
271
- .option('-o, --output <file>', 'Output file path', 'sbom.json')
272
- .action((targetPath = '.', options) => {
273
- const absolutePath = join(process.cwd(), targetPath);
274
- const sbom = new SBOMGenerator();
275
- sbom.generateToFile(absolutePath, options.output);
276
- console.log(chalk.green(`✔ SBOM saved to ${options.output}`));
277
- });
278
-
279
- // -----------------------------------------------------------------------------
280
- // POLICY COMMAND
281
- // -----------------------------------------------------------------------------
282
- program
283
- .command('policy <action>')
284
- .description('Manage security policies (init: create policy template)')
285
- .action((action) => {
286
- if (action === 'init') {
287
- const policyPath = PolicyEngine.generateTemplate(process.cwd());
288
- console.log(chalk.green(`✔ Policy template created: ${policyPath}`));
289
- console.log(chalk.gray(' Edit .ship-safe.policy.json to configure your security policy.'));
290
- } else {
291
- console.log(chalk.yellow(`Unknown policy action: ${action}. Use: policy init`));
292
- }
293
- });
294
-
295
- // -----------------------------------------------------------------------------
296
- // BASELINE COMMAND (v4.3)
297
- // -----------------------------------------------------------------------------
298
- program
299
- .command('baseline [path]')
300
- .description('Create/manage a findings baseline — only report new findings on subsequent scans')
301
- .option('--diff', 'Show what changed since baseline')
302
- .option('--clear', 'Remove the baseline')
303
- .action(baselineCommand);
304
-
305
- // -----------------------------------------------------------------------------
306
- // CI COMMAND (v5.0 — CI/CD Pipeline Integration)
307
- // -----------------------------------------------------------------------------
308
- program
309
- .command('ci [path]')
310
- .description('CI/CD pipeline mode: scan, score, exit 1 on failure — optimized for automation')
311
- .option('--threshold <score>', 'Minimum passing score (default: 75)', parseInt)
312
- .option('--fail-on <severity>', 'Fail on findings at this severity or above (critical, high, medium)')
313
- .option('--sarif <file>', 'Write SARIF output for GitHub Code Scanning')
314
- .option('--json', 'JSON output')
315
- .option('--no-deps', 'Skip dependency audit')
316
- .option('--baseline', 'Only check new findings (not in baseline)')
317
- .option('--github-pr', 'Post findings as a GitHub PR comment (requires gh CLI)')
318
- .action(ciCommand);
319
-
320
- // -----------------------------------------------------------------------------
321
- // VIBE CHECK COMMAND
322
- // -----------------------------------------------------------------------------
323
- program
324
- .command('vibe-check [path]')
325
- .description('Fun security check with emoji output, shareable score, and badge generator')
326
- .option('--badge', 'Generate a shields.io markdown badge for your README')
327
- .action(vibeCheckCommand);
328
-
329
- // -----------------------------------------------------------------------------
330
- // BENCHMARK COMMAND
331
- // -----------------------------------------------------------------------------
332
- program
333
- .command('benchmark [path]')
334
- .description('Compare your security score against industry averages')
335
- .option('--json', 'Output results as JSON')
336
- .action(benchmarkCommand);
337
-
338
- // -----------------------------------------------------------------------------
339
- // OPENCLAW COMMAND
340
- // -----------------------------------------------------------------------------
341
- program
342
- .command('openclaw [path]')
343
- .description('OpenClaw security scan: agent configs, MCP servers, skills, hooks')
344
- .option('--fix', 'Auto-harden OpenClaw and agent configurations')
345
- .option('--preflight', 'Exit non-zero on critical findings (for CI)')
346
- .option('--red-team', 'Simulate adversarial attacks against agent configs')
347
- .option('--json', 'Output results as JSON')
348
- .action(openclawCommand);
349
-
350
- // -----------------------------------------------------------------------------
351
- // SCAN-SKILL COMMAND
352
- // -----------------------------------------------------------------------------
353
- program
354
- .command('scan-skill [target]')
355
- .description('Analyze an AI agent skill for security issues before installing it')
356
- .option('--all', 'Scan all skills defined in openclaw.json')
357
- .option('--json', 'Output results as JSON')
358
- .action(scanSkillCommand);
359
-
360
- // -----------------------------------------------------------------------------
361
- // ABOM COMMAND
362
- // -----------------------------------------------------------------------------
363
- program
364
- .command('abom [path]')
365
- .description('Generate Agent Bill of Materials (CycloneDX ABOM) — MCP servers, skills, configs, LLM providers')
366
- .option('-o, --output <file>', 'Output file path', 'abom.json')
367
- .option('--json', 'Output to stdout as JSON')
368
- .action(abomCommand);
369
-
370
- // -----------------------------------------------------------------------------
371
- // UPDATE-INTEL COMMAND
372
- // -----------------------------------------------------------------------------
373
- program
374
- .command('update-intel')
375
- .description('Update threat intelligence feed (malicious skill hashes, compromised MCP servers)')
376
- .option('--url <url>', 'Custom feed URL')
377
- .action(updateIntelCommand);
378
-
379
- // -----------------------------------------------------------------------------
380
- // DOCTOR COMMAND
381
- // -----------------------------------------------------------------------------
382
- program
383
- .command('doctor')
384
- .description('Diagnose environment: check Node.js, git, API keys, cache, and dependencies')
385
- .action(doctorCommand);
386
-
387
- // -----------------------------------------------------------------------------
388
- // PARSE AND RUN
389
- // -----------------------------------------------------------------------------
390
-
391
- // Show help if no command provided
392
- if (process.argv.length === 2) {
393
- console.log(banner);
394
- console.log(chalk.yellow('\nQuick start:\n'));
395
- console.log(chalk.cyan.bold(' v6.0 — Full Security Audit'));
396
- console.log(chalk.white(' npx ship-safe audit . ') + chalk.gray('# Full audit: secrets + 18 agents + deps + remediation'));
397
- console.log(chalk.white(' npx ship-safe audit . --deep') + chalk.gray('# LLM-powered taint analysis (Anthropic/Ollama)'));
398
- console.log(chalk.white(' npx ship-safe red-team . ') + chalk.gray('# 18-agent red team scan (80+ attack classes)'));
399
- console.log(chalk.white(' npx ship-safe vibe-check . ') + chalk.gray('# Fun security check with emoji & shareable badge'));
400
- console.log(chalk.white(' npx ship-safe benchmark . ') + chalk.gray('# Compare score against industry averages'));
401
- console.log(chalk.white(' npx ship-safe ci . ') + chalk.gray('# CI/CD mode: scan, score, exit code'));
402
- console.log(chalk.white(' npx ship-safe diff ') + chalk.gray('# Scan only changed files (fast pre-commit)'));
403
- console.log(chalk.white(' npx ship-safe watch . ') + chalk.gray('# Continuous monitoring mode'));
404
- console.log(chalk.white(' npx ship-safe openclaw . ') + chalk.gray('# OpenClaw & agent config security scan'));
405
- console.log(chalk.white(' npx ship-safe scan-skill <u>') + chalk.gray('# Vet a skill before installing'));
406
- console.log(chalk.white(' npx ship-safe abom . ') + chalk.gray('# Agent Bill of Materials (CycloneDX)'));
407
- console.log(chalk.white(' npx ship-safe sbom . ') + chalk.gray('# Generate CycloneDX SBOM (CRA-ready)'));
408
- console.log(chalk.white(' npx ship-safe update-intel ') + chalk.gray('# Update threat intelligence feed'));
409
- console.log(chalk.white(' npx ship-safe policy init ') + chalk.gray('# Create security policy template'));
410
- console.log(chalk.white(' npx ship-safe doctor ') + chalk.gray('# Check environment and configuration'));
411
- console.log();
412
- console.log(chalk.gray(' Core commands:'));
413
- console.log(chalk.white(' npx ship-safe agent . ') + chalk.gray('# AI audit: scan + classify + auto-fix'));
414
- console.log(chalk.white(' npx ship-safe scan . ') + chalk.gray('# Scan for secrets'));
415
- console.log(chalk.white(' npx ship-safe remediate . ') + chalk.gray('# Auto-fix: rewrite code + write .env'));
416
- console.log(chalk.white(' npx ship-safe rotate . ') + chalk.gray('# Revoke exposed keys (provider guides)'));
417
- console.log(chalk.white(' npx ship-safe deps . ') + chalk.gray('# Audit dependencies for CVEs'));
418
- console.log(chalk.white(' npx ship-safe score . ') + chalk.gray('# Security health score (0-100)'));
419
- console.log(chalk.white(' npx ship-safe guard ') + chalk.gray('# Block git push if secrets found'));
420
- console.log(chalk.white(' npx ship-safe init ') + chalk.gray('# Add security configs to your project'));
421
- console.log(chalk.white('\n npx ship-safe --help ') + chalk.gray('# Show all options'));
422
- console.log();
423
- process.exit(0);
424
- }
425
-
426
- program.parse();
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Ship Safe CLI
5
+ * =============
6
+ *
7
+ * Security toolkit for vibe coders and indie hackers.
8
+ *
9
+ * USAGE:
10
+ * npx ship-safe scan [path] Scan for secrets in your codebase
11
+ * npx ship-safe checklist Run the launch-day security checklist
12
+ * npx ship-safe init Initialize security configs in your project
13
+ * npx ship-safe fix Generate .env.example from found secrets
14
+ * npx ship-safe guard Install pre-push git hook
15
+ * npx ship-safe --help Show all commands
16
+ */
17
+
18
+ import { program } from 'commander';
19
+ import chalk from 'chalk';
20
+ import { readFileSync } from 'fs';
21
+ import { fileURLToPath } from 'url';
22
+ import { dirname, join } from 'path';
23
+ import { scanCommand } from '../commands/scan.js';
24
+ import { checklistCommand } from '../commands/checklist.js';
25
+ import { initCommand } from '../commands/init.js';
26
+ import { fixCommand } from '../commands/fix.js';
27
+ import { guardCommand } from '../commands/guard.js';
28
+ import { mcpCommand } from '../commands/mcp.js';
29
+ import { remediateCommand } from '../commands/remediate.js';
30
+ import { rotateCommand } from '../commands/rotate.js';
31
+ import { agentCommand } from '../commands/agent.js';
32
+ import { depsCommand } from '../commands/deps.js';
33
+ import { scoreCommand } from '../commands/score.js';
34
+ import { redTeamCommand } from '../commands/red-team.js';
35
+ import { watchCommand } from '../commands/watch.js';
36
+ import { auditCommand } from '../commands/audit.js';
37
+ import { doctorCommand } from '../commands/doctor.js';
38
+ import { baselineCommand } from '../commands/baseline.js';
39
+ import { ciCommand } from '../commands/ci.js';
40
+ import { diffCommand } from '../commands/diff.js';
41
+ import { vibeCheckCommand } from '../commands/vibe-check.js';
42
+ import { benchmarkCommand } from '../commands/benchmark.js';
43
+ import { openclawCommand } from '../commands/openclaw.js';
44
+ import { scanSkillCommand } from '../commands/scan-skill.js';
45
+ import { abomCommand } from '../commands/abom.js';
46
+ import { updateIntelCommand } from '../commands/update-intel.js';
47
+ import { hooksCommand } from '../commands/hooks.js';
48
+ import { legalCommand } from '../commands/legal.js';
49
+ import { ABOMGenerator } from '../agents/abom-generator.js';
50
+ import { PolicyEngine } from '../agents/policy-engine.js';
51
+ import { SBOMGenerator } from '../agents/sbom-generator.js';
52
+
53
+ // =============================================================================
54
+ // CLI CONFIGURATION
55
+ // =============================================================================
56
+
57
+ const DEFAULT_MODEL = 'claude-haiku-4-5-20251001';
58
+
59
+ // Read version from package.json
60
+ const __filename = fileURLToPath(import.meta.url); // ship-safe-ignore — module's own path via import.meta.url, not user input
61
+ const __dirname = dirname(__filename);
62
+ const packageJson = JSON.parse(readFileSync(join(__dirname, '../../package.json'), 'utf8'));
63
+ const VERSION = packageJson.version;
64
+
65
+ // Banner shown on help
66
+ const banner = `
67
+ ${chalk.cyan('███████╗██╗ ██╗██╗██████╗ ███████╗ █████╗ ███████╗███████╗')}
68
+ ${chalk.cyan('██╔════╝██║ ██║██║██╔══██╗ ██╔════╝██╔══██╗██╔════╝██╔════╝')}
69
+ ${chalk.cyan('███████╗███████║██║██████╔╝ ███████╗███████║█████╗ █████╗ ')}
70
+ ${chalk.cyan('╚════██║██╔══██║██║██╔═══╝ ╚════██║██╔══██║██╔══╝ ██╔══╝ ')}
71
+ ${chalk.cyan('███████║██║ ██║██║██║ ███████║██║ ██║██║ ███████╗')}
72
+ ${chalk.cyan('╚══════╝╚═╝ ╚═╝╚═╝╚═╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚══════╝')}
73
+
74
+ ${chalk.gray('Security toolkit for vibe coders. Secure your MVP in 5 minutes.')}
75
+ `;
76
+
77
+ // =============================================================================
78
+ // PROGRAM SETUP
79
+ // =============================================================================
80
+
81
+ program
82
+ .name('ship-safe')
83
+ .description('Security toolkit for vibe coders and indie hackers')
84
+ .version(VERSION)
85
+ .addHelpText('before', banner);
86
+
87
+ // -----------------------------------------------------------------------------
88
+ // SCAN COMMAND
89
+ // -----------------------------------------------------------------------------
90
+ program
91
+ .command('scan [path]')
92
+ .description('Scan your codebase for leaked secrets (API keys, passwords, etc.)')
93
+ .option('-v, --verbose', 'Show all files being scanned')
94
+ .option('--no-color', 'Disable colored output')
95
+ .option('--json', 'Output results as JSON (useful for CI)')
96
+ .option('--sarif', 'Output results in SARIF format (for GitHub Code Scanning)')
97
+ .option('--include-tests', 'Also scan test files (excluded by default to reduce false positives)')
98
+ .option('--no-cache', 'Force full rescan (ignore cached results)')
99
+ .action(scanCommand);
100
+
101
+ // -----------------------------------------------------------------------------
102
+ // CHECKLIST COMMAND
103
+ // -----------------------------------------------------------------------------
104
+ program
105
+ .command('checklist')
106
+ .description('Run through the launch-day security checklist interactively')
107
+ .option('--no-interactive', 'Print checklist without prompts')
108
+ .action(checklistCommand);
109
+
110
+ // -----------------------------------------------------------------------------
111
+ // INIT COMMAND
112
+ // -----------------------------------------------------------------------------
113
+ program
114
+ .command('init')
115
+ .description('Initialize security configs in your project')
116
+ .option('-f, --force', 'Overwrite existing files')
117
+ .option('--gitignore', 'Only copy .gitignore')
118
+ .option('--headers', 'Only copy security headers config')
119
+ .option('--agents', 'Only add security rules to AI agent instruction files (CLAUDE.md, .cursor/rules/, .windsurfrules, copilot-instructions.md)')
120
+ .option('--openclaw', 'Generate a hardened openclaw.json template')
121
+ .action(initCommand);
122
+
123
+ // -----------------------------------------------------------------------------
124
+ // FIX COMMAND
125
+ // -----------------------------------------------------------------------------
126
+ program
127
+ .command('fix')
128
+ .description('Scan for secrets and generate a .env.example with placeholder values')
129
+ .option('--dry-run', 'Preview generated .env.example without writing it')
130
+ .action(fixCommand);
131
+
132
+ // -----------------------------------------------------------------------------
133
+ // GUARD COMMAND
134
+ // -----------------------------------------------------------------------------
135
+ program
136
+ .command('guard [action]')
137
+ .description('Install a git hook to block pushes if secrets are found')
138
+ .option('--pre-commit', 'Install as pre-commit hook instead of pre-push')
139
+ .option('--generate-hooks', 'Generate defensive Claude Code hooks (.claude/settings.json)')
140
+ .action(guardCommand);
141
+
142
+ // -----------------------------------------------------------------------------
143
+ // MCP SERVER COMMAND
144
+ // -----------------------------------------------------------------------------
145
+ program
146
+ .command('mcp')
147
+ .description('Start ship-safe as an MCP server (for Claude Desktop, Cursor, Windsurf, etc.)')
148
+ .action(mcpCommand);
149
+
150
+ // -----------------------------------------------------------------------------
151
+ // REMEDIATE COMMAND
152
+ // -----------------------------------------------------------------------------
153
+ program
154
+ .command('remediate [path]')
155
+ .description('Auto-fix hardcoded secrets: rewrite source code + write .env + update .gitignore')
156
+ .option('--dry-run', 'Preview changes without writing any files')
157
+ .option('--yes', 'Apply all fixes without prompting (for CI)')
158
+ .option('--stage', 'Also run git add on modified files after fixing')
159
+ .option('--all', 'Also fix common agent findings (debug mode, TLS bypass, shell injection)')
160
+ .action(remediateCommand);
161
+
162
+ // -----------------------------------------------------------------------------
163
+ // ROTATE COMMAND
164
+ // -----------------------------------------------------------------------------
165
+ program
166
+ .command('rotate [path]')
167
+ .description('Revoke and rotate exposed secrets — opens provider dashboards with step-by-step guide')
168
+ .option('--provider <name>', 'Only rotate secrets for a specific provider (e.g. github, stripe, openai)')
169
+ .action(rotateCommand);
170
+
171
+ // -----------------------------------------------------------------------------
172
+ // AGENT COMMAND
173
+ // -----------------------------------------------------------------------------
174
+ program
175
+ .command('agent [path]')
176
+ .description('AI-powered security audit: scan, classify with Claude, auto-remediate confirmed secrets')
177
+ .option('--dry-run', 'Show classification and plan without writing any files')
178
+ .option('--model <model>', `Claude model to use (default: ${DEFAULT_MODEL})`)
179
+ .action(agentCommand);
180
+
181
+ // -----------------------------------------------------------------------------
182
+ // DEPS COMMAND
183
+ // -----------------------------------------------------------------------------
184
+ program
185
+ .command('deps [path]')
186
+ .description('Audit dependencies for known CVEs (npm, yarn, pnpm, pip-audit, bundler-audit)')
187
+ .option('--fix', 'Run the package manager fix command after auditing')
188
+ .action(depsCommand);
189
+
190
+ // -----------------------------------------------------------------------------
191
+ // SCORE COMMAND
192
+ // -----------------------------------------------------------------------------
193
+ program
194
+ .command('score [path]')
195
+ .description('Compute a 0-100 security health score for your project')
196
+ .option('--no-deps', 'Skip dependency audit')
197
+ .action(scoreCommand);
198
+
199
+ // -----------------------------------------------------------------------------
200
+ // AUDIT COMMAND (v4.0 — Full Security Audit)
201
+ // -----------------------------------------------------------------------------
202
+ program
203
+ .command('audit [path]')
204
+ .description('Full security audit: secrets + 18 agents + deps + score + deep analysis + remediation plan')
205
+ .option('--json', 'Output results as JSON')
206
+ .option('--sarif', 'Output results in SARIF format')
207
+ .option('--csv', 'Output results as CSV')
208
+ .option('--md', 'Output results as Markdown')
209
+ .option('--html [file]', 'HTML report path (default: ship-safe-report.html)')
210
+ .option('--compare', 'Show detailed comparison with last scan')
211
+ .option('--timeout <ms>', 'Per-agent timeout in milliseconds (default: 30000)', parseInt)
212
+ .option('--no-deps', 'Skip dependency audit')
213
+ .option('--no-ai', 'Skip AI classification')
214
+ .option('--no-cache', 'Force full rescan (ignore cached results)')
215
+ .option('--baseline', 'Only show findings not in the baseline')
216
+ .option('--pdf [file]', 'Generate PDF report (requires Chrome/Chromium)')
217
+ .option('--deep', 'LLM-powered taint analysis for critical/high findings')
218
+ .option('--local', 'Use local Ollama model for deep analysis (default: llama3.2)')
219
+ .option('--model <model>', 'LLM model to use for deep/AI analysis')
220
+ .option('--provider <name>', 'LLM provider: anthropic, openai, google, ollama, groq, together, mistral, cohere, deepseek, xai, lmstudio')
221
+ .option('--base-url <url>', 'Custom OpenAI-compatible endpoint (e.g. http://localhost:1234/v1/chat/completions)')
222
+ .option('--budget <cents>', 'Max spend in cents for deep analysis (default: 50)', parseInt)
223
+ .option('--verify', 'Check if leaked secrets are still active (probes provider APIs)')
224
+ .option('--include-legal', 'Also run the legal risk scan (DMCA, leaked source, IP disputes)')
225
+ .option('-v, --verbose', 'Verbose output')
226
+ .action(auditCommand);
227
+
228
+ // -----------------------------------------------------------------------------
229
+ // DIFF COMMAND (v6.0 Scan only changed files)
230
+ // -----------------------------------------------------------------------------
231
+ program
232
+ .command('diff [ref]')
233
+ .description('Scan only changed files (git diff) — fast pre-commit & PR scanning')
234
+ .option('--staged', 'Scan only staged changes')
235
+ .option('--json', 'Output results as JSON')
236
+ .option('-p, --path <path>', 'Project path (default: cwd)')
237
+ .option('--timeout <ms>', 'Per-agent timeout in milliseconds (default: 30000)', parseInt)
238
+ .action(diffCommand);
239
+
240
+ // -----------------------------------------------------------------------------
241
+ // RED TEAM COMMAND (v4.0 Multi-Agent Security Audit)
242
+ // -----------------------------------------------------------------------------
243
+ program
244
+ .command('red-team [path]')
245
+ .description('Multi-agent security audit: 18 agents scan for 80+ attack classes')
246
+ .option('--agents <list>', 'Comma-separated list of agents to run')
247
+ .option('--json', 'Output results as JSON')
248
+ .option('--sarif', 'Output results in SARIF format')
249
+ .option('--html [file]', 'Generate HTML security report')
250
+ .option('--sbom [file]', 'Generate CycloneDX SBOM')
251
+ .option('--no-deps', 'Skip dependency audit')
252
+ .option('--no-ai', 'Skip AI classification')
253
+ .option('--deep', 'LLM-powered taint analysis for critical/high findings')
254
+ .option('--local', 'Use local Ollama model for deep analysis (default: llama3.2)')
255
+ .option('--model <model>', 'LLM model for deep analysis')
256
+ .option('--provider <name>', 'LLM provider: anthropic, openai, google, ollama, groq, together, mistral, cohere, deepseek, xai, lmstudio')
257
+ .option('--base-url <url>', 'Custom OpenAI-compatible endpoint (e.g. http://localhost:1234/v1/chat/completions)')
258
+ .option('--budget <cents>', 'Max spend in cents for deep analysis (default: 50)', parseInt)
259
+ .option('-v, --verbose', 'Verbose output')
260
+ .action(redTeamCommand);
261
+
262
+ // -----------------------------------------------------------------------------
263
+ // WATCH COMMAND
264
+ // -----------------------------------------------------------------------------
265
+ program
266
+ .command('watch [path]')
267
+ .description('Continuous monitoring: watch files for security issues in real-time')
268
+ .option('--poll', 'Use polling mode (for network drives)')
269
+ .option('--configs', 'Watch only agent config files (openclaw.json, .cursorrules, mcp.json, etc.)')
270
+ .action(watchCommand);
271
+
272
+ // -----------------------------------------------------------------------------
273
+ // SBOM COMMAND
274
+ // -----------------------------------------------------------------------------
275
+ program
276
+ .command('sbom [path]')
277
+ .description('Generate Software Bill of Materials (CycloneDX SBOM)')
278
+ .option('-o, --output <file>', 'Output file path', 'sbom.json')
279
+ .action((targetPath = '.', options) => {
280
+ const absolutePath = join(process.cwd(), targetPath);
281
+ const sbom = new SBOMGenerator();
282
+ sbom.generateToFile(absolutePath, options.output);
283
+ console.log(chalk.green(`✔ SBOM saved to ${options.output}`));
284
+ });
285
+
286
+ // -----------------------------------------------------------------------------
287
+ // POLICY COMMAND
288
+ // -----------------------------------------------------------------------------
289
+ program
290
+ .command('policy <action>')
291
+ .description('Manage security policies (init: create policy template)')
292
+ .action((action) => {
293
+ if (action === 'init') {
294
+ const policyPath = PolicyEngine.generateTemplate(process.cwd());
295
+ console.log(chalk.green(`✔ Policy template created: ${policyPath}`));
296
+ console.log(chalk.gray(' Edit .ship-safe.policy.json to configure your security policy.'));
297
+ } else {
298
+ console.log(chalk.yellow(`Unknown policy action: ${action}. Use: policy init`));
299
+ }
300
+ });
301
+
302
+ // -----------------------------------------------------------------------------
303
+ // BASELINE COMMAND (v4.3)
304
+ // -----------------------------------------------------------------------------
305
+ program
306
+ .command('baseline [path]')
307
+ .description('Create/manage a findings baseline — only report new findings on subsequent scans')
308
+ .option('--diff', 'Show what changed since baseline')
309
+ .option('--clear', 'Remove the baseline')
310
+ .action(baselineCommand);
311
+
312
+ // -----------------------------------------------------------------------------
313
+ // CI COMMAND (v5.0 CI/CD Pipeline Integration)
314
+ // -----------------------------------------------------------------------------
315
+ program
316
+ .command('ci [path]')
317
+ .description('CI/CD pipeline mode: scan, score, exit 1 on failure optimized for automation')
318
+ .option('--threshold <score>', 'Minimum passing score (default: 75)', parseInt)
319
+ .option('--fail-on <severity>', 'Fail on findings at this severity or above (critical, high, medium)')
320
+ .option('--sarif <file>', 'Write SARIF output for GitHub Code Scanning')
321
+ .option('--json', 'JSON output')
322
+ .option('--no-deps', 'Skip dependency audit')
323
+ .option('--baseline', 'Only check new findings (not in baseline)')
324
+ .option('--github-pr', 'Post findings as a GitHub PR comment (requires gh CLI)')
325
+ .action(ciCommand);
326
+
327
+ // -----------------------------------------------------------------------------
328
+ // VIBE CHECK COMMAND
329
+ // -----------------------------------------------------------------------------
330
+ program
331
+ .command('vibe-check [path]')
332
+ .description('Fun security check with emoji output, shareable score, and badge generator')
333
+ .option('--badge', 'Generate a shields.io markdown badge for your README')
334
+ .action(vibeCheckCommand);
335
+
336
+ // -----------------------------------------------------------------------------
337
+ // BENCHMARK COMMAND
338
+ // -----------------------------------------------------------------------------
339
+ program
340
+ .command('benchmark [path]')
341
+ .description('Compare your security score against industry averages')
342
+ .option('--json', 'Output results as JSON')
343
+ .action(benchmarkCommand);
344
+
345
+ // -----------------------------------------------------------------------------
346
+ // OPENCLAW COMMAND
347
+ // -----------------------------------------------------------------------------
348
+ program
349
+ .command('openclaw [path]')
350
+ .description('OpenClaw security scan: agent configs, MCP servers, skills, hooks')
351
+ .option('--fix', 'Auto-harden OpenClaw and agent configurations')
352
+ .option('--preflight', 'Exit non-zero on critical findings (for CI)')
353
+ .option('--red-team', 'Simulate adversarial attacks against agent configs')
354
+ .option('--json', 'Output results as JSON')
355
+ .action(openclawCommand);
356
+
357
+ // -----------------------------------------------------------------------------
358
+ // SCAN-SKILL COMMAND
359
+ // -----------------------------------------------------------------------------
360
+ program
361
+ .command('scan-skill [target]')
362
+ .description('Analyze an AI agent skill for security issues before installing it')
363
+ .option('--all', 'Scan all skills defined in openclaw.json')
364
+ .option('--json', 'Output results as JSON')
365
+ .action(scanSkillCommand);
366
+
367
+ // -----------------------------------------------------------------------------
368
+ // ABOM COMMAND
369
+ // -----------------------------------------------------------------------------
370
+ program
371
+ .command('abom [path]')
372
+ .description('Generate Agent Bill of Materials (CycloneDX ABOM) — MCP servers, skills, configs, LLM providers')
373
+ .option('-o, --output <file>', 'Output file path', 'abom.json')
374
+ .option('--json', 'Output to stdout as JSON')
375
+ .action(abomCommand);
376
+
377
+ // -----------------------------------------------------------------------------
378
+ // HOOKS COMMAND (Claude Code PreToolUse / PostToolUse integration)
379
+ // -----------------------------------------------------------------------------
380
+ program
381
+ .command('hooks [action]')
382
+ .description('Manage Claude Code hooks — real-time security gate on every Write, Edit, and Bash call')
383
+ .addHelpText('after', `
384
+ Actions:
385
+ install Register ship-safe as PreToolUse + PostToolUse hooks in ~/.claude/settings.json
386
+ remove Unregister ship-safe hooks
387
+ status Show whether hooks are installed
388
+
389
+ How it works:
390
+ PreToolUse — blocks Write/Edit if critical secrets are in the new content;
391
+ blocks dangerous Bash patterns (curl|bash, credential exfiltration)
392
+ PostToolUse scans the file after it is written; injects advisory findings
393
+ into Claude's context so issues are caught immediately
394
+ `)
395
+ .action(hooksCommand);
396
+
397
+ // -----------------------------------------------------------------------------
398
+ // LEGAL COMMAND
399
+ // -----------------------------------------------------------------------------
400
+ program
401
+ .command('legal [path]')
402
+ .description('Legal risk audit: DMCA notices, leaked-source derivatives, IP disputes in dependencies')
403
+ .option('--json', 'Output results as JSON')
404
+ .action(legalCommand);
405
+
406
+ // -----------------------------------------------------------------------------
407
+ // UPDATE-INTEL COMMAND
408
+ // -----------------------------------------------------------------------------
409
+ program
410
+ .command('update-intel')
411
+ .description('Update threat intelligence feed (malicious skill hashes, compromised MCP servers)')
412
+ .option('--url <url>', 'Custom feed URL')
413
+ .action(updateIntelCommand);
414
+
415
+ // -----------------------------------------------------------------------------
416
+ // DOCTOR COMMAND
417
+ // -----------------------------------------------------------------------------
418
+ program
419
+ .command('doctor')
420
+ .description('Diagnose environment: check Node.js, git, API keys, cache, and dependencies')
421
+ .action(doctorCommand);
422
+
423
+ // -----------------------------------------------------------------------------
424
+ // PARSE AND RUN
425
+ // -----------------------------------------------------------------------------
426
+
427
+ // Show help if no command provided
428
+ if (process.argv.length === 2) {
429
+ console.log(banner);
430
+ console.log(chalk.yellow('\nQuick start:\n'));
431
+ console.log(chalk.cyan.bold(' v6.0 — Full Security Audit'));
432
+ console.log(chalk.white(' npx ship-safe audit . ') + chalk.gray('# Full audit: secrets + 18 agents + deps + remediation'));
433
+ console.log(chalk.white(' npx ship-safe audit . --deep') + chalk.gray('# LLM-powered taint analysis (Anthropic/Ollama)'));
434
+ console.log(chalk.white(' npx ship-safe red-team . ') + chalk.gray('# 18-agent red team scan (80+ attack classes)'));
435
+ console.log(chalk.white(' npx ship-safe vibe-check . ') + chalk.gray('# Fun security check with emoji & shareable badge'));
436
+ console.log(chalk.white(' npx ship-safe benchmark . ') + chalk.gray('# Compare score against industry averages'));
437
+ console.log(chalk.white(' npx ship-safe ci . ') + chalk.gray('# CI/CD mode: scan, score, exit code'));
438
+ console.log(chalk.white(' npx ship-safe diff ') + chalk.gray('# Scan only changed files (fast pre-commit)'));
439
+ console.log(chalk.white(' npx ship-safe watch . ') + chalk.gray('# Continuous monitoring mode'));
440
+ console.log(chalk.white(' npx ship-safe openclaw . ') + chalk.gray('# OpenClaw & agent config security scan'));
441
+ console.log(chalk.white(' npx ship-safe scan-skill <u>') + chalk.gray('# Vet a skill before installing'));
442
+ console.log(chalk.white(' npx ship-safe abom . ') + chalk.gray('# Agent Bill of Materials (CycloneDX)'));
443
+ console.log(chalk.white(' npx ship-safe sbom . ') + chalk.gray('# Generate CycloneDX SBOM (CRA-ready)'));
444
+ console.log(chalk.white(' npx ship-safe legal . ') + chalk.gray('# Legal risk audit: DMCA, leaked source, IP disputes'));
445
+ console.log(chalk.white(' npx ship-safe update-intel ') + chalk.gray('# Update threat intelligence feed'));
446
+ console.log(chalk.white(' npx ship-safe policy init ') + chalk.gray('# Create security policy template'));
447
+ console.log(chalk.white(' npx ship-safe doctor ') + chalk.gray('# Check environment and configuration'));
448
+ console.log();
449
+ console.log(chalk.gray(' Core commands:'));
450
+ console.log(chalk.white(' npx ship-safe agent . ') + chalk.gray('# AI audit: scan + classify + auto-fix'));
451
+ console.log(chalk.white(' npx ship-safe scan . ') + chalk.gray('# Scan for secrets'));
452
+ console.log(chalk.white(' npx ship-safe remediate . ') + chalk.gray('# Auto-fix: rewrite code + write .env'));
453
+ console.log(chalk.white(' npx ship-safe rotate . ') + chalk.gray('# Revoke exposed keys (provider guides)'));
454
+ console.log(chalk.white(' npx ship-safe deps . ') + chalk.gray('# Audit dependencies for CVEs'));
455
+ console.log(chalk.white(' npx ship-safe score . ') + chalk.gray('# Security health score (0-100)'));
456
+ console.log(chalk.white(' npx ship-safe hooks install ') + chalk.gray('# Real-time security gate inside Claude Code (PreToolUse/PostToolUse)'));
457
+ console.log(chalk.white(' npx ship-safe guard ') + chalk.gray('# Block git push if secrets found'));
458
+ console.log(chalk.white(' npx ship-safe init ') + chalk.gray('# Add security configs to your project'));
459
+ console.log(chalk.white('\n npx ship-safe --help ') + chalk.gray('# Show all options'));
460
+ console.log();
461
+ process.exit(0);
462
+ }
463
+
464
+ program.parse();