@rafter-security/cli 0.7.3 → 0.7.6
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/dist/commands/agent/audit-skill.js +1 -1
- package/dist/commands/agent/components.js +58 -0
- package/dist/commands/agent/exec.js +1 -1
- package/dist/commands/agent/init.js +92 -2
- package/dist/commands/agent/instruction-block.js +18 -19
- package/dist/commands/agent/scan.js +12 -1
- package/dist/commands/brief.js +2 -2
- package/dist/commands/ci/init.js +3 -3
- package/dist/commands/hook/pretool.js +1 -1
- package/dist/commands/report.js +1 -1
- package/dist/commands/scan/index.js +6 -5
- package/dist/index.js +3 -0
- package/package.json +1 -1
- package/resources/pre-commit-hook.sh +1 -1
- package/resources/pre-push-hook.sh +2 -2
- package/resources/rafter-security-skill.md +2 -2
- package/resources/skills/rafter/SKILL.md +20 -20
- package/resources/skills/rafter/docs/backend.md +3 -3
- package/resources/skills/rafter/docs/cli-reference.md +6 -8
- package/resources/skills/rafter/docs/shift-left.md +1 -1
- package/resources/skills/rafter-code-review/SKILL.md +3 -3
- package/resources/skills/rafter-code-review/docs/cwe-top25.md +1 -1
- package/resources/skills/rafter-code-review/docs/web-app.md +1 -1
- package/resources/skills/rafter-secure-design/SKILL.md +2 -2
- package/resources/skills/rafter-skill-review/SKILL.md +2 -2
|
@@ -145,7 +145,7 @@ function displayQuickScan(scan, skillName) {
|
|
|
145
145
|
else {
|
|
146
146
|
console.log(fmt.warning(`Secrets: ${scan.secrets} found`));
|
|
147
147
|
console.log(" → API keys, tokens, or credentials detected");
|
|
148
|
-
console.log(" → Run: rafter
|
|
148
|
+
console.log(" → Run: rafter secrets <path> for details");
|
|
149
149
|
}
|
|
150
150
|
// URLs
|
|
151
151
|
if (scan.urls.length === 0) {
|
|
@@ -104,6 +104,12 @@ function claudeCodeHooks() {
|
|
|
104
104
|
const post = { type: "command", command: "rafter hook posttool" };
|
|
105
105
|
settings.hooks.PreToolUse = filterOutRafter(settings.hooks.PreToolUse, (e) => hookEntryMatchesRafter(e, "rafter hook pretool"));
|
|
106
106
|
settings.hooks.PostToolUse = filterOutRafter(settings.hooks.PostToolUse, (e) => hookEntryMatchesRafter(e, "rafter hook posttool"));
|
|
107
|
+
// Strip legacy SessionStart entry from <=0.7.4 installs.
|
|
108
|
+
if (Array.isArray(settings.hooks.SessionStart)) {
|
|
109
|
+
settings.hooks.SessionStart = filterOutRafter(settings.hooks.SessionStart, (e) => hookEntryMatchesRafter(e, "rafter hook session-start"));
|
|
110
|
+
if (settings.hooks.SessionStart.length === 0)
|
|
111
|
+
delete settings.hooks.SessionStart;
|
|
112
|
+
}
|
|
107
113
|
settings.hooks.PreToolUse.push({ matcher: "Bash", hooks: [pre] }, { matcher: "Write|Edit", hooks: [pre] });
|
|
108
114
|
settings.hooks.PostToolUse.push({ matcher: ".*", hooks: [post] });
|
|
109
115
|
writeJson(settingsPath, settings);
|
|
@@ -118,6 +124,11 @@ function claudeCodeHooks() {
|
|
|
118
124
|
if (settings.hooks?.PostToolUse) {
|
|
119
125
|
settings.hooks.PostToolUse = filterOutRafter(settings.hooks.PostToolUse, (e) => hookEntryMatchesRafter(e, "rafter hook posttool"));
|
|
120
126
|
}
|
|
127
|
+
if (Array.isArray(settings.hooks?.SessionStart)) {
|
|
128
|
+
settings.hooks.SessionStart = filterOutRafter(settings.hooks.SessionStart, (e) => hookEntryMatchesRafter(e, "rafter hook session-start"));
|
|
129
|
+
if (settings.hooks.SessionStart.length === 0)
|
|
130
|
+
delete settings.hooks.SessionStart;
|
|
131
|
+
}
|
|
121
132
|
writeJson(settingsPath, settings);
|
|
122
133
|
},
|
|
123
134
|
};
|
|
@@ -266,6 +277,52 @@ function codexHooks() {
|
|
|
266
277
|
},
|
|
267
278
|
};
|
|
268
279
|
}
|
|
280
|
+
/**
|
|
281
|
+
* Project-scope Claude Code MCP config (<cwd>/.mcp.json). Unlike other
|
|
282
|
+
* claude-code components which touch ~/.claude, this one writes at the
|
|
283
|
+
* project root — Claude Code auto-loads it on startup and exposes
|
|
284
|
+
* `mcp__rafter__*` tools to the agent.
|
|
285
|
+
*/
|
|
286
|
+
function claudeCodeMcp() {
|
|
287
|
+
const home = os.homedir();
|
|
288
|
+
const mcpPath = path.join(process.cwd(), ".mcp.json");
|
|
289
|
+
return {
|
|
290
|
+
id: "claude-code.mcp",
|
|
291
|
+
platform: "claude-code",
|
|
292
|
+
kind: "mcp",
|
|
293
|
+
description: "Claude Code project-scope MCP server (<project>/.mcp.json)",
|
|
294
|
+
detectDir: path.join(home, ".claude"),
|
|
295
|
+
path: mcpPath,
|
|
296
|
+
isInstalled: () => {
|
|
297
|
+
if (!fs.existsSync(mcpPath))
|
|
298
|
+
return false;
|
|
299
|
+
const cfg = readJson(mcpPath);
|
|
300
|
+
return !!cfg.mcpServers?.rafter;
|
|
301
|
+
},
|
|
302
|
+
install: () => {
|
|
303
|
+
const cfg = fs.existsSync(mcpPath) ? readJson(mcpPath) : {};
|
|
304
|
+
cfg.mcpServers ?? (cfg.mcpServers = {});
|
|
305
|
+
cfg.mcpServers.rafter = { ...RAFTER_MCP_ENTRY };
|
|
306
|
+
writeJson(mcpPath, cfg);
|
|
307
|
+
},
|
|
308
|
+
uninstall: () => {
|
|
309
|
+
if (!fs.existsSync(mcpPath))
|
|
310
|
+
return;
|
|
311
|
+
const cfg = readJson(mcpPath);
|
|
312
|
+
if (!removeKey(cfg.mcpServers, "rafter"))
|
|
313
|
+
return;
|
|
314
|
+
if (cfg.mcpServers && Object.keys(cfg.mcpServers).length === 0) {
|
|
315
|
+
delete cfg.mcpServers;
|
|
316
|
+
}
|
|
317
|
+
if (Object.keys(cfg).length === 0) {
|
|
318
|
+
fs.unlinkSync(mcpPath);
|
|
319
|
+
}
|
|
320
|
+
else {
|
|
321
|
+
writeJson(mcpPath, cfg);
|
|
322
|
+
}
|
|
323
|
+
},
|
|
324
|
+
};
|
|
325
|
+
}
|
|
269
326
|
function cursorHooks() {
|
|
270
327
|
const home = os.homedir();
|
|
271
328
|
const hooksPath = path.join(home, ".cursor", "hooks.json");
|
|
@@ -728,6 +785,7 @@ export function getComponentRegistry() {
|
|
|
728
785
|
claudeCodeHooks(),
|
|
729
786
|
claudeCodeInstructions(),
|
|
730
787
|
claudeCodeSkills(),
|
|
788
|
+
claudeCodeMcp(),
|
|
731
789
|
codexHooks(),
|
|
732
790
|
codexSkills(),
|
|
733
791
|
cursorHooks(),
|
|
@@ -29,7 +29,7 @@ export function createExecCommand() {
|
|
|
29
29
|
if (scanResult.secretsFound) {
|
|
30
30
|
console.error(`\n${fmt.warning("Secrets detected in staged files!")}\n`);
|
|
31
31
|
console.error(`Found ${scanResult.count} secret(s) in ${scanResult.files} file(s)`);
|
|
32
|
-
console.error(`\nRun 'rafter
|
|
32
|
+
console.error(`\nRun 'rafter secrets' for details.\n`);
|
|
33
33
|
interceptor.logEvaluation(evaluation, "blocked");
|
|
34
34
|
process.exit(1);
|
|
35
35
|
}
|
|
@@ -127,6 +127,15 @@ function installClaudeCodeHooks(root) {
|
|
|
127
127
|
const hooks = entry.hooks || [];
|
|
128
128
|
return !hooks.some((h) => h.command === "rafter hook posttool");
|
|
129
129
|
});
|
|
130
|
+
// Strip legacy SessionStart entry left over from <=0.7.4 installs.
|
|
131
|
+
if (Array.isArray(settings.hooks.SessionStart)) {
|
|
132
|
+
settings.hooks.SessionStart = settings.hooks.SessionStart.filter((entry) => {
|
|
133
|
+
const hooks = entry.hooks || [];
|
|
134
|
+
return !hooks.some((h) => h.command === "rafter hook session-start");
|
|
135
|
+
});
|
|
136
|
+
if (settings.hooks.SessionStart.length === 0)
|
|
137
|
+
delete settings.hooks.SessionStart;
|
|
138
|
+
}
|
|
130
139
|
// Add Rafter hooks
|
|
131
140
|
settings.hooks.PreToolUse.push({ matcher: "Bash", hooks: [preHook] }, { matcher: "Write|Edit", hooks: [preHook] });
|
|
132
141
|
settings.hooks.PostToolUse.push({ matcher: ".*", hooks: [postHook] });
|
|
@@ -303,6 +312,28 @@ const RAFTER_MCP_ENTRY = {
|
|
|
303
312
|
command: "rafter",
|
|
304
313
|
args: ["mcp", "serve"],
|
|
305
314
|
};
|
|
315
|
+
/**
|
|
316
|
+
* Install MCP server config for Claude Code (<root>/.mcp.json).
|
|
317
|
+
* Project-scope MCP config that Claude Code auto-loads on startup.
|
|
318
|
+
*/
|
|
319
|
+
function installClaudeCodeMcp(root) {
|
|
320
|
+
const mcpPath = path.join(root, ".mcp.json");
|
|
321
|
+
let config = {};
|
|
322
|
+
if (fs.existsSync(mcpPath)) {
|
|
323
|
+
try {
|
|
324
|
+
config = JSON.parse(fs.readFileSync(mcpPath, "utf-8"));
|
|
325
|
+
}
|
|
326
|
+
catch {
|
|
327
|
+
console.log(fmt.warning("Existing .mcp.json was unreadable, creating new one"));
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
if (!config.mcpServers)
|
|
331
|
+
config.mcpServers = {};
|
|
332
|
+
config.mcpServers.rafter = { ...RAFTER_MCP_ENTRY };
|
|
333
|
+
fs.writeFileSync(mcpPath, JSON.stringify(config, null, 2), "utf-8");
|
|
334
|
+
console.log(fmt.success(`Installed Rafter MCP server to ${mcpPath}`));
|
|
335
|
+
return true;
|
|
336
|
+
}
|
|
306
337
|
/**
|
|
307
338
|
* Install MCP server config for Gemini CLI (~/.gemini/settings.json)
|
|
308
339
|
*/
|
|
@@ -463,6 +494,52 @@ async function installClaudeCodeSkills(root) {
|
|
|
463
494
|
function installCodexSkills(root) {
|
|
464
495
|
installSkillsTo(path.join(root, ".agents", "skills"));
|
|
465
496
|
}
|
|
497
|
+
function installGeminiSkills(root) {
|
|
498
|
+
installSkillsTo(path.join(root, ".agents", "skills"));
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
501
|
+
* Register installed skills with Gemini CLI via `gemini skills link <abs-path>`.
|
|
502
|
+
*
|
|
503
|
+
* Requires gemini CLI >= 0.35 (the version that added `gemini skills`).
|
|
504
|
+
* Missing CLI, missing subcommand, and per-skill registration failures are
|
|
505
|
+
* non-fatal: we warn and continue so the on-disk install still succeeds.
|
|
506
|
+
*/
|
|
507
|
+
function registerGeminiSkills(skillsDir) {
|
|
508
|
+
// Probe for the `gemini` binary. Absence is expected on CI / fresh machines.
|
|
509
|
+
try {
|
|
510
|
+
execSync("gemini --version", { stdio: ["ignore", "pipe", "ignore"], timeout: 5000 });
|
|
511
|
+
}
|
|
512
|
+
catch {
|
|
513
|
+
console.log(fmt.warning("gemini CLI not found on PATH — skipping skill registration. " +
|
|
514
|
+
"Skills are installed to disk; re-run after installing gemini ≥ 0.35."));
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
// Probe `gemini skills` subcommand (added in 0.35).
|
|
518
|
+
try {
|
|
519
|
+
execSync("gemini skills --help", { stdio: ["ignore", "pipe", "ignore"], timeout: 5000 });
|
|
520
|
+
}
|
|
521
|
+
catch {
|
|
522
|
+
console.log(fmt.warning("gemini CLI does not support `skills` subcommand (needs ≥ 0.35). " +
|
|
523
|
+
"Skipping registration — skills are still installed to disk."));
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
for (const skill of AGENT_SKILLS) {
|
|
527
|
+
const absPath = path.resolve(skillsDir, skill.name);
|
|
528
|
+
if (!fs.existsSync(absPath))
|
|
529
|
+
continue;
|
|
530
|
+
try {
|
|
531
|
+
execSync(`gemini skills link ${JSON.stringify(absPath)}`, {
|
|
532
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
533
|
+
timeout: 10000,
|
|
534
|
+
});
|
|
535
|
+
console.log(fmt.success(`Registered ${skill.name} with Gemini CLI`));
|
|
536
|
+
}
|
|
537
|
+
catch (e) {
|
|
538
|
+
const msg = (e?.stderr?.toString?.() || e?.message || "").trim();
|
|
539
|
+
console.log(fmt.warning(`Failed to register ${skill.name} with Gemini CLI: ${msg.split("\n")[0] || "unknown error"}`));
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
}
|
|
466
543
|
async function askYesNo(question, defaultYes = true) {
|
|
467
544
|
const rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
468
545
|
const suffix = defaultYes ? "[Y/n]" : "[y/N]";
|
|
@@ -717,6 +794,17 @@ export function createInitCommand() {
|
|
|
717
794
|
try {
|
|
718
795
|
await installClaudeCodeSkills(root);
|
|
719
796
|
installClaudeCodeHooks(root);
|
|
797
|
+
if (scope === "project") {
|
|
798
|
+
const components = (manager.get("agent.components") ?? {});
|
|
799
|
+
if (components["claude-code.mcp"]?.enabled === false) {
|
|
800
|
+
console.log(fmt.info("Skipped .mcp.json (claude-code.mcp disabled; re-enable with `rafter agent enable claude-code.mcp`)"));
|
|
801
|
+
}
|
|
802
|
+
else {
|
|
803
|
+
installClaudeCodeMcp(root);
|
|
804
|
+
components["claude-code.mcp"] = { enabled: true, updatedAt: new Date().toISOString() };
|
|
805
|
+
manager.set("agent.components", components);
|
|
806
|
+
}
|
|
807
|
+
}
|
|
720
808
|
if (scope === "user")
|
|
721
809
|
manager.set("agent.environments.claudeCode.enabled", true);
|
|
722
810
|
claudeCodeOk = true;
|
|
@@ -739,11 +827,13 @@ export function createInitCommand() {
|
|
|
739
827
|
console.error(fmt.error(`Failed to install Codex CLI integration: ${e}`));
|
|
740
828
|
}
|
|
741
829
|
}
|
|
742
|
-
// Install Gemini CLI MCP + hooks if opted in
|
|
830
|
+
// Install Gemini CLI MCP + skills + hooks if opted in
|
|
743
831
|
let geminiOk = false;
|
|
744
832
|
if ((hasGemini || (opts.local && wantGemini)) && wantGemini) {
|
|
745
833
|
try {
|
|
746
834
|
geminiOk = installGeminiMcp(root);
|
|
835
|
+
installGeminiSkills(root);
|
|
836
|
+
registerGeminiSkills(path.join(root, ".agents", "skills"));
|
|
747
837
|
installGeminiHooks(root);
|
|
748
838
|
if (geminiOk && scope === "user")
|
|
749
839
|
manager.set("agent.environments.gemini.enabled", true);
|
|
@@ -873,7 +963,7 @@ export function createInitCommand() {
|
|
|
873
963
|
console.log("No agent environments detected. Install an agent tool and re-run with --with-<tool>.");
|
|
874
964
|
}
|
|
875
965
|
console.log();
|
|
876
|
-
console.log(" - Run: rafter
|
|
966
|
+
console.log(" - Run: rafter secrets . (test secret scanning)");
|
|
877
967
|
console.log(" - Configure: rafter agent config show");
|
|
878
968
|
console.log();
|
|
879
969
|
// Warn if a different rafter version shadows this one on PATH
|
|
@@ -4,30 +4,29 @@ export const RAFTER_MARKER_START = "<!-- rafter:start -->";
|
|
|
4
4
|
export const RAFTER_MARKER_END = "<!-- rafter:end -->";
|
|
5
5
|
/** Rafter security context block for agent instruction files (CLAUDE.md, AGENTS.md, etc.) */
|
|
6
6
|
export const RAFTER_INSTRUCTION_BLOCK = `${RAFTER_MARKER_START}
|
|
7
|
-
## Security: Rafter
|
|
7
|
+
## Security: Rafter (required review gate)
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Rafter is this project's security review gate. A security-relevant task is
|
|
10
|
+
**not complete** until a rafter skill (or \`rafter run\`) has reviewed it.
|
|
11
|
+
Don't mark done, don't hand off, don't open a PR without that pass.
|
|
10
12
|
|
|
11
|
-
**
|
|
12
|
-
- \`rafter scan local .\` — scan for leaked credentials
|
|
13
|
-
- \`rafter scan local --staged\` — scan staged changes only
|
|
13
|
+
**Stop and invoke before continuing:**
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
-
|
|
15
|
+
- Designing auth, payments, credentials, tokens, sessions, file upload,
|
|
16
|
+
user data, or data deletion → **\`rafter-secure-design\`** (before writing code).
|
|
17
|
+
- Diff touches user input, SQL, shell, auth, credentials, file paths, or
|
|
18
|
+
serialization → **\`rafter-code-review\`** + \`rafter run\` (before declaring done).
|
|
19
|
+
- About to install or forward a third-party SKILL.md, MCP manifest, Cursor
|
|
20
|
+
rule, or agent config → **\`rafter-skill-review\`** (before copying anywhere).
|
|
21
|
+
- Security-adjacent but the angle isn't clear → **\`rafter\`** (the router skill,
|
|
22
|
+
powerful toolkit to help you write more secure code).
|
|
18
23
|
|
|
19
|
-
|
|
20
|
-
If RAFTER_API_KEY is not set, local scanning still works — use what's available.
|
|
24
|
+
**CLI:**
|
|
21
25
|
|
|
22
|
-
|
|
23
|
-
- \`rafter
|
|
24
|
-
- \`rafter
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
**More:**
|
|
28
|
-
- \`rafter agent audit-skill <path>\` — audit a skill before installing
|
|
29
|
-
- \`rafter agent audit --last 5\` — recent security events
|
|
30
|
-
- \`rafter brief commands\` — full CLI reference
|
|
26
|
+
- \`rafter run\` — remote SAST + SCA + secrets (real code analysis, needs \`RAFTER_API_KEY\`)
|
|
27
|
+
- \`rafter secrets .\` — local secrets only (offline; NOT a code-security scan)
|
|
28
|
+
- \`rafter run --mode plus\` — everything in default (\`--mode fast\`) plus
|
|
29
|
+
powerful agentic deep-dives (needs \`RAFTER_API_KEY\`)
|
|
31
30
|
${RAFTER_MARKER_END}`;
|
|
32
31
|
/**
|
|
33
32
|
* Write a Rafter instruction block into an instruction file.
|
|
@@ -67,7 +67,7 @@ export function createScanCommand() {
|
|
|
67
67
|
const isAgentScan = argv.includes("agent") && argv.includes("scan") &&
|
|
68
68
|
argv.indexOf("agent") < argv.indexOf("scan");
|
|
69
69
|
if (isAgentScan) {
|
|
70
|
-
process.stderr.write("Warning: rafter agent scan is deprecated and will be removed in a future major version. Use rafter
|
|
70
|
+
process.stderr.write("Warning: rafter agent scan is deprecated and will be removed in a future major version. Use rafter secrets instead.\n");
|
|
71
71
|
}
|
|
72
72
|
// Load policy-merged config for excludePaths/customPatterns
|
|
73
73
|
const manager = new ConfigManager();
|
|
@@ -115,6 +115,17 @@ export function createScanCommand() {
|
|
|
115
115
|
outputScanResults(applyBaseline(results, baselineEntries), opts);
|
|
116
116
|
});
|
|
117
117
|
}
|
|
118
|
+
/**
|
|
119
|
+
* `rafter secrets` — top-level alias for the secret scanner. Same engine
|
|
120
|
+
* and flags as `rafter scan local`; the name makes the scope (secrets only,
|
|
121
|
+
* not full code analysis) explicit to agents and humans.
|
|
122
|
+
*/
|
|
123
|
+
export function createSecretsCommand() {
|
|
124
|
+
const cmd = createScanCommand();
|
|
125
|
+
cmd.name("secrets");
|
|
126
|
+
cmd.description("Scan files/directories for hardcoded secrets (regex + gitleaks). Secrets only — not a code analysis. For full SAST/SCA, use 'rafter run'.");
|
|
127
|
+
return cmd;
|
|
128
|
+
}
|
|
118
129
|
/**
|
|
119
130
|
* Emit SARIF 2.1.0 JSON for GitHub/GitLab security tab integration
|
|
120
131
|
*/
|
package/dist/commands/brief.js
CHANGED
|
@@ -210,7 +210,7 @@ This installs:
|
|
|
210
210
|
## Manual Setup (if automated init isn't available)
|
|
211
211
|
|
|
212
212
|
1. Run \`rafter brief scanning\` and save the command reference
|
|
213
|
-
2. Before commits, run: \`rafter
|
|
213
|
+
2. Before commits, run: \`rafter secrets .\`
|
|
214
214
|
3. For remote analysis: \`rafter run\``,
|
|
215
215
|
codex: `# Rafter Setup — Codex CLI
|
|
216
216
|
|
|
@@ -424,7 +424,7 @@ rafter brief commands # know what commands are available
|
|
|
424
424
|
|
|
425
425
|
## Key Commands to Know
|
|
426
426
|
|
|
427
|
-
- \`rafter
|
|
427
|
+
- \`rafter secrets .\` — scan for hardcoded secrets locally (no API key needed)
|
|
428
428
|
- \`rafter run\` — trigger remote SAST/SCA analysis (needs API key)
|
|
429
429
|
- \`rafter get <id>\` — retrieve scan results
|
|
430
430
|
- \`rafter agent audit\` — review security event log
|
package/dist/commands/ci/init.js
CHANGED
|
@@ -100,7 +100,7 @@ jobs:
|
|
|
100
100
|
run: npm install -g @rafter-security/cli
|
|
101
101
|
|
|
102
102
|
- name: Scan for secrets
|
|
103
|
-
run: rafter
|
|
103
|
+
run: rafter secrets . --quiet
|
|
104
104
|
`;
|
|
105
105
|
if (withBackend) {
|
|
106
106
|
yaml += `
|
|
@@ -131,7 +131,7 @@ secret-scan:
|
|
|
131
131
|
image: node:20
|
|
132
132
|
script:
|
|
133
133
|
- npm install -g @rafter-security/cli
|
|
134
|
-
- rafter
|
|
134
|
+
- rafter secrets . --quiet
|
|
135
135
|
rules:
|
|
136
136
|
- if: $CI_PIPELINE_SOURCE == "push"
|
|
137
137
|
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
|
@@ -169,7 +169,7 @@ jobs:
|
|
|
169
169
|
command: npm install -g @rafter-security/cli
|
|
170
170
|
- run:
|
|
171
171
|
name: Scan for secrets
|
|
172
|
-
command: rafter
|
|
172
|
+
command: rafter secrets . --quiet
|
|
173
173
|
`;
|
|
174
174
|
if (withBackend) {
|
|
175
175
|
yaml += `
|
|
@@ -142,7 +142,7 @@ function evaluateBash(command) {
|
|
|
142
142
|
audit.logSecretDetected("staged files", `${scanResult.count} secret(s)`, "blocked");
|
|
143
143
|
return {
|
|
144
144
|
decision: "deny",
|
|
145
|
-
reason: `${scanResult.count} secret(s) detected in ${scanResult.files} staged file(s). Run 'rafter
|
|
145
|
+
reason: `${scanResult.count} secret(s) detected in ${scanResult.files} staged file(s). Run 'rafter secrets --staged' for details.`,
|
|
146
146
|
};
|
|
147
147
|
}
|
|
148
148
|
}
|
package/dist/commands/report.js
CHANGED
|
@@ -25,7 +25,7 @@ export function createReportCommand() {
|
|
|
25
25
|
}
|
|
26
26
|
else {
|
|
27
27
|
console.error("Error: No input provided. Pipe scan results or provide a file path.\n" +
|
|
28
|
-
" Example: rafter
|
|
28
|
+
" Example: rafter secrets --json . | rafter report -o report.html\n" +
|
|
29
29
|
" Example: rafter report scan-results.json -o report.html");
|
|
30
30
|
process.exit(2);
|
|
31
31
|
return;
|
|
@@ -3,16 +3,17 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Default (no subcommand): remote scan (same as `rafter run`)
|
|
5
5
|
* rafter scan remote: explicit alias for remote scan
|
|
6
|
-
* rafter scan local [path]:
|
|
6
|
+
* rafter scan local [path]: hidden back-compat alias for `rafter secrets`
|
|
7
|
+
* (was `rafter agent scan` before 0.7.4).
|
|
7
8
|
*/
|
|
8
9
|
import { Command } from "commander";
|
|
9
10
|
import { runRemoteScan } from "../backend/run.js";
|
|
10
11
|
import { createScanCommand as createLocalScanCommand } from "../agent/scan.js";
|
|
11
12
|
export function createScanGroupCommand() {
|
|
12
|
-
// "local" subcommand —
|
|
13
|
+
// "local" subcommand — back-compat alias for `rafter secrets`. Hidden from help.
|
|
13
14
|
const localCmd = createLocalScanCommand();
|
|
14
15
|
localCmd.name("local");
|
|
15
|
-
localCmd.description("
|
|
16
|
+
localCmd.description("(deprecated alias for 'rafter secrets')");
|
|
16
17
|
// "remote" subcommand — same handler as `rafter run`
|
|
17
18
|
const remoteCmd = new Command("remote")
|
|
18
19
|
.description("Trigger a remote backend security scan (explicit alias for 'rafter run')")
|
|
@@ -29,7 +30,7 @@ export function createScanGroupCommand() {
|
|
|
29
30
|
});
|
|
30
31
|
// Root scan group — default action is remote scan
|
|
31
32
|
const scanGroup = new Command("scan")
|
|
32
|
-
.description("
|
|
33
|
+
.description("Trigger a remote security scan (requires RAFTER_API_KEY).")
|
|
33
34
|
.enablePositionalOptions()
|
|
34
35
|
.option("-r, --repo <repo>", "org/repo (default: current)")
|
|
35
36
|
.option("-b, --branch <branch>", "branch (default: current else main)")
|
|
@@ -39,7 +40,7 @@ export function createScanGroupCommand() {
|
|
|
39
40
|
.option("--github-token <token>", "GitHub PAT for private repos (or RAFTER_GITHUB_TOKEN env var)")
|
|
40
41
|
.option("--skip-interactive", "do not wait for scan to complete")
|
|
41
42
|
.option("--quiet", "suppress status messages");
|
|
42
|
-
scanGroup.addCommand(localCmd);
|
|
43
|
+
scanGroup.addCommand(localCmd, { hidden: true });
|
|
43
44
|
scanGroup.addCommand(remoteCmd);
|
|
44
45
|
// When invoked with no subcommand, run remote scan
|
|
45
46
|
scanGroup.action(async (opts) => {
|
package/dist/index.js
CHANGED
|
@@ -5,6 +5,7 @@ import { createRunCommand } from "./commands/backend/run.js";
|
|
|
5
5
|
import { createGetCommand } from "./commands/backend/get.js";
|
|
6
6
|
import { createUsageCommand } from "./commands/backend/usage.js";
|
|
7
7
|
import { createScanGroupCommand } from "./commands/scan/index.js";
|
|
8
|
+
import { createSecretsCommand } from "./commands/agent/scan.js";
|
|
8
9
|
import { createAgentCommand } from "./commands/agent/index.js";
|
|
9
10
|
import { createSkillCommand } from "./commands/skill/index.js";
|
|
10
11
|
import { createCiCommand } from "./commands/ci/index.js";
|
|
@@ -40,6 +41,8 @@ program.addCommand(createGetCommand());
|
|
|
40
41
|
program.addCommand(createUsageCommand());
|
|
41
42
|
// Scan command group (default: remote scan; subcommands: local, remote)
|
|
42
43
|
program.addCommand(createScanGroupCommand());
|
|
44
|
+
// Secrets — top-level alias for local secret scanning (explicit-scope name)
|
|
45
|
+
program.addCommand(createSecretsCommand());
|
|
43
46
|
// Agent commands
|
|
44
47
|
program.addCommand(createAgentCommand());
|
|
45
48
|
// Skill commands (install / uninstall / list rafter-authored skills)
|
package/package.json
CHANGED
|
@@ -38,7 +38,7 @@ while read local_ref local_sha remote_ref remote_sha; do
|
|
|
38
38
|
|
|
39
39
|
echo "🔍 Rafter: Scanning commits being pushed ($local_ref)..."
|
|
40
40
|
|
|
41
|
-
rafter
|
|
41
|
+
rafter secrets --diff "$ref_arg" --quiet
|
|
42
42
|
EXIT_CODE=$?
|
|
43
43
|
|
|
44
44
|
if [ $EXIT_CODE -ne 0 ]; then
|
|
@@ -49,7 +49,7 @@ done
|
|
|
49
49
|
if [ $FOUND_SECRETS -ne 0 ]; then
|
|
50
50
|
echo -e "${RED}❌ Push blocked: Secrets detected in commits being pushed${NC}"
|
|
51
51
|
echo ""
|
|
52
|
-
echo " Run: rafter
|
|
52
|
+
echo " Run: rafter secrets --diff <remote-sha>"
|
|
53
53
|
echo " To see details and remediate."
|
|
54
54
|
echo ""
|
|
55
55
|
echo " To bypass (NOT recommended): git push --no-verify"
|
|
@@ -51,7 +51,7 @@ rafter agent init --all
|
|
|
51
51
|
Scan files for secrets before committing.
|
|
52
52
|
|
|
53
53
|
```bash
|
|
54
|
-
rafter
|
|
54
|
+
rafter secrets <path>
|
|
55
55
|
```
|
|
56
56
|
|
|
57
57
|
**When to use:**
|
|
@@ -284,7 +284,7 @@ Configure with: `rafter agent config set agent.riskLevel moderate`
|
|
|
284
284
|
|
|
285
285
|
## Best Practices
|
|
286
286
|
|
|
287
|
-
1. **Always scan before commits**: Run `rafter
|
|
287
|
+
1. **Always scan before commits**: Run `rafter secrets` before `git commit`
|
|
288
288
|
2. **Audit untrusted skills**: Run `/rafter-audit-skill` on skills from unknown sources before installation
|
|
289
289
|
3. **Review audit logs**: Check `rafter agent audit` after suspicious activity
|
|
290
290
|
4. **Keep patterns updated**: Patterns updated automatically with CLI updates
|
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: rafter
|
|
3
|
-
description: "
|
|
3
|
+
description: "Entry point for rafter. Invoke when a sub-skill is unclear, or when the task needs `rafter run` (remote SAST+SCA), `rafter secrets` (local secrets-only), `rafter audit`, policy checks, or command-risk evaluation. If a task is security-relevant and no rafter skill or CLI call has been made, invoke this before handing the task off — an un-evaluated \"done\" on security-relevant work is not done."
|
|
4
4
|
version: 0.7.0
|
|
5
5
|
allowed-tools: [Bash, Read]
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Rafter — Security Toolkit for AI Workflows
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
## Picking the right tier — DO NOT stop at "local"
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
Rafter ships three tiers. **They are not interchangeable.** The local tier is narrow; skipping remote analysis is the #1 way agents under-use rafter.
|
|
13
|
+
|
|
14
|
+
1. **Local (`rafter secrets`)** — secrets only. Regex + gitleaks for hardcoded API keys, tokens, private keys. Fast, offline, no key. **This is NOT a code security scan.** It will not find SQL injection, SSRF, auth bugs, insecure deserialization, logic flaws, or dependency vulns. If an agent's entire rafter interaction was `rafter secrets .` and it exited clean, the agent has done secret-hygiene only — not security review.
|
|
15
|
+
2. **Remote fast (`rafter run`, default mode)** — SAST + SCA + secrets via the Rafter API. This is the real code-analysis pass: dataflow, taint, known-vulnerable dependencies, crypto misuse, injection sinks. Needs `RAFTER_API_KEY`.
|
|
16
|
+
3. **Remote plus (`rafter run --mode plus`)** — agentic deep-dive: LLM-guided investigation of suspicious patterns the rules engine flags. Slower, higher signal. Code is deleted server-side after the run.
|
|
17
|
+
|
|
18
|
+
**Default expectation for a security-relevant task**: run `rafter run`. Fall back to `rafter secrets` only when no API key is available or you specifically need offline secret-hygiene. If you've only run the local scanner, say so explicitly — don't claim the code was "scanned" without qualification.
|
|
15
19
|
|
|
16
20
|
Stable exit codes, stable JSON shapes, deterministic findings. Safe to chain in CI and in agent loops.
|
|
17
21
|
|
|
@@ -25,10 +29,11 @@ Pick the branch that matches what you're trying to do. Each branch points at a s
|
|
|
25
29
|
|
|
26
30
|
Use this for: "Is this safe to push?", "Check for leaks", "Run a security scan", pre-merge / pre-deploy gating, post-dependency-update checks.
|
|
27
31
|
|
|
28
|
-
-
|
|
29
|
-
-
|
|
32
|
+
- **Default: `rafter run`** — remote SAST + SCA + secrets. This is the real scan. Needs `RAFTER_API_KEY`.
|
|
33
|
+
- **Deep-dive: `rafter run --mode plus`** — agentic analysis when stakes are high or fast mode flagged something suspicious worth investigating.
|
|
34
|
+
- **Secrets-only fallback: `rafter secrets`** — use when no API key is available, or alongside `rafter run` for fastest secret-leak feedback. Does NOT analyse code — only hunts hardcoded credentials.
|
|
30
35
|
- **Read `docs/backend.md`** for fast-vs-plus modes, auth, latency, cost.
|
|
31
|
-
- **Read `docs/cli-reference.md`** §`scan
|
|
36
|
+
- **Read `docs/cli-reference.md`** §`secrets`, §`scan`, §`run` for full flag matrix.
|
|
32
37
|
|
|
33
38
|
### (b) I want to evaluate a command before running it
|
|
34
39
|
|
|
@@ -86,9 +91,10 @@ MCP-connected agents: the same surface is exposed as the `rafter://docs` resourc
|
|
|
86
91
|
## Fast Path (most common)
|
|
87
92
|
|
|
88
93
|
```bash
|
|
89
|
-
rafter
|
|
90
|
-
rafter run
|
|
91
|
-
rafter
|
|
94
|
+
rafter run # remote SAST + SCA + secrets — the real code scan
|
|
95
|
+
rafter run --mode plus # agentic deep-dive when fast mode flags something
|
|
96
|
+
rafter secrets # secrets-only — offline, no key
|
|
97
|
+
rafter get <scan-id> # fetch results by id
|
|
92
98
|
rafter usage # check API quota
|
|
93
99
|
```
|
|
94
100
|
|
|
@@ -100,19 +106,13 @@ Full CLI tree: **Read `docs/cli-reference.md`**. Full digest: `rafter brief comm
|
|
|
100
106
|
|
|
101
107
|
## Configuration
|
|
102
108
|
|
|
103
|
-
|
|
109
|
+
`rafter run` (the full code scan) needs an API key:
|
|
104
110
|
|
|
105
111
|
```bash
|
|
106
112
|
export RAFTER_API_KEY="..." # or put it in .env
|
|
107
113
|
```
|
|
108
114
|
|
|
109
|
-
Without a key,
|
|
115
|
+
Without a key, only `rafter secrets` works — that's secret-hygiene, not code review. If security matters for the task, flag the missing key to the user rather than silently accepting the narrower scan.
|
|
110
116
|
|
|
111
117
|
## Strengthen the Project
|
|
112
|
-
|
|
113
|
-
If this repo doesn't have Rafter wired in yet:
|
|
114
|
-
|
|
115
|
-
- `rafter agent install-hook` — pre-commit secret scan
|
|
116
|
-
- `rafter ci init` — CI workflow with scanning
|
|
117
|
-
- `.rafter.yml` — project-specific policy
|
|
118
|
-
- `rafter brief setup/<platform>` — per-agent integration guide
|
|
118
|
+
Not wired in yet? `rafter agent install-hook` (pre-commit), `rafter ci init` (CI workflow), `.rafter.yml` (policy). Per-platform setup: `rafter brief setup/<platform>`.
|
|
@@ -6,7 +6,7 @@ When to reach for the Rafter API instead of (or in addition to) the local scanne
|
|
|
6
6
|
|
|
7
7
|
| Question | Answer |
|
|
8
8
|
|---|---|
|
|
9
|
-
| "Are there leaked secrets in this diff/repo?" | **Local first** (`rafter
|
|
9
|
+
| "Are there leaked secrets in this diff/repo?" | **Local first** (`rafter secrets .`). Deterministic, offline, sub-second. |
|
|
10
10
|
| "Any SAST issues — SQLi, XSS, insecure deserialization, weak crypto?" | **Remote** (`rafter run`). Needs the backend's analyzers. |
|
|
11
11
|
| "Are my dependencies vulnerable (CVEs)?" | **Remote** — SCA runs server-side. |
|
|
12
12
|
| "I'm in a CI pipeline without a `RAFTER_API_KEY`" | **Local only**. Don't fail the build on a missing key. |
|
|
@@ -26,7 +26,7 @@ Private GitHub repos need `RAFTER_GITHUB_TOKEN` (or `--github-token`) so the bac
|
|
|
26
26
|
|
|
27
27
|
Check quota with `rafter usage` before firing a batch of scans.
|
|
28
28
|
|
|
29
|
-
If the key is missing, `rafter run` exits with a clear error — **do not** prompt the user mid-flow; recommend `rafter
|
|
29
|
+
If the key is missing, `rafter run` exits with a clear error — **do not** prompt the user mid-flow; recommend `rafter secrets` and move on.
|
|
30
30
|
|
|
31
31
|
## Modes
|
|
32
32
|
|
|
@@ -47,7 +47,7 @@ Agentic deep-dive pass on top of fast mode: cross-file reasoning, data-flow hypo
|
|
|
47
47
|
- **Output**: same JSON shape as fast mode, plus narrative `notes` and higher-confidence chains.
|
|
48
48
|
|
|
49
49
|
Recommended flow:
|
|
50
|
-
1. `rafter
|
|
50
|
+
1. `rafter secrets .` — secrets guardrail in dev loop.
|
|
51
51
|
2. `rafter run --mode fast` — every PR in CI.
|
|
52
52
|
3. `rafter run --mode plus` — before release, or when a fast-mode finding needs deeper context.
|
|
53
53
|
|
|
@@ -30,7 +30,7 @@ Key options: `--repo org/repo`, `--branch <name>`, `--mode fast|plus`, `--format
|
|
|
30
30
|
|
|
31
31
|
Example: `rafter run --repo myorg/api --branch feature/auth --mode plus --format json`
|
|
32
32
|
|
|
33
|
-
### `rafter
|
|
33
|
+
### `rafter secrets [path]`
|
|
34
34
|
|
|
35
35
|
Local secret scan. Deterministic, offline, no API key. Dual-engine: Gitleaks binary if present, built-in regex fallback (21+ patterns).
|
|
36
36
|
|
|
@@ -38,7 +38,9 @@ When: pre-commit, pre-push, fast first pass before remote scan, air-gapped envs.
|
|
|
38
38
|
|
|
39
39
|
Useful flags: `--history` (scan git history with Gitleaks), `--format json`, `--quiet`.
|
|
40
40
|
|
|
41
|
-
Example: `rafter
|
|
41
|
+
Example: `rafter secrets . --format json`
|
|
42
|
+
|
|
43
|
+
(Back-compat aliases: `rafter scan local` and `rafter agent scan`. Prefer `rafter secrets`.)
|
|
42
44
|
|
|
43
45
|
### `rafter get <scan-id>`
|
|
44
46
|
|
|
@@ -76,10 +78,6 @@ When: vetting a third-party skill, MCP server, or CLI plugin before install.
|
|
|
76
78
|
|
|
77
79
|
Audit a single skill file (SKILL.md). Flags prompt-injection, unbounded tool use, exfiltration patterns.
|
|
78
80
|
|
|
79
|
-
### `rafter agent scan [path]`
|
|
80
|
-
|
|
81
|
-
Alias for `rafter scan local` kept for back-compat. Prefer `rafter scan local`.
|
|
82
|
-
|
|
83
81
|
### `rafter agent status` · `rafter agent verify`
|
|
84
82
|
|
|
85
83
|
`status`: dump config, hook state, gitleaks availability, audit log location.
|
|
@@ -95,7 +93,7 @@ Scaffold `.rafter.yml` and a baseline for the current repo.
|
|
|
95
93
|
|
|
96
94
|
### `rafter agent install-hook`
|
|
97
95
|
|
|
98
|
-
Install a pre-commit hook that runs `rafter
|
|
96
|
+
Install a pre-commit hook that runs `rafter secrets --staged` before every commit.
|
|
99
97
|
|
|
100
98
|
### `rafter agent config [get|set|list]`
|
|
101
99
|
|
|
@@ -189,7 +187,7 @@ Emit shell completion script.
|
|
|
189
187
|
|
|
190
188
|
| User intent | Command |
|
|
191
189
|
|---|---|
|
|
192
|
-
| Fast secret check locally | `rafter
|
|
190
|
+
| Fast secret check locally | `rafter secrets .` |
|
|
193
191
|
| Full repo security review | `rafter run` (then `rafter get <id>`) |
|
|
194
192
|
| "Is this command safe?" | `rafter agent exec --dry-run -- <cmd>` |
|
|
195
193
|
| "Is this skill safe to install?" | `rafter agent audit <path>` |
|
|
@@ -50,7 +50,7 @@ Pair with `rafter run --mode plus` when you want both a human-style walkthrough
|
|
|
50
50
|
|
|
51
51
|
- Designing a new service → **secure-design**.
|
|
52
52
|
- Reviewing a teammate's PR by eye → **code-review**.
|
|
53
|
-
- CI gate / pre-push / scheduled scan → **rafter** (this skill), `rafter run` / `rafter
|
|
53
|
+
- CI gate / pre-push / scheduled scan → **rafter** (this skill), `rafter run` / `rafter secrets`.
|
|
54
54
|
- "I have a finding, now what?" → **rafter**, `docs/finding-triage.md`.
|
|
55
55
|
- "I have a risky command, is it safe?" → **rafter**, `docs/guardrails.md`.
|
|
56
56
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: rafter-code-review
|
|
3
|
-
description: "
|
|
3
|
+
description: "REQUIRED before declaring a task done when the diff touches user input, SQL, shell, auth, credentials, file paths, serialization, or dependency surface. Walks OWASP/ASVS/MITRE questions the compiler and the test suite won't catch. Pair with `rafter run` on the same diff. A security-relevant task marked complete without both has been under-delivered — do not claim done."
|
|
4
4
|
version: 0.7.0
|
|
5
5
|
allowed-tools: [Bash, Read, Glob, Grep]
|
|
6
6
|
---
|
|
@@ -64,7 +64,7 @@ For: single-finding follow-up, tracing a suspicious call, "is this input reachab
|
|
|
64
64
|
## What this skill will NOT do
|
|
65
65
|
|
|
66
66
|
- It will not generate a monolithic "security audit report". If you need a report, run `rafter run --mode plus` — the backend is better at that.
|
|
67
|
-
- It will not replace automated scanning. Always pair with `rafter
|
|
67
|
+
- It will not replace automated scanning. Always pair with `rafter secrets .` (secrets) and `rafter run` (SAST/SCA) before review.
|
|
68
68
|
- It will not produce recommendations without evidence. Every question expects a file:line answer before moving on.
|
|
69
69
|
|
|
70
70
|
---
|
|
@@ -73,7 +73,7 @@ For: single-finding follow-up, tracing a suspicious call, "is this input reachab
|
|
|
73
73
|
|
|
74
74
|
```bash
|
|
75
75
|
# 1. Run deterministic checks first — cheap, catches the obvious
|
|
76
|
-
rafter
|
|
76
|
+
rafter secrets .
|
|
77
77
|
rafter run # remote SAST/SCA, if RAFTER_API_KEY set
|
|
78
78
|
|
|
79
79
|
# 2. Then pick the category and walk the questions
|
|
@@ -15,7 +15,7 @@ MITRE's CWE Top 25 is weakness-level, not risk-level. Use this for CLI tools, li
|
|
|
15
15
|
- **CWE-22 Path Traversal** — any `open(path)`, `fs.readFile(path)`, `os.path.join(base, user_input)`. Canonicalize (`realpath` / `filepath.Abs`) and verify the result stays under an allow-root.
|
|
16
16
|
- **CWE-352 CSRF** — state-changing endpoints: is there a token check? SameSite cookies are necessary but not sufficient for cross-site POSTs in older browsers / API clients.
|
|
17
17
|
- **CWE-287 Improper Authentication / CWE-862 Missing Authorization** — covered in web-app.md / api.md.
|
|
18
|
-
- **CWE-798 Hardcoded Credentials** — `rafter
|
|
18
|
+
- **CWE-798 Hardcoded Credentials** — `rafter secrets .` catches literal secrets; manually check env-var defaults (`API_KEY = os.environ.get("KEY", "dev-fallback-abc123")` ships the fallback).
|
|
19
19
|
- **CWE-918 SSRF** — any user-supplied URL fetched server-side. See web-app.md A10.
|
|
20
20
|
|
|
21
21
|
---
|
|
@@ -16,7 +16,7 @@ The #1 risk. Every authenticated route must answer: "who is allowed?"
|
|
|
16
16
|
|
|
17
17
|
- What algorithms appear? Grep for `md5`, `sha1`, `des`, `rc4`, `ecb`. Any hit on user data, session tokens, or passwords is a finding.
|
|
18
18
|
- How are passwords hashed? Look for `bcrypt`, `scrypt`, `argon2`, `pbkdf2`. Absence is the finding. `sha256(password + salt)` is not password hashing.
|
|
19
|
-
- Are secrets in source? Run `rafter
|
|
19
|
+
- Are secrets in source? Run `rafter secrets .` first — but also grep for `private_key`, `api_key`, `BEGIN RSA`, `.pem`, `.p12`.
|
|
20
20
|
- Is TLS enforced? Look for redirect middleware, HSTS headers, cookie `Secure` flag. Cookies without `Secure` + `HttpOnly` + `SameSite` — ask why.
|
|
21
21
|
- Is randomness from `Math.random()` / `rand()` used for tokens, session ids, password resets? Must be `crypto.randomBytes` / `secrets.token_*` / `crypto/rand`.
|
|
22
22
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: rafter-secure-design
|
|
3
|
-
description: "
|
|
3
|
+
description: "REQUIRED before writing code for any feature touching auth, payments, credentials, tokens, sessions, file upload, user data, or data deletion. Invoke it, record answers inline in the design doc or PR description, then write the code. Skipping this is how auth bypass, token leakage, and delete-the-wrong-record bugs ship. If the feature touches any of those surfaces and this hasn't run, the design is incomplete — do not proceed to implementation."
|
|
4
4
|
version: 0.1.0
|
|
5
5
|
allowed-tools: [Read, Glob, Grep]
|
|
6
6
|
---
|
|
@@ -98,6 +98,6 @@ If you're revisiting an existing design (refactor, migration), same flow: treat
|
|
|
98
98
|
## Tie-backs
|
|
99
99
|
|
|
100
100
|
- Ready to review the code that implements the design? → `rafter-code-review`.
|
|
101
|
-
- Implementation landed, need automated checks? → `rafter` skill, `rafter run` / `rafter
|
|
101
|
+
- Implementation landed, need automated checks? → `rafter` skill, `rafter run` / `rafter secrets`.
|
|
102
102
|
- Risky command came up mid-design (spike, data migration)? → `rafter` skill, `docs/guardrails.md`.
|
|
103
103
|
- Have a specific finding from a scan? → `rafter` skill, `docs/finding-triage.md`.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: rafter-skill-review
|
|
3
|
-
description: "
|
|
3
|
+
description: "REQUIRED before copying any third-party SKILL.md, MCP manifest, Cursor rule, or agent config into this machine or forwarding one to a user. Installing a skill grants Read/Bash/network under your identity — this is `curl | sh` in a different costume. Do not install, load, recommend, or forward any third-party agent asset until this skill has vetted it. Bypassing this turns the agent into a delivery vector."
|
|
4
4
|
version: 0.1.0
|
|
5
5
|
allowed-tools: [Bash, Read, Grep, Glob, WebFetch]
|
|
6
6
|
---
|
|
@@ -24,7 +24,7 @@ rafter skill review <path> --format text # human-readable summary
|
|
|
24
24
|
|
|
25
25
|
The command:
|
|
26
26
|
- pulls the skill (if a URL, does a shallow clone into a temp dir),
|
|
27
|
-
- runs `rafter
|
|
27
|
+
- runs `rafter secrets` over the tree,
|
|
28
28
|
- extracts URLs, high-risk shell patterns, obfuscation signals,
|
|
29
29
|
- reads `SKILL.md` frontmatter (`allowed-tools`, `version`, etc.),
|
|
30
30
|
- prints a structured JSON report — see `shared-docs/CLI_SPEC.md` §`rafter skill review`.
|