@kevinrabun/judges 3.61.0 → 3.63.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 +14 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +112 -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/finding-group.d.ts +16 -0
- package/dist/commands/finding-group.d.ts.map +1 -0
- package/dist/commands/finding-group.js +165 -0
- package/dist/commands/finding-group.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/ignore-list.d.ts +19 -0
- package/dist/commands/ignore-list.d.ts.map +1 -0
- package/dist/commands/ignore-list.js +166 -0
- package/dist/commands/ignore-list.js.map +1 -0
- package/dist/commands/incremental-review.d.ts +5 -0
- package/dist/commands/incremental-review.d.ts.map +1 -0
- package/dist/commands/incremental-review.js +240 -0
- package/dist/commands/incremental-review.js.map +1 -0
- package/dist/commands/review-cache.d.ts +23 -0
- package/dist/commands/review-cache.d.ts.map +1 -0
- package/dist/commands/review-cache.js +135 -0
- package/dist/commands/review-cache.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/review-log.d.ts +23 -0
- package/dist/commands/review-log.d.ts.map +1 -0
- package/dist/commands/review-log.js +165 -0
- package/dist/commands/review-log.js.map +1 -0
- package/dist/commands/review-summary.d.ts +5 -0
- package/dist/commands/review-summary.d.ts.map +1 -0
- package/dist/commands/review-summary.js +175 -0
- package/dist/commands/review-summary.js.map +1 -0
- package/dist/commands/rule-test.d.ts +5 -0
- package/dist/commands/rule-test.d.ts.map +1 -0
- package/dist/commands/rule-test.js +216 -0
- package/dist/commands/rule-test.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/team-config.d.ts +5 -0
- package/dist/commands/team-config.d.ts.map +1 -0
- package/dist/commands/team-config.js +235 -0
- package/dist/commands/team-config.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-explain.d.ts","sourceRoot":"","sources":["../../src/commands/review-explain.ts"],"names":[],"mappings":"AAAA;;;GAGG;AA6JH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CA8ErD"}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Review-explain — Generate plain-language explanation of review findings
|
|
3
|
+
* for non-technical stakeholders.
|
|
4
|
+
*/
|
|
5
|
+
import { readFileSync, existsSync } from "fs";
|
|
6
|
+
// ─── Plain-language explanations ───────────────────────────────────────────
|
|
7
|
+
const EXPLANATIONS = {
|
|
8
|
+
"hardcoded-secret": {
|
|
9
|
+
plain: "A password or secret key is written directly in the code, visible to anyone who can see the source.",
|
|
10
|
+
impact: "If code is shared or leaked, attackers can immediately access protected systems. This is a leading cause of data breaches.",
|
|
11
|
+
recommendation: "Move secrets to environment variables or a secrets manager. Never commit credentials to version control.",
|
|
12
|
+
},
|
|
13
|
+
"eval-usage": {
|
|
14
|
+
plain: "The code can execute arbitrary commands, potentially from untrusted input.",
|
|
15
|
+
impact: "An attacker could inject malicious code that runs with full application permissions, leading to data theft or system compromise.",
|
|
16
|
+
recommendation: "Replace with safer alternatives that don't allow arbitrary code execution.",
|
|
17
|
+
},
|
|
18
|
+
"sql-injection": {
|
|
19
|
+
plain: "User input is being inserted directly into database queries without protection.",
|
|
20
|
+
impact: "Attackers can manipulate queries to steal, modify, or delete data. SQL injection is consistently in the OWASP Top 10.",
|
|
21
|
+
recommendation: "Use parameterized queries or an ORM that handles input sanitization automatically.",
|
|
22
|
+
},
|
|
23
|
+
"sql-concat": {
|
|
24
|
+
plain: "Database queries are built by concatenating strings, which can allow injection attacks.",
|
|
25
|
+
impact: "Similar to SQL injection — attackers can manipulate queries through crafted input.",
|
|
26
|
+
recommendation: "Use parameterized queries instead of string concatenation.",
|
|
27
|
+
},
|
|
28
|
+
"xss-risk": {
|
|
29
|
+
plain: "The code inserts content directly into web pages without sanitization.",
|
|
30
|
+
impact: "Attackers can inject scripts that steal user credentials, redirect users, or deface the application.",
|
|
31
|
+
recommendation: "Always sanitize user content before displaying it. Use framework-provided escaping functions.",
|
|
32
|
+
},
|
|
33
|
+
"command-injection": {
|
|
34
|
+
plain: "The code runs system commands that include user-controllable data.",
|
|
35
|
+
impact: "An attacker could execute arbitrary commands on the server, potentially taking full control.",
|
|
36
|
+
recommendation: "Avoid passing user input to system commands. Use libraries with safe APIs instead.",
|
|
37
|
+
},
|
|
38
|
+
"empty-catch": {
|
|
39
|
+
plain: "Errors are being silently ignored — the code catches errors but does nothing with them.",
|
|
40
|
+
impact: "Issues go undetected in production, making debugging extremely difficult and potentially hiding security problems.",
|
|
41
|
+
recommendation: "Log errors appropriately and handle them based on their severity.",
|
|
42
|
+
},
|
|
43
|
+
"any-type": {
|
|
44
|
+
plain: "Code uses unsafe typing that bypasses compile-time safety checks.",
|
|
45
|
+
impact: "Bugs that would normally be caught before deployment can slip through to production.",
|
|
46
|
+
recommendation: "Use proper type definitions to catch errors during development rather than in production.",
|
|
47
|
+
},
|
|
48
|
+
"console-log": {
|
|
49
|
+
plain: "Debug logging statements are present in production code.",
|
|
50
|
+
impact: "Can leak sensitive information and create unnecessary noise in production logs.",
|
|
51
|
+
recommendation: "Use a proper logging framework with configurable levels.",
|
|
52
|
+
},
|
|
53
|
+
"deprecated-api": {
|
|
54
|
+
plain: "The code uses outdated functions that may be removed in future releases.",
|
|
55
|
+
impact: "Future updates could break the application. Deprecated APIs may also have known security vulnerabilities.",
|
|
56
|
+
recommendation: "Migrate to the recommended replacement APIs.",
|
|
57
|
+
},
|
|
58
|
+
"unsafe-regex": {
|
|
59
|
+
plain: "A pattern matching expression is built from user input, which could be exploited.",
|
|
60
|
+
impact: "Attackers can craft input that causes the application to hang or consume excessive resources.",
|
|
61
|
+
recommendation: "Validate and escape user input before using it in pattern matching.",
|
|
62
|
+
},
|
|
63
|
+
"todo-fixme": {
|
|
64
|
+
plain: "The code contains unfinished work markers that haven't been addressed.",
|
|
65
|
+
impact: "Incomplete implementations may have missing validation, error handling, or security checks.",
|
|
66
|
+
recommendation: "Track these in your project management tool and resolve before release.",
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
// ─── Analysis ──────────────────────────────────────────────────────────────
|
|
70
|
+
function explainFindings(findings) {
|
|
71
|
+
// Deduplicate by pattern
|
|
72
|
+
const patternSet = new Map();
|
|
73
|
+
for (const f of findings) {
|
|
74
|
+
const existing = patternSet.get(f.pattern);
|
|
75
|
+
if (existing) {
|
|
76
|
+
existing.count++;
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
patternSet.set(f.pattern, { severity: f.severity, count: 1 });
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
const explanations = [];
|
|
83
|
+
for (const [pattern, data] of patternSet) {
|
|
84
|
+
const expl = EXPLANATIONS[pattern] || {
|
|
85
|
+
plain: `Found ${data.count} instance(s) of '${pattern}' pattern.`,
|
|
86
|
+
impact: "This finding may affect code quality or security depending on context.",
|
|
87
|
+
recommendation: "Review each instance and address based on your team's standards.",
|
|
88
|
+
};
|
|
89
|
+
explanations.push({
|
|
90
|
+
pattern,
|
|
91
|
+
severity: data.severity,
|
|
92
|
+
plainExplanation: expl.plain,
|
|
93
|
+
businessImpact: expl.impact,
|
|
94
|
+
recommendation: expl.recommendation,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
// Sort by severity
|
|
98
|
+
const sevRank = { critical: 4, high: 3, medium: 2, low: 1 };
|
|
99
|
+
explanations.sort((a, b) => (sevRank[b.severity] || 0) - (sevRank[a.severity] || 0));
|
|
100
|
+
const criticalCount = findings.filter((f) => f.severity === "critical").length;
|
|
101
|
+
const highCount = findings.filter((f) => f.severity === "high").length;
|
|
102
|
+
let riskLevel = "Low";
|
|
103
|
+
if (criticalCount > 0)
|
|
104
|
+
riskLevel = "Critical";
|
|
105
|
+
else if (highCount > 0)
|
|
106
|
+
riskLevel = "High";
|
|
107
|
+
else if (findings.length > 10)
|
|
108
|
+
riskLevel = "Medium";
|
|
109
|
+
let executiveSummary;
|
|
110
|
+
if (findings.length === 0) {
|
|
111
|
+
executiveSummary = "No issues were found in this review. The code appears to meet quality and security standards.";
|
|
112
|
+
}
|
|
113
|
+
else if (criticalCount > 0) {
|
|
114
|
+
executiveSummary = `This review found ${criticalCount} critical security issue(s) that must be resolved before deployment. These issues could lead to data breaches or system compromise if exploited.`;
|
|
115
|
+
}
|
|
116
|
+
else if (highCount > 0) {
|
|
117
|
+
executiveSummary = `This review found ${highCount} high-severity issue(s) that should be addressed promptly. While not immediately exploitable, they represent significant risk.`;
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
executiveSummary = `This review found ${findings.length} issue(s), none critical. These represent code quality improvements that will reduce technical debt.`;
|
|
121
|
+
}
|
|
122
|
+
return { totalFindings: findings.length, explanations, executiveSummary, riskLevel };
|
|
123
|
+
}
|
|
124
|
+
// ─── CLI ────────────────────────────────────────────────────────────────────
|
|
125
|
+
export function runReviewExplain(argv) {
|
|
126
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
127
|
+
console.log(`
|
|
128
|
+
judges review-explain — Plain-language explanation of review findings
|
|
129
|
+
|
|
130
|
+
Usage:
|
|
131
|
+
judges review-explain --file findings.json Explain findings from JSON
|
|
132
|
+
judges review-explain --format json JSON output
|
|
133
|
+
|
|
134
|
+
Options:
|
|
135
|
+
--file <path> Path to findings JSON (array or {findings: [...]})
|
|
136
|
+
--format json JSON output
|
|
137
|
+
--help, -h Show this help
|
|
138
|
+
|
|
139
|
+
Translates technical security and quality findings into plain language
|
|
140
|
+
that non-technical stakeholders can understand. Includes business impact
|
|
141
|
+
and actionable recommendations.
|
|
142
|
+
`);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
|
|
146
|
+
const findingsFile = argv.find((_a, i) => argv[i - 1] === "--file");
|
|
147
|
+
if (!findingsFile) {
|
|
148
|
+
console.error("Error: --file <path> is required. Provide a JSON file with findings.");
|
|
149
|
+
process.exitCode = 1;
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
if (!existsSync(findingsFile)) {
|
|
153
|
+
console.error(`Error: File '${findingsFile}' not found.`);
|
|
154
|
+
process.exitCode = 1;
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
let findings;
|
|
158
|
+
try {
|
|
159
|
+
const raw = JSON.parse(readFileSync(findingsFile, "utf-8"));
|
|
160
|
+
findings = Array.isArray(raw) ? raw : Array.isArray(raw.findings) ? raw.findings : [];
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
console.error("Error: Cannot parse findings file.");
|
|
164
|
+
process.exitCode = 1;
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
const result = explainFindings(findings);
|
|
168
|
+
if (format === "json") {
|
|
169
|
+
console.log(JSON.stringify(result, null, 2));
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
console.log(`\n Review Explanation\n ─────────────────────────────`);
|
|
173
|
+
console.log(` Risk Level: ${result.riskLevel}`);
|
|
174
|
+
console.log(` Total Findings: ${result.totalFindings}\n`);
|
|
175
|
+
console.log(` Executive Summary:`);
|
|
176
|
+
console.log(` ${result.executiveSummary}`);
|
|
177
|
+
if (result.explanations.length > 0) {
|
|
178
|
+
console.log("\n Detailed Findings:");
|
|
179
|
+
for (const expl of result.explanations) {
|
|
180
|
+
const icon = expl.severity === "critical"
|
|
181
|
+
? "🔴"
|
|
182
|
+
: expl.severity === "high"
|
|
183
|
+
? "🟠"
|
|
184
|
+
: expl.severity === "medium"
|
|
185
|
+
? "🟡"
|
|
186
|
+
: "🔵";
|
|
187
|
+
console.log(`\n ${icon} ${expl.pattern} (${expl.severity})`);
|
|
188
|
+
console.log(` What: ${expl.plainExplanation}`);
|
|
189
|
+
console.log(` Impact: ${expl.businessImpact}`);
|
|
190
|
+
console.log(` Action: ${expl.recommendation}`);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
console.log();
|
|
194
|
+
}
|
|
195
|
+
//# sourceMappingURL=review-explain.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review-explain.js","sourceRoot":"","sources":["../../src/commands/review-explain.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AA0B9C,8EAA8E;AAE9E,MAAM,YAAY,GAA8E;IAC9F,kBAAkB,EAAE;QAClB,KAAK,EAAE,qGAAqG;QAC5G,MAAM,EACJ,4HAA4H;QAC9H,cAAc,EACZ,0GAA0G;KAC7G;IACD,YAAY,EAAE;QACZ,KAAK,EAAE,4EAA4E;QACnF,MAAM,EACJ,kIAAkI;QACpI,cAAc,EAAE,4EAA4E;KAC7F;IACD,eAAe,EAAE;QACf,KAAK,EAAE,iFAAiF;QACxF,MAAM,EACJ,uHAAuH;QACzH,cAAc,EAAE,oFAAoF;KACrG;IACD,YAAY,EAAE;QACZ,KAAK,EAAE,yFAAyF;QAChG,MAAM,EAAE,oFAAoF;QAC5F,cAAc,EAAE,4DAA4D;KAC7E;IACD,UAAU,EAAE;QACV,KAAK,EAAE,wEAAwE;QAC/E,MAAM,EAAE,sGAAsG;QAC9G,cAAc,EAAE,+FAA+F;KAChH;IACD,mBAAmB,EAAE;QACnB,KAAK,EAAE,oEAAoE;QAC3E,MAAM,EAAE,8FAA8F;QACtG,cAAc,EAAE,oFAAoF;KACrG;IACD,aAAa,EAAE;QACb,KAAK,EAAE,yFAAyF;QAChG,MAAM,EACJ,oHAAoH;QACtH,cAAc,EAAE,mEAAmE;KACpF;IACD,UAAU,EAAE;QACV,KAAK,EAAE,mEAAmE;QAC1E,MAAM,EAAE,sFAAsF;QAC9F,cAAc,EAAE,2FAA2F;KAC5G;IACD,aAAa,EAAE;QACb,KAAK,EAAE,0DAA0D;QACjE,MAAM,EAAE,iFAAiF;QACzF,cAAc,EAAE,0DAA0D;KAC3E;IACD,gBAAgB,EAAE;QAChB,KAAK,EAAE,0EAA0E;QACjF,MAAM,EAAE,2GAA2G;QACnH,cAAc,EAAE,8CAA8C;KAC/D;IACD,cAAc,EAAE;QACd,KAAK,EAAE,mFAAmF;QAC1F,MAAM,EAAE,+FAA+F;QACvG,cAAc,EAAE,qEAAqE;KACtF;IACD,YAAY,EAAE;QACZ,KAAK,EAAE,wEAAwE;QAC/E,MAAM,EAAE,6FAA6F;QACrG,cAAc,EAAE,yEAAyE;KAC1F;CACF,CAAC;AAEF,8EAA8E;AAE9E,SAAS,eAAe,CAAC,QAAyB;IAChD,yBAAyB;IACzB,MAAM,UAAU,GAAG,IAAI,GAAG,EAA+C,CAAC;IAC1E,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,MAAM,QAAQ,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;QAC3C,IAAI,QAAQ,EAAE,CAAC;YACb,QAAQ,CAAC,KAAK,EAAE,CAAC;QACnB,CAAC;aAAM,CAAC;YACN,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;QAChE,CAAC;IACH,CAAC;IAED,MAAM,YAAY,GAAuB,EAAE,CAAC;IAC5C,KAAK,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,UAAU,EAAE,CAAC;QACzC,MAAM,IAAI,GAAG,YAAY,CAAC,OAAO,CAAC,IAAI;YACpC,KAAK,EAAE,SAAS,IAAI,CAAC,KAAK,oBAAoB,OAAO,YAAY;YACjE,MAAM,EAAE,wEAAwE;YAChF,cAAc,EAAE,kEAAkE;SACnF,CAAC;QAEF,YAAY,CAAC,IAAI,CAAC;YAChB,OAAO;YACP,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,gBAAgB,EAAE,IAAI,CAAC,KAAK;YAC5B,cAAc,EAAE,IAAI,CAAC,MAAM;YAC3B,cAAc,EAAE,IAAI,CAAC,cAAc;SACpC,CAAC,CAAC;IACL,CAAC;IAED,mBAAmB;IACnB,MAAM,OAAO,GAA2B,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC;IACpF,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAErF,MAAM,aAAa,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,UAAU,CAAC,CAAC,MAAM,CAAC;IAC/E,MAAM,SAAS,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,MAAM,CAAC,CAAC,MAAM,CAAC;IAEvE,IAAI,SAAS,GAAG,KAAK,CAAC;IACtB,IAAI,aAAa,GAAG,CAAC;QAAE,SAAS,GAAG,UAAU,CAAC;SACzC,IAAI,SAAS,GAAG,CAAC;QAAE,SAAS,GAAG,MAAM,CAAC;SACtC,IAAI,QAAQ,CAAC,MAAM,GAAG,EAAE;QAAE,SAAS,GAAG,QAAQ,CAAC;IAEpD,IAAI,gBAAwB,CAAC;IAC7B,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,gBAAgB,GAAG,+FAA+F,CAAC;IACrH,CAAC;SAAM,IAAI,aAAa,GAAG,CAAC,EAAE,CAAC;QAC7B,gBAAgB,GAAG,qBAAqB,aAAa,kJAAkJ,CAAC;IAC1M,CAAC;SAAM,IAAI,SAAS,GAAG,CAAC,EAAE,CAAC;QACzB,gBAAgB,GAAG,qBAAqB,SAAS,gIAAgI,CAAC;IACpL,CAAC;SAAM,CAAC;QACN,gBAAgB,GAAG,qBAAqB,QAAQ,CAAC,MAAM,sGAAsG,CAAC;IAChK,CAAC;IAED,OAAO,EAAE,aAAa,EAAE,QAAQ,CAAC,MAAM,EAAE,YAAY,EAAE,gBAAgB,EAAE,SAAS,EAAE,CAAC;AACvF,CAAC;AAED,+EAA+E;AAE/E,MAAM,UAAU,gBAAgB,CAAC,IAAc;IAC7C,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACnD,OAAO,CAAC,GAAG,CAAC;;;;;;;;;;;;;;;CAef,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,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,EAAU,EAAE,CAAS,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,QAAQ,CAAC,CAAC;IAEpF,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,OAAO,CAAC,KAAK,CAAC,sEAAsE,CAAC,CAAC;QACtF,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACrB,OAAO;IACT,CAAC;IAED,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QAC9B,OAAO,CAAC,KAAK,CAAC,gBAAgB,YAAY,cAAc,CAAC,CAAC;QAC1D,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACrB,OAAO;IACT,CAAC;IAED,IAAI,QAAyB,CAAC;IAC9B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC,CAAC;QAC5D,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;IACxF,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,KAAK,CAAC,oCAAoC,CAAC,CAAC;QACpD,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACrB,OAAO;IACT,CAAC;IAED,MAAM,MAAM,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC;IAEzC,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;QACtB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAC7C,OAAO;IACT,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,yDAAyD,CAAC,CAAC;IACvE,OAAO,CAAC,GAAG,CAAC,mBAAmB,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC;IACnD,OAAO,CAAC,GAAG,CAAC,uBAAuB,MAAM,CAAC,aAAa,IAAI,CAAC,CAAC;IAC7D,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;IACtC,OAAO,CAAC,GAAG,CAAC,OAAO,MAAM,CAAC,gBAAgB,EAAE,CAAC,CAAC;IAE9C,IAAI,MAAM,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACnC,OAAO,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAC;QACxC,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC;YACvC,MAAM,IAAI,GACR,IAAI,CAAC,QAAQ,KAAK,UAAU;gBAC1B,CAAC,CAAC,IAAI;gBACN,CAAC,CAAC,IAAI,CAAC,QAAQ,KAAK,MAAM;oBACxB,CAAC,CAAC,IAAI;oBACN,CAAC,CAAC,IAAI,CAAC,QAAQ,KAAK,QAAQ;wBAC1B,CAAC,CAAC,IAAI;wBACN,CAAC,CAAC,IAAI,CAAC;YACf,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,IAAI,IAAI,CAAC,OAAO,KAAK,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC;YAClE,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,CAAC,gBAAgB,EAAE,CAAC,CAAC;YACvD,OAAO,CAAC,GAAG,CAAC,oBAAoB,IAAI,CAAC,cAAc,EAAE,CAAC,CAAC;YACvD,OAAO,CAAC,GAAG,CAAC,oBAAoB,IAAI,CAAC,cAAc,EAAE,CAAC,CAAC;QACzD,CAAC;IACH,CAAC;IAED,OAAO,CAAC,GAAG,EAAE,CAAC;AAChB,CAAC"}
|
|
@@ -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":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Review-log — Structured audit log of all review actions for compliance.
|
|
3
|
+
*/
|
|
4
|
+
interface ReviewLogEntry {
|
|
5
|
+
timestamp: string;
|
|
6
|
+
action: string;
|
|
7
|
+
command: string;
|
|
8
|
+
filesReviewed: number;
|
|
9
|
+
findingsCount: number;
|
|
10
|
+
severity: {
|
|
11
|
+
critical: number;
|
|
12
|
+
high: number;
|
|
13
|
+
medium: number;
|
|
14
|
+
low: number;
|
|
15
|
+
};
|
|
16
|
+
duration: number;
|
|
17
|
+
user: string;
|
|
18
|
+
commit: string;
|
|
19
|
+
}
|
|
20
|
+
declare function appendLog(entry: ReviewLogEntry): void;
|
|
21
|
+
export declare function runReviewLog(argv: string[]): void;
|
|
22
|
+
export { appendLog, ReviewLogEntry };
|
|
23
|
+
//# sourceMappingURL=review-log.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review-log.d.ts","sourceRoot":"","sources":["../../src/commands/review-log.ts"],"names":[],"mappings":"AAAA;;GAEG;AAOH,UAAU,cAAc;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1E,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAwBD,iBAAS,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI,CAK9C;AA2DD,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAuGjD;AAGD,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,CAAC"}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Review-log — Structured audit log of all review actions for compliance.
|
|
3
|
+
*/
|
|
4
|
+
import { readFileSync, existsSync, mkdirSync, appendFileSync, readdirSync } from "fs";
|
|
5
|
+
import { join } from "path";
|
|
6
|
+
// ─── Helpers ────────────────────────────────────────────────────────────────
|
|
7
|
+
function getLogDir() {
|
|
8
|
+
return join(".", ".judges", "logs");
|
|
9
|
+
}
|
|
10
|
+
function getLogFile() {
|
|
11
|
+
const dir = getLogDir();
|
|
12
|
+
const date = new Date().toISOString().slice(0, 7); // YYYY-MM
|
|
13
|
+
return join(dir, `review-log-${date}.jsonl`);
|
|
14
|
+
}
|
|
15
|
+
function appendLog(entry) {
|
|
16
|
+
const dir = getLogDir();
|
|
17
|
+
if (!existsSync(dir))
|
|
18
|
+
mkdirSync(dir, { recursive: true });
|
|
19
|
+
const logFile = getLogFile();
|
|
20
|
+
appendFileSync(logFile, JSON.stringify(entry) + "\n", "utf-8");
|
|
21
|
+
}
|
|
22
|
+
function readAllLogs() {
|
|
23
|
+
const dir = getLogDir();
|
|
24
|
+
if (!existsSync(dir))
|
|
25
|
+
return [];
|
|
26
|
+
const entries = [];
|
|
27
|
+
const files = readdirSync(dir).filter((f) => f.endsWith(".jsonl")).sort();
|
|
28
|
+
for (const f of files) {
|
|
29
|
+
try {
|
|
30
|
+
const content = readFileSync(join(dir, f), "utf-8");
|
|
31
|
+
const lines = content.split("\n").filter((l) => l.trim());
|
|
32
|
+
for (const line of lines) {
|
|
33
|
+
try {
|
|
34
|
+
entries.push(JSON.parse(line));
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
// skip invalid lines
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
// skip unreadable files
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return entries;
|
|
46
|
+
}
|
|
47
|
+
function summarizeLogs(entries) {
|
|
48
|
+
if (entries.length === 0) {
|
|
49
|
+
return {
|
|
50
|
+
totalEntries: 0,
|
|
51
|
+
recentEntries: [],
|
|
52
|
+
totalFilesReviewed: 0,
|
|
53
|
+
totalFindings: 0,
|
|
54
|
+
averageDuration: 0,
|
|
55
|
+
firstEntry: "",
|
|
56
|
+
lastEntry: "",
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
const sorted = [...entries].sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
|
|
60
|
+
const totalFiles = entries.reduce((s, e) => s + e.filesReviewed, 0);
|
|
61
|
+
const totalFindings = entries.reduce((s, e) => s + e.findingsCount, 0);
|
|
62
|
+
const totalDuration = entries.reduce((s, e) => s + e.duration, 0);
|
|
63
|
+
return {
|
|
64
|
+
totalEntries: entries.length,
|
|
65
|
+
recentEntries: sorted.slice(-10),
|
|
66
|
+
totalFilesReviewed: totalFiles,
|
|
67
|
+
totalFindings,
|
|
68
|
+
averageDuration: Math.round(totalDuration / entries.length),
|
|
69
|
+
firstEntry: sorted[0].timestamp,
|
|
70
|
+
lastEntry: sorted[sorted.length - 1].timestamp,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
// ─── CLI ────────────────────────────────────────────────────────────────────
|
|
74
|
+
export function runReviewLog(argv) {
|
|
75
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
76
|
+
console.log(`
|
|
77
|
+
judges review-log — Structured audit log of review actions
|
|
78
|
+
|
|
79
|
+
Usage:
|
|
80
|
+
judges review-log show Show log summary
|
|
81
|
+
judges review-log record --command eval --files 10 --findings 5
|
|
82
|
+
judges review-log export Export all logs as JSON
|
|
83
|
+
judges review-log --format json JSON output
|
|
84
|
+
|
|
85
|
+
Subcommands:
|
|
86
|
+
show Show log summary and recent entries
|
|
87
|
+
record Record a review action to the log
|
|
88
|
+
export Export complete log as JSON
|
|
89
|
+
|
|
90
|
+
Record Options:
|
|
91
|
+
--command <name> Command that was run
|
|
92
|
+
--files <n> Number of files reviewed
|
|
93
|
+
--findings <n> Number of findings
|
|
94
|
+
--critical <n> Critical findings count
|
|
95
|
+
--high <n> High findings count
|
|
96
|
+
--medium <n> Medium findings count
|
|
97
|
+
--low <n> Low findings count
|
|
98
|
+
--duration <ms> Duration in milliseconds
|
|
99
|
+
--commit <hash> Associated commit
|
|
100
|
+
|
|
101
|
+
Logs are stored locally in .judges/logs/ as monthly JSONL files.
|
|
102
|
+
`);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
|
|
106
|
+
const subcommand = argv.find((a) => !a.startsWith("-") && a !== "review-log") || "show";
|
|
107
|
+
if (subcommand === "record") {
|
|
108
|
+
const getNum = (flag) => {
|
|
109
|
+
const val = argv.find((_a, i) => argv[i - 1] === flag);
|
|
110
|
+
return val ? parseInt(val, 10) : 0;
|
|
111
|
+
};
|
|
112
|
+
const getStr = (flag) => {
|
|
113
|
+
return argv.find((_a, i) => argv[i - 1] === flag) || "";
|
|
114
|
+
};
|
|
115
|
+
const entry = {
|
|
116
|
+
timestamp: new Date().toISOString(),
|
|
117
|
+
action: "review",
|
|
118
|
+
command: getStr("--command") || "unknown",
|
|
119
|
+
filesReviewed: getNum("--files"),
|
|
120
|
+
findingsCount: getNum("--findings"),
|
|
121
|
+
severity: {
|
|
122
|
+
critical: getNum("--critical"),
|
|
123
|
+
high: getNum("--high"),
|
|
124
|
+
medium: getNum("--medium"),
|
|
125
|
+
low: getNum("--low"),
|
|
126
|
+
},
|
|
127
|
+
duration: getNum("--duration"),
|
|
128
|
+
user: process.env.USER || process.env.USERNAME || "unknown",
|
|
129
|
+
commit: getStr("--commit"),
|
|
130
|
+
};
|
|
131
|
+
appendLog(entry);
|
|
132
|
+
console.log(`Logged review action: ${entry.command} (${entry.findingsCount} findings, ${entry.filesReviewed} files).`);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
if (subcommand === "export") {
|
|
136
|
+
const entries = readAllLogs();
|
|
137
|
+
console.log(JSON.stringify(entries, null, 2));
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
// Show
|
|
141
|
+
const entries = readAllLogs();
|
|
142
|
+
const summary = summarizeLogs(entries);
|
|
143
|
+
if (format === "json") {
|
|
144
|
+
console.log(JSON.stringify(summary, null, 2));
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
console.log(`\n Review Log\n ─────────────────────────────`);
|
|
148
|
+
console.log(` Total entries: ${summary.totalEntries}`);
|
|
149
|
+
console.log(` Total files reviewed: ${summary.totalFilesReviewed}`);
|
|
150
|
+
console.log(` Total findings: ${summary.totalFindings}`);
|
|
151
|
+
console.log(` Average duration: ${summary.averageDuration}ms`);
|
|
152
|
+
if (summary.firstEntry) {
|
|
153
|
+
console.log(` Period: ${summary.firstEntry.slice(0, 10)} to ${summary.lastEntry.slice(0, 10)}`);
|
|
154
|
+
}
|
|
155
|
+
if (summary.recentEntries.length > 0) {
|
|
156
|
+
console.log("\n Recent entries:");
|
|
157
|
+
for (const e of summary.recentEntries) {
|
|
158
|
+
console.log(` ${e.timestamp.slice(0, 19)} ${e.command} — ${e.findingsCount} findings, ${e.filesReviewed} files, ${e.duration}ms`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
console.log();
|
|
162
|
+
}
|
|
163
|
+
// Export for use by other commands
|
|
164
|
+
export { appendLog };
|
|
165
|
+
//# sourceMappingURL=review-log.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review-log.js","sourceRoot":"","sources":["../../src/commands/review-log.ts"],"names":[],"mappings":"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"}
|