@kevinrabun/judges 3.61.0 → 3.62.0
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/CHANGELOG.md +7 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +56 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/batch-review.d.ts +5 -0
- package/dist/commands/batch-review.d.ts.map +1 -0
- package/dist/commands/batch-review.js +181 -0
- package/dist/commands/batch-review.js.map +1 -0
- package/dist/commands/custom-rule.d.ts +5 -0
- package/dist/commands/custom-rule.d.ts.map +1 -0
- package/dist/commands/custom-rule.js +211 -0
- package/dist/commands/custom-rule.js.map +1 -0
- package/dist/commands/diff-review.d.ts +5 -0
- package/dist/commands/diff-review.d.ts.map +1 -0
- package/dist/commands/diff-review.js +191 -0
- package/dist/commands/diff-review.js.map +1 -0
- package/dist/commands/focus-area.d.ts +6 -0
- package/dist/commands/focus-area.d.ts.map +1 -0
- package/dist/commands/focus-area.js +193 -0
- package/dist/commands/focus-area.js.map +1 -0
- package/dist/commands/review-compare.d.ts +5 -0
- package/dist/commands/review-compare.d.ts.map +1 -0
- package/dist/commands/review-compare.js +201 -0
- package/dist/commands/review-compare.js.map +1 -0
- package/dist/commands/review-explain.d.ts +6 -0
- package/dist/commands/review-explain.d.ts.map +1 -0
- package/dist/commands/review-explain.js +195 -0
- package/dist/commands/review-explain.js.map +1 -0
- package/dist/commands/review-gate.d.ts +5 -0
- package/dist/commands/review-gate.d.ts.map +1 -0
- package/dist/commands/review-gate.js +213 -0
- package/dist/commands/review-gate.js.map +1 -0
- package/dist/commands/severity-tune.d.ts +5 -0
- package/dist/commands/severity-tune.d.ts.map +1 -0
- package/dist/commands/severity-tune.js +209 -0
- package/dist/commands/severity-tune.js.map +1 -0
- package/dist/commands/trend-report.d.ts +5 -0
- package/dist/commands/trend-report.d.ts.map +1 -0
- package/dist/commands/trend-report.js +149 -0
- package/dist/commands/trend-report.js.map +1 -0
- package/package.json +1 -1
- package/server.json +2 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review-gate.d.ts","sourceRoot":"","sources":["../../src/commands/review-gate.ts"],"names":[],"mappings":"AAAA;;GAEG;AAoKH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAoFlD"}
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Review-gate — CI/CD quality gate with configurable pass/fail thresholds.
|
|
3
|
+
*/
|
|
4
|
+
import { readFileSync, readdirSync, statSync } from "fs";
|
|
5
|
+
import { join, extname } from "path";
|
|
6
|
+
// ─── Default thresholds ────────────────────────────────────────────────────
|
|
7
|
+
function defaultConfig() {
|
|
8
|
+
return {
|
|
9
|
+
maxCritical: 0,
|
|
10
|
+
maxHigh: 5,
|
|
11
|
+
maxTotal: 25,
|
|
12
|
+
blockOnSecurityFindings: true,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
// ─── Patterns ──────────────────────────────────────────────────────────────
|
|
16
|
+
const GATE_PATTERNS = [
|
|
17
|
+
{
|
|
18
|
+
name: "hardcoded-secret",
|
|
19
|
+
severity: "critical",
|
|
20
|
+
regex: /(?:password|secret|api_key|token)\s*[:=]\s*["'][^"']{8,}/i,
|
|
21
|
+
security: true,
|
|
22
|
+
},
|
|
23
|
+
{ name: "eval-usage", severity: "critical", regex: /\beval\s*\(/, security: true },
|
|
24
|
+
{ name: "sql-injection", severity: "critical", regex: /(?:query|execute)\s*\(\s*["'`].*\+/, security: true },
|
|
25
|
+
{ name: "xss-risk", severity: "high", regex: /innerHTML\s*=|\.html\s*\(|document\.write\s*\(/, security: true },
|
|
26
|
+
{
|
|
27
|
+
name: "unsafe-deserialization",
|
|
28
|
+
severity: "critical",
|
|
29
|
+
regex: /JSON\.parse\s*\(\s*(?:req|request|body|params|query)\b/,
|
|
30
|
+
security: true,
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
name: "command-injection",
|
|
34
|
+
severity: "critical",
|
|
35
|
+
regex: /exec(?:Sync)?\s*\(\s*(?:`[^`]*\$\{|["'][^"']*\+)/,
|
|
36
|
+
security: true,
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: "path-traversal",
|
|
40
|
+
severity: "high",
|
|
41
|
+
regex: /(?:readFile|writeFile|createReadStream)\s*\([^)]*(?:req|params|query)/,
|
|
42
|
+
security: true,
|
|
43
|
+
},
|
|
44
|
+
{ name: "empty-catch", severity: "medium", regex: /catch\s*\([^)]*\)\s*\{\s*\}/, security: false },
|
|
45
|
+
{ name: "any-type", severity: "low", regex: /:\s*any\b/, security: false },
|
|
46
|
+
{ name: "console-log", severity: "low", regex: /console\.log\s*\(/, security: false },
|
|
47
|
+
{ name: "deprecated-api", severity: "medium", regex: /new\s+Buffer\s*\(|\.substr\s*\(/, security: false },
|
|
48
|
+
{ name: "todo-in-code", severity: "low", regex: /\/\/\s*(?:TODO|FIXME|HACK)\b/i, security: false },
|
|
49
|
+
];
|
|
50
|
+
// ─── Helpers ────────────────────────────────────────────────────────────────
|
|
51
|
+
function collectSourceFiles(dir) {
|
|
52
|
+
const exts = new Set([".ts", ".js", ".tsx", ".jsx", ".py", ".java", ".go", ".rs", ".cs"]);
|
|
53
|
+
const files = [];
|
|
54
|
+
const skipDirs = new Set(["node_modules", ".git", "dist", "build", "coverage"]);
|
|
55
|
+
function walk(d) {
|
|
56
|
+
let entries;
|
|
57
|
+
try {
|
|
58
|
+
entries = readdirSync(d);
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
for (const name of entries) {
|
|
64
|
+
if (skipDirs.has(name))
|
|
65
|
+
continue;
|
|
66
|
+
const full = join(d, name);
|
|
67
|
+
try {
|
|
68
|
+
const st = statSync(full);
|
|
69
|
+
if (st.isDirectory())
|
|
70
|
+
walk(full);
|
|
71
|
+
else if (exts.has(extname(name)))
|
|
72
|
+
files.push(full);
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
// skip
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
walk(dir);
|
|
80
|
+
return files;
|
|
81
|
+
}
|
|
82
|
+
function runGate(files, config) {
|
|
83
|
+
const findings = [];
|
|
84
|
+
for (const filePath of files) {
|
|
85
|
+
let content;
|
|
86
|
+
try {
|
|
87
|
+
content = readFileSync(filePath, "utf-8");
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
const lines = content.split("\n");
|
|
93
|
+
for (let i = 0; i < lines.length; i++) {
|
|
94
|
+
for (const pat of GATE_PATTERNS) {
|
|
95
|
+
if (pat.regex.test(lines[i])) {
|
|
96
|
+
findings.push({ pattern: pat.name, severity: pat.severity, file: filePath, line: i + 1 });
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
const counts = { critical: 0, high: 0, medium: 0, low: 0, total: findings.length };
|
|
102
|
+
for (const f of findings) {
|
|
103
|
+
if (f.severity === "critical")
|
|
104
|
+
counts.critical++;
|
|
105
|
+
else if (f.severity === "high")
|
|
106
|
+
counts.high++;
|
|
107
|
+
else if (f.severity === "medium")
|
|
108
|
+
counts.medium++;
|
|
109
|
+
else
|
|
110
|
+
counts.low++;
|
|
111
|
+
}
|
|
112
|
+
const failReasons = [];
|
|
113
|
+
if (counts.critical > config.maxCritical) {
|
|
114
|
+
failReasons.push(`Critical findings (${counts.critical}) exceed threshold (${config.maxCritical})`);
|
|
115
|
+
}
|
|
116
|
+
if (counts.high > config.maxHigh) {
|
|
117
|
+
failReasons.push(`High findings (${counts.high}) exceed threshold (${config.maxHigh})`);
|
|
118
|
+
}
|
|
119
|
+
if (counts.total > config.maxTotal) {
|
|
120
|
+
failReasons.push(`Total findings (${counts.total}) exceed threshold (${config.maxTotal})`);
|
|
121
|
+
}
|
|
122
|
+
if (config.blockOnSecurityFindings) {
|
|
123
|
+
const securityFindings = findings.filter((f) => GATE_PATTERNS.find((p) => p.name === f.pattern && p.security));
|
|
124
|
+
if (securityFindings.length > 0) {
|
|
125
|
+
failReasons.push(`${securityFindings.length} security-related findings detected`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return {
|
|
129
|
+
passed: failReasons.length === 0,
|
|
130
|
+
findings,
|
|
131
|
+
counts,
|
|
132
|
+
thresholds: config,
|
|
133
|
+
failReasons,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
// ─── CLI ────────────────────────────────────────────────────────────────────
|
|
137
|
+
export function runReviewGate(argv) {
|
|
138
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
139
|
+
console.log(`
|
|
140
|
+
judges review-gate — CI/CD quality gate with configurable thresholds
|
|
141
|
+
|
|
142
|
+
Usage:
|
|
143
|
+
judges review-gate [dir] Run quality gate check
|
|
144
|
+
judges review-gate --max-critical 0 Set critical threshold
|
|
145
|
+
judges review-gate --max-high 3 Set high threshold
|
|
146
|
+
judges review-gate --max-total 20 Set total threshold
|
|
147
|
+
judges review-gate --format json JSON output
|
|
148
|
+
|
|
149
|
+
Options:
|
|
150
|
+
[dir] Target directory (default: .)
|
|
151
|
+
--max-critical <n> Max critical findings (default: 0)
|
|
152
|
+
--max-high <n> Max high findings (default: 5)
|
|
153
|
+
--max-total <n> Max total findings (default: 25)
|
|
154
|
+
--no-block-security Don't fail on security findings
|
|
155
|
+
--format json JSON output
|
|
156
|
+
--help, -h Show this help
|
|
157
|
+
|
|
158
|
+
Exit code 1 if gate fails. Designed for CI/CD pipeline integration.
|
|
159
|
+
`);
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
|
|
163
|
+
const dir = argv.find((a) => !a.startsWith("-") &&
|
|
164
|
+
a !== "review-gate" &&
|
|
165
|
+
argv[argv.indexOf(a) - 1] !== "--format" &&
|
|
166
|
+
argv[argv.indexOf(a) - 1] !== "--max-critical" &&
|
|
167
|
+
argv[argv.indexOf(a) - 1] !== "--max-high" &&
|
|
168
|
+
argv[argv.indexOf(a) - 1] !== "--max-total") || ".";
|
|
169
|
+
const config = defaultConfig();
|
|
170
|
+
const getNum = (flag, fallback) => {
|
|
171
|
+
const val = argv.find((_a, i) => argv[i - 1] === flag);
|
|
172
|
+
return val ? parseInt(val, 10) : fallback;
|
|
173
|
+
};
|
|
174
|
+
config.maxCritical = getNum("--max-critical", config.maxCritical);
|
|
175
|
+
config.maxHigh = getNum("--max-high", config.maxHigh);
|
|
176
|
+
config.maxTotal = getNum("--max-total", config.maxTotal);
|
|
177
|
+
if (argv.includes("--no-block-security"))
|
|
178
|
+
config.blockOnSecurityFindings = false;
|
|
179
|
+
const files = collectSourceFiles(dir);
|
|
180
|
+
const result = runGate(files, config);
|
|
181
|
+
if (format === "json") {
|
|
182
|
+
console.log(JSON.stringify(result, null, 2));
|
|
183
|
+
if (!result.passed)
|
|
184
|
+
process.exitCode = 1;
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
const icon = result.passed ? "✅" : "❌";
|
|
188
|
+
console.log(`\n Quality Gate: ${icon} ${result.passed ? "PASSED" : "FAILED"}\n ─────────────────────────────`);
|
|
189
|
+
console.log(` Files scanned: ${files.length}`);
|
|
190
|
+
console.log(` Total findings: ${result.counts.total}`);
|
|
191
|
+
console.log(` Critical: ${result.counts.critical}/${config.maxCritical} High: ${result.counts.high}/${config.maxHigh} Total: ${result.counts.total}/${config.maxTotal}`);
|
|
192
|
+
if (result.failReasons.length > 0) {
|
|
193
|
+
console.log("\n Fail reasons:");
|
|
194
|
+
for (const reason of result.failReasons)
|
|
195
|
+
console.log(` ❌ ${reason}`);
|
|
196
|
+
}
|
|
197
|
+
if (result.findings.length > 0 && result.findings.length <= 20) {
|
|
198
|
+
console.log("\n Findings:");
|
|
199
|
+
for (const f of result.findings) {
|
|
200
|
+
console.log(` [${f.severity}] ${f.pattern} — ${f.file}:${f.line}`);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
else if (result.findings.length > 20) {
|
|
204
|
+
console.log(`\n Showing first 20 of ${result.findings.length} findings:`);
|
|
205
|
+
for (const f of result.findings.slice(0, 20)) {
|
|
206
|
+
console.log(` [${f.severity}] ${f.pattern} — ${f.file}:${f.line}`);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
console.log();
|
|
210
|
+
if (!result.passed)
|
|
211
|
+
process.exitCode = 1;
|
|
212
|
+
}
|
|
213
|
+
//# sourceMappingURL=review-gate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review-gate.js","sourceRoot":"","sources":["../../src/commands/review-gate.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,IAAI,CAAC;AACzD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AA0BrC,8EAA8E;AAE9E,SAAS,aAAa;IACpB,OAAO;QACL,WAAW,EAAE,CAAC;QACd,OAAO,EAAE,CAAC;QACV,QAAQ,EAAE,EAAE;QACZ,uBAAuB,EAAE,IAAI;KAC9B,CAAC;AACJ,CAAC;AAED,8EAA8E;AAE9E,MAAM,aAAa,GAA2E;IAC5F;QACE,IAAI,EAAE,kBAAkB;QACxB,QAAQ,EAAE,UAAU;QACpB,KAAK,EAAE,2DAA2D;QAClE,QAAQ,EAAE,IAAI;KACf;IACD,EAAE,IAAI,EAAE,YAAY,EAAE,QAAQ,EAAE,UAAU,EAAE,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,IAAI,EAAE;IAClF,EAAE,IAAI,EAAE,eAAe,EAAE,QAAQ,EAAE,UAAU,EAAE,KAAK,EAAE,oCAAoC,EAAE,QAAQ,EAAE,IAAI,EAAE;IAC5G,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,gDAAgD,EAAE,QAAQ,EAAE,IAAI,EAAE;IAC/G;QACE,IAAI,EAAE,wBAAwB;QAC9B,QAAQ,EAAE,UAAU;QACpB,KAAK,EAAE,wDAAwD;QAC/D,QAAQ,EAAE,IAAI;KACf;IACD;QACE,IAAI,EAAE,mBAAmB;QACzB,QAAQ,EAAE,UAAU;QACpB,KAAK,EAAE,kDAAkD;QACzD,QAAQ,EAAE,IAAI;KACf;IACD;QACE,IAAI,EAAE,gBAAgB;QACtB,QAAQ,EAAE,MAAM;QAChB,KAAK,EAAE,uEAAuE;QAC9E,QAAQ,EAAE,IAAI;KACf;IACD,EAAE,IAAI,EAAE,aAAa,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,6BAA6B,EAAE,QAAQ,EAAE,KAAK,EAAE;IAClG,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,KAAK,EAAE;IAC1E,EAAE,IAAI,EAAE,aAAa,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,mBAAmB,EAAE,QAAQ,EAAE,KAAK,EAAE;IACrF,EAAE,IAAI,EAAE,gBAAgB,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,iCAAiC,EAAE,QAAQ,EAAE,KAAK,EAAE;IACzG,EAAE,IAAI,EAAE,cAAc,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,+BAA+B,EAAE,QAAQ,EAAE,KAAK,EAAE;CACnG,CAAC;AAEF,+EAA+E;AAE/E,SAAS,kBAAkB,CAAC,GAAW;IACrC,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;IAC1F,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,CAAC,cAAc,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC;IAEhF,SAAS,IAAI,CAAC,CAAS;QACrB,IAAI,OAAiB,CAAC;QACtB,IAAI,CAAC;YACH,OAAO,GAAG,WAAW,CAAC,CAAC,CAAwB,CAAC;QAClD,CAAC;QAAC,MAAM,CAAC;YACP,OAAO;QACT,CAAC;QACD,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;YAC3B,IAAI,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC;gBAAE,SAAS;YACjC,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;YAC3B,IAAI,CAAC;gBACH,MAAM,EAAE,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;gBAC1B,IAAI,EAAE,CAAC,WAAW,EAAE;oBAAE,IAAI,CAAC,IAAI,CAAC,CAAC;qBAC5B,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;oBAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACrD,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO;YACT,CAAC;QACH,CAAC;IACH,CAAC;IACD,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,OAAO,CAAC,KAAe,EAAE,MAAkB;IAClD,MAAM,QAAQ,GAAkB,EAAE,CAAC;IAEnC,KAAK,MAAM,QAAQ,IAAI,KAAK,EAAE,CAAC;QAC7B,IAAI,OAAe,CAAC;QACpB,IAAI,CAAC;YACH,OAAO,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAC5C,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;QACD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAElC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACtC,KAAK,MAAM,GAAG,IAAI,aAAa,EAAE,CAAC;gBAChC,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;oBAC7B,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,IAAI,EAAE,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBAC5F,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,MAAM,GAAG,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC;IACnF,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,IAAI,CAAC,CAAC,QAAQ,KAAK,UAAU;YAAE,MAAM,CAAC,QAAQ,EAAE,CAAC;aAC5C,IAAI,CAAC,CAAC,QAAQ,KAAK,MAAM;YAAE,MAAM,CAAC,IAAI,EAAE,CAAC;aACzC,IAAI,CAAC,CAAC,QAAQ,KAAK,QAAQ;YAAE,MAAM,CAAC,MAAM,EAAE,CAAC;;YAC7C,MAAM,CAAC,GAAG,EAAE,CAAC;IACpB,CAAC;IAED,MAAM,WAAW,GAAa,EAAE,CAAC;IACjC,IAAI,MAAM,CAAC,QAAQ,GAAG,MAAM,CAAC,WAAW,EAAE,CAAC;QACzC,WAAW,CAAC,IAAI,CAAC,sBAAsB,MAAM,CAAC,QAAQ,uBAAuB,MAAM,CAAC,WAAW,GAAG,CAAC,CAAC;IACtG,CAAC;IACD,IAAI,MAAM,CAAC,IAAI,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC;QACjC,WAAW,CAAC,IAAI,CAAC,kBAAkB,MAAM,CAAC,IAAI,uBAAuB,MAAM,CAAC,OAAO,GAAG,CAAC,CAAC;IAC1F,CAAC;IACD,IAAI,MAAM,CAAC,KAAK,GAAG,MAAM,CAAC,QAAQ,EAAE,CAAC;QACnC,WAAW,CAAC,IAAI,CAAC,mBAAmB,MAAM,CAAC,KAAK,uBAAuB,MAAM,CAAC,QAAQ,GAAG,CAAC,CAAC;IAC7F,CAAC;IACD,IAAI,MAAM,CAAC,uBAAuB,EAAE,CAAC;QACnC,MAAM,gBAAgB,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;QAC/G,IAAI,gBAAgB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAChC,WAAW,CAAC,IAAI,CAAC,GAAG,gBAAgB,CAAC,MAAM,qCAAqC,CAAC,CAAC;QACpF,CAAC;IACH,CAAC;IAED,OAAO;QACL,MAAM,EAAE,WAAW,CAAC,MAAM,KAAK,CAAC;QAChC,QAAQ;QACR,MAAM;QACN,UAAU,EAAE,MAAM;QAClB,WAAW;KACZ,CAAC;AACJ,CAAC;AAED,+EAA+E;AAE/E,MAAM,UAAU,aAAa,CAAC,IAAc;IAC1C,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACnD,OAAO,CAAC,GAAG,CAAC;;;;;;;;;;;;;;;;;;;;CAoBf,CAAC,CAAC;QACC,OAAO;IACT,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,EAAU,EAAE,CAAS,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,UAAU,CAAC,IAAI,MAAM,CAAC;IAC1F,MAAM,GAAG,GACP,IAAI,CAAC,IAAI,CACP,CAAC,CAAC,EAAE,EAAE,CACJ,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC;QAClB,CAAC,KAAK,aAAa;QACnB,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,UAAU;QACxC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,gBAAgB;QAC9C,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,YAAY;QAC1C,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,aAAa,CAC9C,IAAI,GAAG,CAAC;IAEX,MAAM,MAAM,GAAG,aAAa,EAAE,CAAC;IAC/B,MAAM,MAAM,GAAG,CAAC,IAAY,EAAE,QAAgB,EAAU,EAAE;QACxD,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,EAAU,EAAE,CAAS,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC;QACvE,OAAO,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;IAC5C,CAAC,CAAC;IACF,MAAM,CAAC,WAAW,GAAG,MAAM,CAAC,gBAAgB,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC;IAClE,MAAM,CAAC,OAAO,GAAG,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;IACtD,MAAM,CAAC,QAAQ,GAAG,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;IACzD,IAAI,IAAI,CAAC,QAAQ,CAAC,qBAAqB,CAAC;QAAE,MAAM,CAAC,uBAAuB,GAAG,KAAK,CAAC;IAEjF,MAAM,KAAK,GAAG,kBAAkB,CAAC,GAAG,CAAC,CAAC;IACtC,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IAEtC,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;QACtB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAC7C,IAAI,CAAC,MAAM,CAAC,MAAM;YAAE,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACzC,OAAO;IACT,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;IACvC,OAAO,CAAC,GAAG,CAAC,qBAAqB,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,mCAAmC,CAAC,CAAC;IACjH,OAAO,CAAC,GAAG,CAAC,sBAAsB,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;IAClD,OAAO,CAAC,GAAG,CAAC,uBAAuB,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;IAC1D,OAAO,CAAC,GAAG,CACT,iBAAiB,MAAM,CAAC,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,WAAW,WAAW,MAAM,CAAC,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,OAAO,YAAY,MAAM,CAAC,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,QAAQ,EAAE,CACjK,CAAC;IAEF,IAAI,MAAM,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAClC,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;QACnC,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,WAAW;YAAE,OAAO,CAAC,GAAG,CAAC,WAAW,MAAM,EAAE,CAAC,CAAC;IAC5E,CAAC;IAED,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC;QAC/D,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;QAC/B,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;YAChC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC,OAAO,MAAM,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QAC1E,CAAC;IACH,CAAC;SAAM,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;QACvC,OAAO,CAAC,GAAG,CAAC,6BAA6B,MAAM,CAAC,QAAQ,CAAC,MAAM,YAAY,CAAC,CAAC;QAC7E,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;YAC7C,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC,OAAO,MAAM,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QAC1E,CAAC;IACH,CAAC;IAED,OAAO,CAAC,GAAG,EAAE,CAAC;IACd,IAAI,CAAC,MAAM,CAAC,MAAM;QAAE,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;AAC3C,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"severity-tune.d.ts","sourceRoot":"","sources":["../../src/commands/severity-tune.ts"],"names":[],"mappings":"AAAA;;GAEG;AA+LH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAqDpD"}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Severity-tune — Auto-calibrate severity levels based on project patterns.
|
|
3
|
+
*/
|
|
4
|
+
import { readFileSync, readdirSync, statSync } from "fs";
|
|
5
|
+
import { join, extname } from "path";
|
|
6
|
+
// ─── Patterns with default severity ────────────────────────────────────────
|
|
7
|
+
const TUNE_PATTERNS = [
|
|
8
|
+
{
|
|
9
|
+
name: "hardcoded-secret",
|
|
10
|
+
severity: "critical",
|
|
11
|
+
regex: /(?:password|secret|api_key|token)\s*[:=]\s*["'][^"']{8,}/i,
|
|
12
|
+
},
|
|
13
|
+
{ name: "eval-usage", severity: "critical", regex: /\beval\s*\(/ },
|
|
14
|
+
{ name: "sql-concat", severity: "critical", regex: /(?:query|execute)\s*\(\s*["'`].*\+/ },
|
|
15
|
+
{ name: "xss-risk", severity: "high", regex: /innerHTML\s*=|document\.write\s*\(/ },
|
|
16
|
+
{ name: "command-injection", severity: "critical", regex: /exec(?:Sync)?\s*\(\s*`[^`]*\$\{/ },
|
|
17
|
+
{ name: "empty-catch", severity: "medium", regex: /catch\s*\([^)]*\)\s*\{\s*\}/ },
|
|
18
|
+
{ name: "any-type", severity: "medium", regex: /:\s*any\b/ },
|
|
19
|
+
{ name: "unsafe-regex", severity: "high", regex: /new\s+RegExp\s*\([^)]*\+/ },
|
|
20
|
+
{ name: "deprecated-api", severity: "medium", regex: /new\s+Buffer\s*\(|\.substr\s*\(/ },
|
|
21
|
+
{ name: "console-log", severity: "low", regex: /console\.log\s*\(/ },
|
|
22
|
+
{ name: "todo-fixme", severity: "low", regex: /\/\/\s*(?:TODO|FIXME|HACK)\b/i },
|
|
23
|
+
{ name: "magic-number", severity: "low", regex: /(?:if|return|===?)\s*(?<!\w)\d{3,}(?!\w)/ },
|
|
24
|
+
{ name: "long-line", severity: "low", regex: /^.{200,}$/ },
|
|
25
|
+
{ name: "nested-ternary", severity: "medium", regex: /[?][^:]*[?]/ },
|
|
26
|
+
{ name: "god-function", severity: "medium", regex: /^(?:export\s+)?(?:async\s+)?function\s+\w+/ },
|
|
27
|
+
];
|
|
28
|
+
// ─── Helpers ────────────────────────────────────────────────────────────────
|
|
29
|
+
function collectSourceFiles(dir) {
|
|
30
|
+
const exts = new Set([".ts", ".js", ".tsx", ".jsx", ".py", ".java", ".go", ".rs", ".cs"]);
|
|
31
|
+
const files = [];
|
|
32
|
+
const skipDirs = new Set(["node_modules", ".git", "dist", "build", "coverage"]);
|
|
33
|
+
function walk(d) {
|
|
34
|
+
let entries;
|
|
35
|
+
try {
|
|
36
|
+
entries = readdirSync(d);
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
for (const name of entries) {
|
|
42
|
+
if (skipDirs.has(name))
|
|
43
|
+
continue;
|
|
44
|
+
const full = join(d, name);
|
|
45
|
+
try {
|
|
46
|
+
const st = statSync(full);
|
|
47
|
+
if (st.isDirectory())
|
|
48
|
+
walk(full);
|
|
49
|
+
else if (exts.has(extname(name)))
|
|
50
|
+
files.push(full);
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
// skip
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
walk(dir);
|
|
58
|
+
return files;
|
|
59
|
+
}
|
|
60
|
+
function analyzeSeverity(files) {
|
|
61
|
+
const patternData = new Map();
|
|
62
|
+
for (const pat of TUNE_PATTERNS) {
|
|
63
|
+
patternData.set(pat.name, { occurrences: 0, files: new Set(), severity: pat.severity });
|
|
64
|
+
}
|
|
65
|
+
for (const filePath of files) {
|
|
66
|
+
let content;
|
|
67
|
+
try {
|
|
68
|
+
content = readFileSync(filePath, "utf-8");
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
const lines = content.split("\n");
|
|
74
|
+
for (const pat of TUNE_PATTERNS) {
|
|
75
|
+
// Special handling for god-function: count functions > 80 lines
|
|
76
|
+
if (pat.name === "god-function") {
|
|
77
|
+
let funcCount = 0;
|
|
78
|
+
let inFunc = false;
|
|
79
|
+
let braceDepth = 0;
|
|
80
|
+
let funcLines = 0;
|
|
81
|
+
for (const line of lines) {
|
|
82
|
+
if (pat.regex.test(line) && !inFunc) {
|
|
83
|
+
inFunc = true;
|
|
84
|
+
braceDepth = 0;
|
|
85
|
+
funcLines = 0;
|
|
86
|
+
}
|
|
87
|
+
if (inFunc) {
|
|
88
|
+
funcLines++;
|
|
89
|
+
braceDepth += (line.match(/\{/g) || []).length;
|
|
90
|
+
braceDepth -= (line.match(/\}/g) || []).length;
|
|
91
|
+
if (braceDepth <= 0 && funcLines > 1) {
|
|
92
|
+
if (funcLines > 80)
|
|
93
|
+
funcCount++;
|
|
94
|
+
inFunc = false;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (funcCount > 0) {
|
|
99
|
+
const data = patternData.get(pat.name);
|
|
100
|
+
data.occurrences += funcCount;
|
|
101
|
+
data.files.add(filePath);
|
|
102
|
+
}
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
let hitCount = 0;
|
|
106
|
+
for (const line of lines) {
|
|
107
|
+
if (pat.regex.test(line))
|
|
108
|
+
hitCount++;
|
|
109
|
+
}
|
|
110
|
+
if (hitCount > 0) {
|
|
111
|
+
const data = patternData.get(pat.name);
|
|
112
|
+
data.occurrences += hitCount;
|
|
113
|
+
data.files.add(filePath);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
// Generate tuning recommendations
|
|
118
|
+
const recommendations = [];
|
|
119
|
+
const fileCount = files.length;
|
|
120
|
+
for (const [name, data] of patternData) {
|
|
121
|
+
if (data.occurrences === 0)
|
|
122
|
+
continue;
|
|
123
|
+
const filePercent = fileCount > 0 ? (data.files.size / fileCount) * 100 : 0;
|
|
124
|
+
let suggestedSeverity = data.severity;
|
|
125
|
+
let reason = "Current severity is appropriate";
|
|
126
|
+
// If a pattern appears in >50% of files, it may be a team convention — consider lowering severity
|
|
127
|
+
if (filePercent > 50 && data.severity !== "low") {
|
|
128
|
+
suggestedSeverity = data.severity === "critical" ? "high" : data.severity === "high" ? "medium" : "low";
|
|
129
|
+
reason = `Appears in ${Math.round(filePercent)}% of files — may be a project norm. Consider lowering severity.`;
|
|
130
|
+
}
|
|
131
|
+
// If a critical/high pattern appears very rarely, keep it — it's genuinely concerning
|
|
132
|
+
else if (data.occurrences <= 2 && (data.severity === "critical" || data.severity === "high")) {
|
|
133
|
+
reason = "Rare occurrence — keep current severity to catch regressions";
|
|
134
|
+
}
|
|
135
|
+
// If low-severity pattern is pervasive, suggest ignoring
|
|
136
|
+
else if (data.severity === "low" && data.occurrences > 100) {
|
|
137
|
+
suggestedSeverity = "info";
|
|
138
|
+
reason = `${data.occurrences} occurrences — consider ignoring to reduce noise`;
|
|
139
|
+
}
|
|
140
|
+
if (suggestedSeverity !== data.severity) {
|
|
141
|
+
recommendations.push({
|
|
142
|
+
pattern: name,
|
|
143
|
+
currentSeverity: data.severity,
|
|
144
|
+
occurrences: data.occurrences,
|
|
145
|
+
filesAffected: data.files.size,
|
|
146
|
+
suggestedSeverity,
|
|
147
|
+
reason,
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
recommendations.sort((a, b) => b.occurrences - a.occurrences);
|
|
152
|
+
const summary = recommendations.length === 0
|
|
153
|
+
? "All severity levels appear well-calibrated for this project."
|
|
154
|
+
: `${recommendations.length} severity adjustments recommended to reduce noise and improve signal.`;
|
|
155
|
+
return {
|
|
156
|
+
filesScanned: files.length,
|
|
157
|
+
patternsAnalyzed: TUNE_PATTERNS.length,
|
|
158
|
+
tuningRecommendations: recommendations,
|
|
159
|
+
summary,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
// ─── CLI ────────────────────────────────────────────────────────────────────
|
|
163
|
+
export function runSeverityTune(argv) {
|
|
164
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
165
|
+
console.log(`
|
|
166
|
+
judges severity-tune — Auto-calibrate severity levels for your project
|
|
167
|
+
|
|
168
|
+
Usage:
|
|
169
|
+
judges severity-tune [dir] Analyze and recommend adjustments
|
|
170
|
+
judges severity-tune --format json JSON output
|
|
171
|
+
|
|
172
|
+
Options:
|
|
173
|
+
[dir] Target directory (default: .)
|
|
174
|
+
--format json JSON output
|
|
175
|
+
--help, -h Show this help
|
|
176
|
+
|
|
177
|
+
Analyzes your codebase to determine if default severity levels match your
|
|
178
|
+
project's patterns. Recommends adjustments to reduce alert fatigue for
|
|
179
|
+
pervasive patterns while maintaining sensitivity for genuine issues.
|
|
180
|
+
`);
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
|
|
184
|
+
const dir = argv.find((a) => !a.startsWith("-") && a !== "severity-tune" && argv[argv.indexOf(a) - 1] !== "--format") || ".";
|
|
185
|
+
const files = collectSourceFiles(dir);
|
|
186
|
+
if (files.length === 0) {
|
|
187
|
+
console.log("No source files found.");
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
const result = analyzeSeverity(files);
|
|
191
|
+
if (format === "json") {
|
|
192
|
+
console.log(JSON.stringify(result, null, 2));
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
console.log(`\n Severity Tune\n ─────────────────────────────`);
|
|
196
|
+
console.log(` Files scanned: ${result.filesScanned}`);
|
|
197
|
+
console.log(` Patterns analyzed: ${result.patternsAnalyzed}`);
|
|
198
|
+
console.log(`\n 💡 ${result.summary}`);
|
|
199
|
+
if (result.tuningRecommendations.length > 0) {
|
|
200
|
+
console.log("\n Recommendations:");
|
|
201
|
+
for (const rec of result.tuningRecommendations) {
|
|
202
|
+
console.log(`\n ${rec.pattern}: ${rec.currentSeverity} → ${rec.suggestedSeverity}`);
|
|
203
|
+
console.log(` ${rec.occurrences} occurrences in ${rec.filesAffected} files`);
|
|
204
|
+
console.log(` ${rec.reason}`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
console.log();
|
|
208
|
+
}
|
|
209
|
+
//# sourceMappingURL=severity-tune.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"severity-tune.js","sourceRoot":"","sources":["../../src/commands/severity-tune.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trend-report.d.ts","sourceRoot":"","sources":["../../src/commands/trend-report.ts"],"names":[],"mappings":"AAAA;;GAEG;AAwFH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CA4GnD"}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trend-report — Track finding trends over time to show improvement trajectory.
|
|
3
|
+
*/
|
|
4
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
|
|
5
|
+
import { join } from "path";
|
|
6
|
+
// ─── Helpers ────────────────────────────────────────────────────────────────
|
|
7
|
+
function getTrendDir() {
|
|
8
|
+
return join(".", ".judges", "trends");
|
|
9
|
+
}
|
|
10
|
+
function loadSnapshots() {
|
|
11
|
+
const dir = getTrendDir();
|
|
12
|
+
const indexPath = join(dir, "trend-index.json");
|
|
13
|
+
if (!existsSync(indexPath))
|
|
14
|
+
return [];
|
|
15
|
+
try {
|
|
16
|
+
return JSON.parse(readFileSync(indexPath, "utf-8"));
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return [];
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function saveSnapshots(snapshots) {
|
|
23
|
+
const dir = getTrendDir();
|
|
24
|
+
if (!existsSync(dir))
|
|
25
|
+
mkdirSync(dir, { recursive: true });
|
|
26
|
+
writeFileSync(join(dir, "trend-index.json"), JSON.stringify(snapshots, null, 2), "utf-8");
|
|
27
|
+
}
|
|
28
|
+
function computeTrend(snapshots) {
|
|
29
|
+
if (snapshots.length === 0) {
|
|
30
|
+
return {
|
|
31
|
+
snapshots: [],
|
|
32
|
+
trend: "stable",
|
|
33
|
+
changePercent: 0,
|
|
34
|
+
averageFindings: 0,
|
|
35
|
+
bestSnapshot: null,
|
|
36
|
+
worstSnapshot: null,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
const sorted = [...snapshots].sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
|
|
40
|
+
const totalSum = sorted.reduce((s, snap) => s + snap.totalFindings, 0);
|
|
41
|
+
const avg = Math.round(totalSum / sorted.length);
|
|
42
|
+
const best = sorted.reduce((b, s) => (s.totalFindings < b.totalFindings ? s : b), sorted[0]);
|
|
43
|
+
const worst = sorted.reduce((w, s) => (s.totalFindings > w.totalFindings ? s : w), sorted[0]);
|
|
44
|
+
let trend = "stable";
|
|
45
|
+
let changePercent = 0;
|
|
46
|
+
if (sorted.length >= 2) {
|
|
47
|
+
const first = sorted[0].totalFindings;
|
|
48
|
+
const last = sorted[sorted.length - 1].totalFindings;
|
|
49
|
+
if (first > 0) {
|
|
50
|
+
changePercent = Math.round(((last - first) / first) * 100);
|
|
51
|
+
}
|
|
52
|
+
if (changePercent < -10)
|
|
53
|
+
trend = "improving";
|
|
54
|
+
else if (changePercent > 10)
|
|
55
|
+
trend = "degrading";
|
|
56
|
+
}
|
|
57
|
+
return { snapshots: sorted, trend, changePercent, averageFindings: avg, bestSnapshot: best, worstSnapshot: worst };
|
|
58
|
+
}
|
|
59
|
+
// ─── CLI ────────────────────────────────────────────────────────────────────
|
|
60
|
+
export function runTrendReport(argv) {
|
|
61
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
62
|
+
console.log(`
|
|
63
|
+
judges trend-report — Track finding trends over time
|
|
64
|
+
|
|
65
|
+
Usage:
|
|
66
|
+
judges trend-report record --total 15 --critical 1 --high 3 --medium 6 --low 5 --files 42
|
|
67
|
+
judges trend-report show Show trend analysis
|
|
68
|
+
judges trend-report show --format json JSON output
|
|
69
|
+
|
|
70
|
+
Subcommands:
|
|
71
|
+
record Record a snapshot of current findings
|
|
72
|
+
show Analyze and display trend data
|
|
73
|
+
|
|
74
|
+
Record Options:
|
|
75
|
+
--total <n> Total findings count
|
|
76
|
+
--critical <n> Critical severity count
|
|
77
|
+
--high <n> High severity count
|
|
78
|
+
--medium <n> Medium severity count
|
|
79
|
+
--low <n> Low severity count
|
|
80
|
+
--files <n> Files scanned count
|
|
81
|
+
--commit <hash> Associated commit hash
|
|
82
|
+
|
|
83
|
+
Show Options:
|
|
84
|
+
--format json JSON output
|
|
85
|
+
--help, -h Show this help
|
|
86
|
+
|
|
87
|
+
Snapshots are stored locally in .judges/trends/.
|
|
88
|
+
`);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
|
|
92
|
+
const subcommand = argv.find((a) => !a.startsWith("-") && a !== "trend-report") || "show";
|
|
93
|
+
if (subcommand === "record") {
|
|
94
|
+
const getNum = (flag) => {
|
|
95
|
+
const val = argv.find((_a, i) => argv[i - 1] === flag);
|
|
96
|
+
return val ? parseInt(val, 10) : 0;
|
|
97
|
+
};
|
|
98
|
+
const total = getNum("--total");
|
|
99
|
+
const critical = getNum("--critical");
|
|
100
|
+
const high = getNum("--high");
|
|
101
|
+
const medium = getNum("--medium");
|
|
102
|
+
const low = getNum("--low");
|
|
103
|
+
const filesScanned = getNum("--files");
|
|
104
|
+
const commit = argv.find((_a, i) => argv[i - 1] === "--commit") || "unknown";
|
|
105
|
+
const snapshot = {
|
|
106
|
+
timestamp: new Date().toISOString(),
|
|
107
|
+
commit,
|
|
108
|
+
totalFindings: total,
|
|
109
|
+
critical,
|
|
110
|
+
high,
|
|
111
|
+
medium,
|
|
112
|
+
low,
|
|
113
|
+
filesScanned,
|
|
114
|
+
findingsPerFile: filesScanned > 0 ? Math.round((total / filesScanned) * 100) / 100 : 0,
|
|
115
|
+
};
|
|
116
|
+
const snapshots = loadSnapshots();
|
|
117
|
+
snapshots.push(snapshot);
|
|
118
|
+
saveSnapshots(snapshots);
|
|
119
|
+
console.log(`Recorded snapshot: ${total} findings across ${filesScanned} files.`);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
// Show
|
|
123
|
+
const snapshots = loadSnapshots();
|
|
124
|
+
const report = computeTrend(snapshots);
|
|
125
|
+
if (format === "json") {
|
|
126
|
+
console.log(JSON.stringify(report, null, 2));
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
console.log(`\n Trend Report\n ─────────────────────────────`);
|
|
130
|
+
console.log(` Snapshots: ${report.snapshots.length}`);
|
|
131
|
+
console.log(` Average findings: ${report.averageFindings}`);
|
|
132
|
+
const trendIcon = report.trend === "improving" ? "📉" : report.trend === "degrading" ? "📈" : "➡️";
|
|
133
|
+
console.log(` Trend: ${trendIcon} ${report.trend} (${report.changePercent > 0 ? "+" : ""}${report.changePercent}%)`);
|
|
134
|
+
if (report.bestSnapshot) {
|
|
135
|
+
console.log(`\n Best: ${report.bestSnapshot.totalFindings} findings (${report.bestSnapshot.timestamp.slice(0, 10)})`);
|
|
136
|
+
}
|
|
137
|
+
if (report.worstSnapshot) {
|
|
138
|
+
console.log(` Worst: ${report.worstSnapshot.totalFindings} findings (${report.worstSnapshot.timestamp.slice(0, 10)})`);
|
|
139
|
+
}
|
|
140
|
+
if (report.snapshots.length > 0) {
|
|
141
|
+
console.log("\n Recent snapshots:");
|
|
142
|
+
const recent = report.snapshots.slice(-5);
|
|
143
|
+
for (const snap of recent) {
|
|
144
|
+
console.log(` ${snap.timestamp.slice(0, 10)} ${snap.totalFindings} findings (C:${snap.critical} H:${snap.high} M:${snap.medium} L:${snap.low})`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
console.log();
|
|
148
|
+
}
|
|
149
|
+
//# sourceMappingURL=trend-report.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trend-report.js","sourceRoot":"","sources":["../../src/commands/trend-report.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED