@rafter-security/cli 0.6.6 → 0.7.1

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 (70) hide show
  1. package/README.md +29 -10
  2. package/dist/commands/agent/audit-skill.js +22 -20
  3. package/dist/commands/agent/audit.js +27 -0
  4. package/dist/commands/agent/components.js +800 -0
  5. package/dist/commands/agent/config.js +2 -1
  6. package/dist/commands/agent/disable.js +47 -0
  7. package/dist/commands/agent/enable.js +50 -0
  8. package/dist/commands/agent/exec.js +2 -0
  9. package/dist/commands/agent/index.js +6 -0
  10. package/dist/commands/agent/init.js +162 -163
  11. package/dist/commands/agent/install-hook.js +15 -14
  12. package/dist/commands/agent/list.js +72 -0
  13. package/dist/commands/agent/scan.js +4 -3
  14. package/dist/commands/agent/verify.js +1 -1
  15. package/dist/commands/backend/run.js +12 -3
  16. package/dist/commands/backend/scan-status.js +3 -2
  17. package/dist/commands/brief.js +22 -2
  18. package/dist/commands/ci/init.js +25 -21
  19. package/dist/commands/completion.js +4 -3
  20. package/dist/commands/docs/index.js +18 -0
  21. package/dist/commands/docs/list.js +37 -0
  22. package/dist/commands/docs/show.js +64 -0
  23. package/dist/commands/mcp/server.js +84 -0
  24. package/dist/commands/report.js +42 -41
  25. package/dist/commands/scan/index.js +7 -5
  26. package/dist/commands/skill/index.js +14 -0
  27. package/dist/commands/skill/install.js +89 -0
  28. package/dist/commands/skill/list.js +79 -0
  29. package/dist/commands/skill/registry.js +273 -0
  30. package/dist/commands/skill/remote.js +333 -0
  31. package/dist/commands/skill/review.js +975 -0
  32. package/dist/commands/skill/uninstall.js +65 -0
  33. package/dist/core/audit-logger.js +262 -21
  34. package/dist/core/config-manager.js +3 -0
  35. package/dist/core/docs-loader.js +148 -0
  36. package/dist/core/policy-loader.js +72 -1
  37. package/dist/core/risk-rules.js +16 -3
  38. package/dist/index.js +19 -9
  39. package/dist/scanners/gitleaks.js +6 -2
  40. package/package.json +1 -1
  41. package/resources/skills/rafter/SKILL.md +77 -97
  42. package/resources/skills/rafter/docs/backend.md +106 -0
  43. package/resources/skills/rafter/docs/cli-reference.md +199 -0
  44. package/resources/skills/rafter/docs/finding-triage.md +79 -0
  45. package/resources/skills/rafter/docs/guardrails.md +91 -0
  46. package/resources/skills/rafter/docs/shift-left.md +64 -0
  47. package/resources/skills/rafter-agent-security/SKILL.md +1 -1
  48. package/resources/skills/rafter-code-review/SKILL.md +91 -0
  49. package/resources/skills/rafter-code-review/docs/api.md +90 -0
  50. package/resources/skills/rafter-code-review/docs/asvs.md +120 -0
  51. package/resources/skills/rafter-code-review/docs/cwe-top25.md +78 -0
  52. package/resources/skills/rafter-code-review/docs/investigation-playbook.md +101 -0
  53. package/resources/skills/rafter-code-review/docs/llm.md +87 -0
  54. package/resources/skills/rafter-code-review/docs/web-app.md +84 -0
  55. package/resources/skills/rafter-secure-design/SKILL.md +103 -0
  56. package/resources/skills/rafter-secure-design/docs/api-design.md +97 -0
  57. package/resources/skills/rafter-secure-design/docs/auth.md +67 -0
  58. package/resources/skills/rafter-secure-design/docs/data-storage.md +90 -0
  59. package/resources/skills/rafter-secure-design/docs/dependencies.md +101 -0
  60. package/resources/skills/rafter-secure-design/docs/deployment.md +104 -0
  61. package/resources/skills/rafter-secure-design/docs/ingestion.md +98 -0
  62. package/resources/skills/rafter-secure-design/docs/standards-pointers.md +102 -0
  63. package/resources/skills/rafter-secure-design/docs/threat-modeling.md +128 -0
  64. package/resources/skills/rafter-skill-review/SKILL.md +106 -0
  65. package/resources/skills/rafter-skill-review/docs/authorship-provenance.md +82 -0
  66. package/resources/skills/rafter-skill-review/docs/changelog-review.md +99 -0
  67. package/resources/skills/rafter-skill-review/docs/data-practices.md +88 -0
  68. package/resources/skills/rafter-skill-review/docs/malware-indicators.md +79 -0
  69. package/resources/skills/rafter-skill-review/docs/prompt-injection.md +85 -0
  70. package/resources/skills/rafter-skill-review/docs/telemetry.md +78 -0
package/README.md CHANGED
@@ -23,7 +23,7 @@ yarn global add @rafter-security/cli
23
23
 
24
24
  ### Getting an API Key
25
25
 
26
- To use backend code analysis features, you'll need a Rafter API key:
26
+ To use remote code analysis features, you'll need a Rafter API key:
27
27
 
28
28
  1. **Sign up**: Create an account at [rafter.so](https://rafter.so)
29
29
  2. **Get API key**: Navigate to Dashboard → Settings → API Keys
@@ -36,9 +36,9 @@ To use backend code analysis features, you'll need a Rafter API key:
36
36
  echo "RAFTER_API_KEY=your-api-key-here" >> .env
37
37
  ```
38
38
 
39
- **Note**: Agent security features (secret scanning, command execution) work **without an API key**. Only backend code analysis requires authentication.
39
+ **Note**: Agent security features (secret scanning, command execution) work **without an API key**. Only remote code analysis requires authentication.
40
40
 
41
- ### Backend Code Analysis
41
+ ### Remote Code Analysis
42
42
 
43
43
  ```bash
44
44
  # Set your API key (from above)
@@ -59,20 +59,39 @@ rafter usage
59
59
  ```bash
60
60
  # Initialize local security
61
61
  rafter agent init
62
+ rafter agent init --local # write config to ./.rafter (ephemeral/benchmark)
63
+
64
+ # Granular per-component control
65
+ rafter agent list
66
+ rafter agent enable claude-code
67
+ rafter agent disable gemini
62
68
 
63
69
  # Scan files for secrets
64
70
  rafter agent scan .
71
+ rafter agent scan --history # full git history (gitleaks engine)
65
72
 
66
73
  # Execute commands safely
67
74
  rafter agent exec "git commit -m 'Add feature'"
68
75
 
69
- # View audit logs
76
+ # View audit logs (tamper-evident hash chain)
70
77
  rafter agent audit
78
+ rafter agent audit --verify # verify chain; exit 1 if tampered
71
79
 
72
80
  # Manage configuration
73
81
  rafter agent config show
74
82
  ```
75
83
 
84
+ ### Skills
85
+
86
+ Four first-party skills ship with the CLI: `rafter` (CYOA router), `rafter-code-review`, `rafter-secure-design`, `rafter-skill-review`.
87
+
88
+ ```bash
89
+ rafter skill list # installed + available
90
+ rafter skill install --all # install all four
91
+ rafter skill review github:owner/repo # audit a third-party skill before install
92
+ rafter skill review --installed # audit every skill already on disk
93
+ ```
94
+
76
95
  ## Global Options
77
96
 
78
97
  | Flag | Description |
@@ -500,7 +519,7 @@ Generate CI/CD pipeline configuration for secret scanning.
500
519
  **Options:**
501
520
  - `--platform <platform>` - CI platform: `github`, `gitlab`, `circleci` (default: auto-detect)
502
521
  - `--output <path>` - Output file path (default: platform-specific)
503
- - `--with-backend` - Include backend security audit job (requires `RAFTER_API_KEY`)
522
+ - `--with-remote` - Include remote security audit job (requires `RAFTER_API_KEY`)
504
523
 
505
524
  **Auto-detection:** Checks for `.github/`, `.gitlab-ci.yml`, `.circleci/` in the current directory.
506
525
 
@@ -512,8 +531,8 @@ rafter ci init
512
531
  # Generate GitHub Actions workflow
513
532
  rafter ci init --platform github
514
533
 
515
- # Include backend scanning job
516
- rafter ci init --with-backend
534
+ # Include remote scanning job
535
+ rafter ci init --with-remote
517
536
 
518
537
  # Custom output path
519
538
  rafter ci init --output .github/workflows/security.yml
@@ -624,7 +643,7 @@ When OpenClaw is detected, `rafter agent init` automatically installs a skill to
624
643
 
625
644
  Rafter provides TWO skills for Claude Code:
626
645
 
627
- ### 1. Backend Code Analysis Skill (Core Feature)
646
+ ### 1. Remote Code Analysis Skill (Core Feature)
628
647
 
629
648
  **Automatic Integration** - Claude can proactively suggest security scans
630
649
 
@@ -680,10 +699,10 @@ Explicitly invoke commands:
680
699
 
681
700
  ### Why Two Skills?
682
701
 
683
- - **Backend code analysis skill** - Safe for Claude to auto-invoke (read-only API calls)
702
+ - **Remote code analysis skill** - Safe for Claude to auto-invoke (read-only API calls)
684
703
  - **Agent security skill** - Requires user permission (local file access, command execution)
685
704
 
686
- This separation emphasizes Rafter's core backend code analysis capabilities while keeping local security features safely behind user control.
705
+ This separation emphasizes Rafter's core remote code analysis capabilities while keeping local security features safely behind user control.
687
706
 
688
707
  ## Documentation
689
708
 
@@ -4,20 +4,22 @@ import path from "path";
4
4
  import { PatternEngine } from "../../core/pattern-engine.js";
5
5
  import { DEFAULT_SECRET_PATTERNS } from "../../scanners/secret-patterns.js";
6
6
  import { SkillManager } from "../../utils/skill-manager.js";
7
+ import { fmt } from "../../utils/formatter.js";
7
8
  export function createAuditSkillCommand() {
8
9
  return new Command("audit-skill")
9
- .description("Security audit of a Claude Code skill file")
10
+ .description("[deprecated] Security audit of a Claude Code skill file — use `rafter skill review` instead")
10
11
  .argument("<skill-path>", "Path to skill file to audit")
11
12
  .option("--skip-openclaw", "Skip OpenClaw integration, show manual review prompt")
12
13
  .option("--json", "Output results as JSON")
13
14
  .action(async (skillPath, opts) => {
15
+ process.stderr.write("[deprecated] `rafter agent audit-skill` is deprecated; use `rafter skill review <path-or-url>` instead.\n");
14
16
  await auditSkill(skillPath, opts);
15
17
  });
16
18
  }
17
19
  async function auditSkill(skillPath, opts) {
18
20
  // Validate skill file exists
19
21
  if (!fs.existsSync(skillPath)) {
20
- console.error(`Error: Skill file not found: ${skillPath}`);
22
+ console.error(fmt.error(`Skill file not found: ${skillPath}`));
21
23
  process.exit(2);
22
24
  }
23
25
  const absolutePath = path.resolve(skillPath);
@@ -25,8 +27,8 @@ async function auditSkill(skillPath, opts) {
25
27
  const skillName = path.basename(absolutePath);
26
28
  // Run deterministic analysis
27
29
  if (!opts.json) {
28
- console.log(`\n🔍 Auditing skill: ${skillName}\n`);
29
- console.log("═".repeat(60));
30
+ console.log(fmt.header(`Auditing skill: ${skillName}`));
31
+ console.log(fmt.divider());
30
32
  console.log("Running quick security scan...\n");
31
33
  }
32
34
  const quickScan = await runQuickScan(skillContent);
@@ -56,11 +58,11 @@ async function auditSkill(skillPath, opts) {
56
58
  // Check if we can use OpenClaw
57
59
  if (openClawAvailable && !opts.skipOpenclaw) {
58
60
  if (!rafterSkillInstalled) {
59
- console.log("\n⚠️ Rafter Security skill not installed in OpenClaw.");
61
+ console.log(`\n${fmt.warning("Rafter Security skill not installed in OpenClaw.")}`);
60
62
  console.log(" Run: rafter agent init\n");
61
63
  }
62
64
  else {
63
- console.log("\n🤖 For comprehensive security review:\n");
65
+ console.log(`\n${fmt.info("For comprehensive security review:")}\n`);
64
66
  console.log(" 1. Open OpenClaw");
65
67
  console.log(` 2. Run: /rafter-audit-skill ${absolutePath}`);
66
68
  console.log("\n The auditor will analyze:");
@@ -80,12 +82,12 @@ async function auditSkill(skillPath, opts) {
80
82
  }
81
83
  else {
82
84
  // OpenClaw not available or skipped - show manual review prompt
83
- console.log("\n📋 Manual Security Review Prompt\n");
84
- console.log("═".repeat(60));
85
+ console.log(fmt.header("Manual Security Review Prompt"));
86
+ console.log(fmt.divider());
85
87
  console.log("\nCopy the following to your AI assistant for review:\n");
86
- console.log("─".repeat(60));
88
+ console.log(fmt.divider());
87
89
  console.log(generateManualReviewPrompt(skillName, absolutePath, quickScan, skillContent));
88
- console.log("─".repeat(60));
90
+ console.log(fmt.divider());
89
91
  }
90
92
  console.log();
91
93
  if (quickScan.secrets > 0 || quickScan.highRiskCommands.length > 0) {
@@ -134,25 +136,25 @@ async function runQuickScan(content) {
134
136
  };
135
137
  }
136
138
  function displayQuickScan(scan, skillName) {
137
- console.log("📊 Quick Scan Results");
138
- console.log("═".repeat(60));
139
+ console.log(fmt.header("Quick Scan Results"));
140
+ console.log(fmt.divider());
139
141
  // Secrets
140
142
  if (scan.secrets === 0) {
141
- console.log("Secrets: None detected");
143
+ console.log(fmt.success("Secrets: None detected"));
142
144
  }
143
145
  else {
144
- console.log(`⚠️ Secrets: ${scan.secrets} found`);
146
+ console.log(fmt.warning(`Secrets: ${scan.secrets} found`));
145
147
  console.log(" → API keys, tokens, or credentials detected");
146
148
  console.log(" → Run: rafter scan local <path> for details");
147
149
  }
148
150
  // URLs
149
151
  if (scan.urls.length === 0) {
150
- console.log("External URLs: None");
152
+ console.log(fmt.success("External URLs: None"));
151
153
  }
152
154
  else {
153
- console.log(`⚠️ External URLs: ${scan.urls.length} found`);
155
+ console.log(fmt.warning(`External URLs: ${scan.urls.length} found`));
154
156
  scan.urls.slice(0, 5).forEach(url => {
155
- console.log(` ${url}`);
157
+ console.log(` - ${url}`);
156
158
  });
157
159
  if (scan.urls.length > 5) {
158
160
  console.log(` ... and ${scan.urls.length - 5} more`);
@@ -160,12 +162,12 @@ function displayQuickScan(scan, skillName) {
160
162
  }
161
163
  // High-risk commands
162
164
  if (scan.highRiskCommands.length === 0) {
163
- console.log("High-risk commands: None detected");
165
+ console.log(fmt.success("High-risk commands: None detected"));
164
166
  }
165
167
  else {
166
- console.log(`⚠️ High-risk commands: ${scan.highRiskCommands.length} found`);
168
+ console.log(fmt.warning(`High-risk commands: ${scan.highRiskCommands.length} found`));
167
169
  scan.highRiskCommands.slice(0, 5).forEach(cmd => {
168
- console.log(` ${cmd.command} (line ${cmd.line})`);
170
+ console.log(` - ${cmd.command} (line ${cmd.line})`);
169
171
  });
170
172
  if (scan.highRiskCommands.length > 5) {
171
173
  console.log(` ... and ${scan.highRiskCommands.length - 5} more`);
@@ -13,13 +13,28 @@ export function createAuditCommand() {
13
13
  .option("--event <type>", "Filter by event type")
14
14
  .option("--agent <type>", "Filter by agent type (openclaw, claude-code)")
15
15
  .option("--since <date>", "Show entries since date (YYYY-MM-DD)")
16
+ .option("--repo <pattern>", "Filter by git repo path (substring match)")
17
+ .option("--cwd <pattern>", "Filter by working directory (substring match)")
16
18
  .option("--share", "Generate a redacted excerpt for issue reports")
19
+ .option("--verify", "Verify the audit log hash chain and report tampering")
17
20
  .action((opts) => {
18
21
  if (opts.share) {
19
22
  generateShareExcerpt();
20
23
  return;
21
24
  }
22
25
  const logger = new AuditLogger();
26
+ if (opts.verify) {
27
+ const breaks = logger.verify();
28
+ if (breaks.length === 0) {
29
+ console.log("✓ Audit log hash chain intact");
30
+ return;
31
+ }
32
+ console.error(`✗ Audit log hash chain broken (${breaks.length} break${breaks.length === 1 ? "" : "s"}):`);
33
+ for (const b of breaks) {
34
+ console.error(` line ${b.line}: ${b.reason}`);
35
+ }
36
+ process.exit(1);
37
+ }
23
38
  const filter = {
24
39
  limit: parseInt(opts.last, 10)
25
40
  };
@@ -32,6 +47,12 @@ export function createAuditCommand() {
32
47
  if (opts.since) {
33
48
  filter.since = new Date(opts.since);
34
49
  }
50
+ if (opts.repo) {
51
+ filter.gitRepo = opts.repo;
52
+ }
53
+ if (opts.cwd) {
54
+ filter.cwd = opts.cwd;
55
+ }
35
56
  const entries = logger.read(filter);
36
57
  if (entries.length === 0) {
37
58
  console.log("No audit log entries found");
@@ -46,6 +67,12 @@ export function createAuditCommand() {
46
67
  if (entry.agentType) {
47
68
  console.log(` Agent: ${entry.agentType}`);
48
69
  }
70
+ if (entry.gitRepo) {
71
+ console.log(` Repo: ${entry.gitRepo}`);
72
+ }
73
+ else if (entry.cwd) {
74
+ console.log(` Cwd: ${entry.cwd}`);
75
+ }
49
76
  if (entry.action?.command) {
50
77
  console.log(` Command: ${entry.action.command}`);
51
78
  }