@vibecodeqa/cli 0.39.0 → 0.39.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.
@@ -1,3 +1,5 @@
1
- /** Secret detection — delegates to gitleaks when available, falls back to built-in regex. */
1
+ /** Secret detection — delegates to gitleaks when available; otherwise scans with
2
+ * secretlint's recommended ruleset (broad coverage) PLUS our own patterns (which
3
+ * add LLM keys — OpenAI/Anthropic — that secretlint's preset doesn't cover). */
2
4
  import type { CheckResult } from "../types.js";
3
- export declare function runSecrets(cwd: string): CheckResult;
5
+ export declare function runSecrets(cwd: string): Promise<CheckResult>;
@@ -1,9 +1,20 @@
1
- /** Secret detection — delegates to gitleaks when available, falls back to built-in regex. */
1
+ /** Secret detection — delegates to gitleaks when available; otherwise scans with
2
+ * secretlint's recommended ruleset (broad coverage) PLUS our own patterns (which
3
+ * add LLM keys — OpenAI/Anthropic — that secretlint's preset doesn't cover). */
2
4
  import { existsSync, readFileSync } from "node:fs";
3
5
  import { join } from "node:path";
6
+ import { lintSource } from "@secretlint/core";
7
+ import { creator as secretlintPreset } from "@secretlint/secretlint-rule-preset-recommend";
4
8
  import { collectAllFiles } from "../fs-utils.js";
5
9
  import { gradeFromScore } from "../types.js";
6
10
  import { run } from "./exec.js";
11
+ const SECRETLINT_CONFIG = { rules: [{ id: "@secretlint/secretlint-rule-preset-recommend", rule: secretlintPreset }] };
12
+ /** Human-readable kind from a secretlint message, without leaking the secret value. */
13
+ function secretlintKind(msg) {
14
+ if (msg.messageId)
15
+ return msg.messageId.replace(/_/g, " ").toLowerCase();
16
+ return (msg.ruleId ?? "secret").replace(/.*secretlint-rule-/, "");
17
+ }
7
18
  /** Try running gitleaks for secret detection. Returns true if gitleaks ran. */
8
19
  function tryGitleaks(cwd, issues) {
9
20
  const { stdout, ok } = run("gitleaks detect --no-git --report-format json --report-path /dev/stdout 2>/dev/null", cwd, 30_000);
@@ -58,37 +69,61 @@ const SECRET_PATTERNS = [
58
69
  pattern: /(?:password|secret|api_key|apikey|token|auth)\s*[:=]\s*['"][A-Za-z0-9+/=]{20,}['"]/,
59
70
  },
60
71
  ];
61
- export function runSecrets(cwd) {
62
- const start = Date.now();
63
- const issues = [];
64
- // Try gitleaks first (industry standard, 800+ patterns)
65
- const gitleaksResult = tryGitleaks(cwd, issues);
66
- const tool = gitleaksResult ? "gitleaks" : "built-in";
67
- if (!gitleaksResult) {
68
- // Fallback: built-in regex patterns
69
- const sourceFiles = collectAllFiles(cwd, { extraExts: true });
70
- for (const sf of sourceFiles) {
71
- if (sf.isTest || sf.path.includes("__mock"))
72
+ function scanPatterns(files, add) {
73
+ for (const sf of files) {
74
+ const lines = sf.content.split("\n");
75
+ for (let i = 0; i < lines.length; i++) {
76
+ const line = lines[i];
77
+ if (line.trim().startsWith("//") || line.trim().startsWith("*"))
72
78
  continue;
73
- const lines = sf.content.split("\n");
74
- for (let i = 0; i < lines.length; i++) {
75
- const line = lines[i];
76
- if (line.trim().startsWith("//") || line.trim().startsWith("*"))
77
- continue;
78
- for (const { name, pattern } of SECRET_PATTERNS) {
79
- if (pattern.test(line)) {
80
- issues.push({
81
- severity: "error",
82
- message: `Possible ${name}`,
83
- file: sf.path,
84
- line: i + 1,
85
- rule: "secret-detected",
86
- });
87
- }
88
- }
79
+ for (const { name, pattern } of SECRET_PATTERNS) {
80
+ if (pattern.test(line))
81
+ add({ severity: "error", message: `Possible ${name}`, file: sf.path, line: i + 1, rule: "secret-detected" });
89
82
  }
90
83
  }
91
84
  }
85
+ }
86
+ async function scanSecretlint(files, add) {
87
+ for (const sf of files) {
88
+ try {
89
+ const result = await lintSource({
90
+ source: { filePath: sf.path, content: sf.content, contentType: "text" },
91
+ options: { config: SECRETLINT_CONFIG },
92
+ });
93
+ for (const m of result.messages) {
94
+ add({ severity: "error", message: `Possible ${secretlintKind(m)} — remove and rotate`, file: sf.path, line: m.loc?.start?.line ?? 1, rule: "secret-detected" });
95
+ }
96
+ }
97
+ catch {
98
+ /* unparseable file — skip */
99
+ }
100
+ }
101
+ }
102
+ /** Built-in fallback when gitleaks isn't installed: curated patterns first (nice
103
+ * names + LLM keys, winning dedup), then secretlint's broad ruleset augments. */
104
+ async function scanFallback(cwd) {
105
+ const files = collectAllFiles(cwd, { extraExts: true }).filter((sf) => !sf.isTest && !sf.path.includes("__mock"));
106
+ const issues = [];
107
+ const seen = new Set();
108
+ const add = (iss) => {
109
+ const key = `${iss.file}:${iss.line}`;
110
+ if (!seen.has(key)) {
111
+ seen.add(key);
112
+ issues.push(iss);
113
+ }
114
+ };
115
+ scanPatterns(files, add);
116
+ await scanSecretlint(files, add);
117
+ return issues;
118
+ }
119
+ export async function runSecrets(cwd) {
120
+ const start = Date.now();
121
+ const issues = [];
122
+ // Try gitleaks first (industry standard, 800+ patterns)
123
+ const gitleaksResult = tryGitleaks(cwd, issues);
124
+ const tool = gitleaksResult ? "gitleaks" : "secretlint";
125
+ if (!gitleaksResult)
126
+ issues.push(...(await scanFallback(cwd)));
92
127
  // ── .env file audit ──
93
128
  const envFiles = [".env", ".env.local", ".env.production", ".env.development"];
94
129
  const gitignore = existsSync(join(cwd, ".gitignore")) ? readFileSync(join(cwd, ".gitignore"), "utf-8") : "";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibecodeqa/cli",
3
- "version": "0.39.0",
3
+ "version": "0.39.1",
4
4
  "description": "Code health scanner for the AI coding era. 25 checks, zero config, full report.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -54,6 +54,8 @@
54
54
  },
55
55
  "dependencies": {
56
56
  "@jscpd/core": "^4.2.4",
57
+ "@secretlint/core": "^13.0.2",
58
+ "@secretlint/secretlint-rule-preset-recommend": "^13.0.2",
57
59
  "dependency-cruiser": "^17.4.3",
58
60
  "ink": "^5.2.1",
59
61
  "react": "^18.3.1"