@rafter-security/cli 0.5.3 → 0.5.9

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 (40) hide show
  1. package/README.md +15 -3
  2. package/dist/commands/agent/audit-skill.js +2 -2
  3. package/dist/commands/agent/audit.js +96 -0
  4. package/dist/commands/agent/baseline.js +213 -0
  5. package/dist/commands/agent/exec.js +1 -1
  6. package/dist/commands/agent/index.js +4 -0
  7. package/dist/commands/agent/init.js +371 -29
  8. package/dist/commands/agent/install-hook.js +41 -47
  9. package/dist/commands/agent/scan.js +196 -23
  10. package/dist/commands/agent/status.js +65 -4
  11. package/dist/commands/agent/update-gitleaks.js +40 -0
  12. package/dist/commands/agent/verify.js +18 -4
  13. package/dist/commands/backend/run.js +69 -61
  14. package/dist/commands/ci/init.js +10 -3
  15. package/dist/commands/completion.js +320 -110
  16. package/dist/commands/hook/posttool.js +21 -7
  17. package/dist/commands/hook/pretool.js +50 -13
  18. package/dist/commands/issues/dedup.js +39 -0
  19. package/dist/commands/issues/from-scan.js +143 -0
  20. package/dist/commands/issues/from-text.js +185 -0
  21. package/dist/commands/issues/github-client.js +85 -0
  22. package/dist/commands/issues/index.js +25 -0
  23. package/dist/commands/issues/issue-builder.js +101 -0
  24. package/dist/commands/policy/export.js +7 -2
  25. package/dist/commands/scan/index.js +44 -0
  26. package/dist/core/audit-logger.js +41 -0
  27. package/dist/core/config-defaults.js +28 -0
  28. package/dist/core/config-manager.js +19 -2
  29. package/dist/core/pattern-engine.js +26 -1
  30. package/dist/core/risk-rules.js +5 -3
  31. package/dist/index.js +8 -2
  32. package/dist/scanners/gitleaks.js +5 -5
  33. package/dist/scanners/regex-scanner.js +12 -1
  34. package/dist/scanners/secret-patterns.js +3 -3
  35. package/dist/utils/binary-manager.js +59 -20
  36. package/dist/utils/skill-manager.js +5 -3
  37. package/package.json +2 -1
  38. package/resources/pre-commit-hook.sh +2 -2
  39. package/resources/pre-push-hook.sh +60 -0
  40. package/resources/rafter-security-skill.md +7 -11
@@ -4,23 +4,44 @@ import ora from "ora";
4
4
  import { detectRepo } from "../../utils/git.js";
5
5
  import { API, resolveKey, EXIT_GENERAL_ERROR, EXIT_QUOTA_EXHAUSTED } from "../../utils/api.js";
6
6
  import { handleScanStatus } from "./scan-status.js";
7
- export function createRunCommand() {
8
- return new Command("run")
9
- .alias("scan")
10
- .option("-r, --repo <repo>", "org/repo (default: current)")
11
- .option("-b, --branch <branch>", "branch (default: current else main)")
12
- .option("-k, --api-key <key>", "API key or RAFTER_API_KEY env var")
13
- .option("-f, --format <format>", "json | md", "md")
14
- .option("--skip-interactive", "do not wait for scan to complete")
15
- .option("--quiet", "suppress status messages")
16
- .action(async (opts) => {
17
- const key = resolveKey(opts.apiKey);
18
- let repo, branch;
7
+ /**
8
+ * Shared handler for the remote backend scan (used by both `rafter run` and `rafter scan` / `rafter scan remote`).
9
+ */
10
+ export async function runRemoteScan(opts) {
11
+ const key = resolveKey(opts.apiKey);
12
+ let repo, branch;
13
+ try {
14
+ ({ repo, branch } = detectRepo({ repo: opts.repo, branch: opts.branch, quiet: opts.quiet }));
15
+ }
16
+ catch (e) {
17
+ if (e instanceof Error) {
18
+ console.error(e.message);
19
+ }
20
+ else {
21
+ console.error(e);
22
+ }
23
+ process.exit(EXIT_GENERAL_ERROR);
24
+ }
25
+ if (!opts.quiet) {
26
+ const spinner = ora("Submitting scan").start();
19
27
  try {
20
- ({ repo, branch } = detectRepo({ repo: opts.repo, branch: opts.branch, quiet: opts.quiet }));
28
+ const { data } = await axios.post(`${API}/static/scan`, { repository_name: repo, branch_name: branch }, { headers: { "x-api-key": key } });
29
+ spinner.succeed(`Scan ID: ${data.scan_id}`);
30
+ if (opts.skipInteractive)
31
+ return;
32
+ const exitCode = await handleScanStatus(data.scan_id, { "x-api-key": key }, opts.format ?? "md", opts.quiet);
33
+ process.exit(exitCode);
21
34
  }
22
35
  catch (e) {
23
- if (e instanceof Error) {
36
+ spinner.fail("Request failed");
37
+ if (e.response?.status === 429) {
38
+ console.error("Quota exhausted");
39
+ process.exit(EXIT_QUOTA_EXHAUSTED);
40
+ }
41
+ else if (e.response?.data) {
42
+ console.error(e.response.data);
43
+ }
44
+ else if (e instanceof Error) {
24
45
  console.error(e.message);
25
46
  }
26
47
  else {
@@ -28,57 +49,44 @@ export function createRunCommand() {
28
49
  }
29
50
  process.exit(EXIT_GENERAL_ERROR);
30
51
  }
31
- if (!opts.quiet) {
32
- const spinner = ora("Submitting scan").start();
33
- try {
34
- const { data } = await axios.post(`${API}/static/scan`, { repository_name: repo, branch_name: branch }, { headers: { "x-api-key": key } });
35
- spinner.succeed(`Scan ID: ${data.scan_id}`);
36
- if (opts.skipInteractive)
37
- return;
38
- const exitCode = await handleScanStatus(data.scan_id, { "x-api-key": key }, opts.format, opts.quiet);
39
- process.exit(exitCode);
52
+ }
53
+ else {
54
+ try {
55
+ const { data } = await axios.post(`${API}/static/scan`, { repository_name: repo, branch_name: branch }, { headers: { "x-api-key": key } });
56
+ if (opts.skipInteractive)
57
+ return;
58
+ const exitCode = await handleScanStatus(data.scan_id, { "x-api-key": key }, opts.format ?? "md", opts.quiet);
59
+ process.exit(exitCode);
60
+ }
61
+ catch (e) {
62
+ if (e.response?.status === 429) {
63
+ process.exit(EXIT_QUOTA_EXHAUSTED);
40
64
  }
41
- catch (e) {
42
- spinner.fail("Request failed");
43
- if (e.response?.status === 429) {
44
- console.error("Quota exhausted");
45
- process.exit(EXIT_QUOTA_EXHAUSTED);
46
- }
47
- else if (e.response?.data) {
48
- console.error(e.response.data);
49
- }
50
- else if (e instanceof Error) {
51
- console.error(e.message);
52
- }
53
- else {
54
- console.error(e);
55
- }
56
- process.exit(EXIT_GENERAL_ERROR);
65
+ else if (e.response?.data) {
66
+ console.error(e.response.data);
57
67
  }
58
- }
59
- else {
60
- try {
61
- const { data } = await axios.post(`${API}/static/scan`, { repository_name: repo, branch_name: branch }, { headers: { "x-api-key": key } });
62
- if (opts.skipInteractive)
63
- return;
64
- const exitCode = await handleScanStatus(data.scan_id, { "x-api-key": key }, opts.format, opts.quiet);
65
- process.exit(exitCode);
68
+ else if (e instanceof Error) {
69
+ console.error(e.message);
66
70
  }
67
- catch (e) {
68
- if (e.response?.status === 429) {
69
- process.exit(EXIT_QUOTA_EXHAUSTED);
70
- }
71
- else if (e.response?.data) {
72
- console.error(e.response.data);
73
- }
74
- else if (e instanceof Error) {
75
- console.error(e.message);
76
- }
77
- else {
78
- console.error(e);
79
- }
80
- process.exit(EXIT_GENERAL_ERROR);
71
+ else {
72
+ console.error(e);
81
73
  }
74
+ process.exit(EXIT_GENERAL_ERROR);
82
75
  }
76
+ }
77
+ }
78
+ function addRunOptions(cmd) {
79
+ return cmd
80
+ .option("-r, --repo <repo>", "org/repo (default: current)")
81
+ .option("-b, --branch <branch>", "branch (default: current else main)")
82
+ .option("-k, --api-key <key>", "API key or RAFTER_API_KEY env var")
83
+ .option("-f, --format <format>", "json | md", "md")
84
+ .option("--skip-interactive", "do not wait for scan to complete")
85
+ .option("--quiet", "suppress status messages");
86
+ }
87
+ export function createRunCommand() {
88
+ return addRunOptions(new Command("run")
89
+ .description("Trigger a remote backend security scan")).action(async (opts) => {
90
+ await runRemoteScan(opts);
83
91
  });
84
92
  }
@@ -44,6 +44,12 @@ export function createCiInitCommand() {
44
44
  }
45
45
  }
46
46
  console.log(` ${opts.withBackend ? "3" : "2"}. Commit and push to trigger the pipeline`);
47
+ if (platform === "github") {
48
+ console.log();
49
+ console.log("Alternatives:");
50
+ console.log(" - GitHub Action: uses: Raftersecurity/rafter-cli@v0");
51
+ console.log(" - Pre-commit: https://github.com/Raftersecurity/rafter-cli#pre-commit-framework");
52
+ }
47
53
  console.log();
48
54
  });
49
55
  }
@@ -68,6 +74,7 @@ function generateTemplate(platform, withBackend) {
68
74
  }
69
75
  function githubTemplate(withBackend) {
70
76
  let yaml = `# Generated by: rafter ci init
77
+ # Alternative: uses: Raftersecurity/rafter-cli@v0
71
78
  name: Rafter Security
72
79
 
73
80
  on:
@@ -89,7 +96,7 @@ jobs:
89
96
  run: npm install -g @rafter-security/cli
90
97
 
91
98
  - name: Scan for secrets
92
- run: rafter agent scan . --quiet
99
+ run: rafter scan local . --quiet
93
100
  `;
94
101
  if (withBackend) {
95
102
  yaml += `
@@ -120,7 +127,7 @@ secret-scan:
120
127
  image: node:20
121
128
  script:
122
129
  - npm install -g @rafter-security/cli
123
- - rafter agent scan . --quiet
130
+ - rafter scan local . --quiet
124
131
  rules:
125
132
  - if: $CI_PIPELINE_SOURCE == "push"
126
133
  - if: $CI_PIPELINE_SOURCE == "merge_request_event"
@@ -158,7 +165,7 @@ jobs:
158
165
  command: npm install -g @rafter-security/cli
159
166
  - run:
160
167
  name: Scan for secrets
161
- command: rafter agent scan . --quiet
168
+ command: rafter scan local . --quiet
162
169
  `;
163
170
  if (withBackend) {
164
171
  yaml += `