@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.
- package/README.md +29 -10
- package/dist/commands/agent/audit-skill.js +22 -20
- package/dist/commands/agent/audit.js +27 -0
- package/dist/commands/agent/components.js +800 -0
- package/dist/commands/agent/config.js +2 -1
- package/dist/commands/agent/disable.js +47 -0
- package/dist/commands/agent/enable.js +50 -0
- package/dist/commands/agent/exec.js +2 -0
- package/dist/commands/agent/index.js +6 -0
- package/dist/commands/agent/init.js +162 -163
- package/dist/commands/agent/install-hook.js +15 -14
- package/dist/commands/agent/list.js +72 -0
- package/dist/commands/agent/scan.js +4 -3
- package/dist/commands/agent/verify.js +1 -1
- package/dist/commands/backend/run.js +12 -3
- package/dist/commands/backend/scan-status.js +3 -2
- package/dist/commands/brief.js +22 -2
- package/dist/commands/ci/init.js +25 -21
- package/dist/commands/completion.js +4 -3
- package/dist/commands/docs/index.js +18 -0
- package/dist/commands/docs/list.js +37 -0
- package/dist/commands/docs/show.js +64 -0
- package/dist/commands/mcp/server.js +84 -0
- package/dist/commands/report.js +42 -41
- package/dist/commands/scan/index.js +7 -5
- package/dist/commands/skill/index.js +14 -0
- package/dist/commands/skill/install.js +89 -0
- package/dist/commands/skill/list.js +79 -0
- package/dist/commands/skill/registry.js +273 -0
- package/dist/commands/skill/remote.js +333 -0
- package/dist/commands/skill/review.js +975 -0
- package/dist/commands/skill/uninstall.js +65 -0
- package/dist/core/audit-logger.js +262 -21
- package/dist/core/config-manager.js +3 -0
- package/dist/core/docs-loader.js +148 -0
- package/dist/core/policy-loader.js +72 -1
- package/dist/core/risk-rules.js +16 -3
- package/dist/index.js +19 -9
- package/dist/scanners/gitleaks.js +6 -2
- package/package.json +1 -1
- package/resources/skills/rafter/SKILL.md +77 -97
- package/resources/skills/rafter/docs/backend.md +106 -0
- package/resources/skills/rafter/docs/cli-reference.md +199 -0
- package/resources/skills/rafter/docs/finding-triage.md +79 -0
- package/resources/skills/rafter/docs/guardrails.md +91 -0
- package/resources/skills/rafter/docs/shift-left.md +64 -0
- package/resources/skills/rafter-agent-security/SKILL.md +1 -1
- package/resources/skills/rafter-code-review/SKILL.md +91 -0
- package/resources/skills/rafter-code-review/docs/api.md +90 -0
- package/resources/skills/rafter-code-review/docs/asvs.md +120 -0
- package/resources/skills/rafter-code-review/docs/cwe-top25.md +78 -0
- package/resources/skills/rafter-code-review/docs/investigation-playbook.md +101 -0
- package/resources/skills/rafter-code-review/docs/llm.md +87 -0
- package/resources/skills/rafter-code-review/docs/web-app.md +84 -0
- package/resources/skills/rafter-secure-design/SKILL.md +103 -0
- package/resources/skills/rafter-secure-design/docs/api-design.md +97 -0
- package/resources/skills/rafter-secure-design/docs/auth.md +67 -0
- package/resources/skills/rafter-secure-design/docs/data-storage.md +90 -0
- package/resources/skills/rafter-secure-design/docs/dependencies.md +101 -0
- package/resources/skills/rafter-secure-design/docs/deployment.md +104 -0
- package/resources/skills/rafter-secure-design/docs/ingestion.md +98 -0
- package/resources/skills/rafter-secure-design/docs/standards-pointers.md +102 -0
- package/resources/skills/rafter-secure-design/docs/threat-modeling.md +128 -0
- package/resources/skills/rafter-skill-review/SKILL.md +106 -0
- package/resources/skills/rafter-skill-review/docs/authorship-provenance.md +82 -0
- package/resources/skills/rafter-skill-review/docs/changelog-review.md +99 -0
- package/resources/skills/rafter-skill-review/docs/data-practices.md +88 -0
- package/resources/skills/rafter-skill-review/docs/malware-indicators.md +79 -0
- package/resources/skills/rafter-skill-review/docs/prompt-injection.md +85 -0
- 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
|
|
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
|
|
39
|
+
**Note**: Agent security features (secret scanning, command execution) work **without an API key**. Only remote code analysis requires authentication.
|
|
40
40
|
|
|
41
|
-
###
|
|
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-
|
|
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
|
|
516
|
-
rafter ci init --with-
|
|
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.
|
|
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
|
-
- **
|
|
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
|
|
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(`
|
|
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(
|
|
29
|
-
console.log(
|
|
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("
|
|
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("
|
|
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("
|
|
84
|
-
console.log(
|
|
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(
|
|
88
|
+
console.log(fmt.divider());
|
|
87
89
|
console.log(generateManualReviewPrompt(skillName, absolutePath, quickScan, skillContent));
|
|
88
|
-
console.log(
|
|
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("
|
|
138
|
-
console.log(
|
|
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("
|
|
143
|
+
console.log(fmt.success("Secrets: None detected"));
|
|
142
144
|
}
|
|
143
145
|
else {
|
|
144
|
-
console.log(
|
|
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("
|
|
152
|
+
console.log(fmt.success("External URLs: None"));
|
|
151
153
|
}
|
|
152
154
|
else {
|
|
153
|
-
console.log(
|
|
155
|
+
console.log(fmt.warning(`External URLs: ${scan.urls.length} found`));
|
|
154
156
|
scan.urls.slice(0, 5).forEach(url => {
|
|
155
|
-
console.log(`
|
|
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("
|
|
165
|
+
console.log(fmt.success("High-risk commands: None detected"));
|
|
164
166
|
}
|
|
165
167
|
else {
|
|
166
|
-
console.log(
|
|
168
|
+
console.log(fmt.warning(`High-risk commands: ${scan.highRiskCommands.length} found`));
|
|
167
169
|
scan.highRiskCommands.slice(0, 5).forEach(cmd => {
|
|
168
|
-
console.log(`
|
|
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
|
}
|