@kevinrabun/judges 3.62.0 → 3.64.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/auto-approve.d.ts +5 -0
- package/dist/commands/auto-approve.d.ts.map +1 -0
- package/dist/commands/auto-approve.js +189 -0
- package/dist/commands/auto-approve.js.map +1 -0
- package/dist/commands/diff-explain.d.ts +5 -0
- package/dist/commands/diff-explain.d.ts.map +1 -0
- package/dist/commands/diff-explain.js +143 -0
- package/dist/commands/diff-explain.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/fix-suggest.d.ts +5 -0
- package/dist/commands/fix-suggest.d.ts.map +1 -0
- package/dist/commands/fix-suggest.js +172 -0
- package/dist/commands/fix-suggest.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/multi-lang-review.d.ts +5 -0
- package/dist/commands/multi-lang-review.d.ts.map +1 -0
- package/dist/commands/multi-lang-review.js +231 -0
- package/dist/commands/multi-lang-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-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-priority.d.ts +5 -0
- package/dist/commands/review-priority.d.ts.map +1 -0
- package/dist/commands/review-priority.js +158 -0
- package/dist/commands/review-priority.js.map +1 -0
- package/dist/commands/review-profile.d.ts +5 -0
- package/dist/commands/review-profile.d.ts.map +1 -0
- package/dist/commands/review-profile.js +169 -0
- package/dist/commands/review-profile.js.map +1 -0
- package/dist/commands/review-stats.d.ts +5 -0
- package/dist/commands/review-stats.d.ts.map +1 -0
- package/dist/commands/review-stats.js +176 -0
- package/dist/commands/review-stats.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/review-template.d.ts +5 -0
- package/dist/commands/review-template.d.ts.map +1 -0
- package/dist/commands/review-template.js +213 -0
- package/dist/commands/review-template.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/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/package.json +1 -1
- package/server.json +2 -2
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-approve — Auto-approve findings below a configurable threshold.
|
|
3
|
+
*/
|
|
4
|
+
import { readFileSync } from "fs";
|
|
5
|
+
// ─── Default policy ────────────────────────────────────────────────────────
|
|
6
|
+
function defaultPolicy() {
|
|
7
|
+
return {
|
|
8
|
+
maxLowFindings: 10,
|
|
9
|
+
maxMediumFindings: 3,
|
|
10
|
+
allowedRules: [],
|
|
11
|
+
requireMinScore: 70,
|
|
12
|
+
blockOnSecurity: true,
|
|
13
|
+
autoApproveClean: true,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
// ─── Severity classification ────────────────────────────────────────────────
|
|
17
|
+
const SEVERITY_ORDER = { critical: 0, high: 1, medium: 2, low: 3 };
|
|
18
|
+
function isSecurity(finding) {
|
|
19
|
+
const rid = (finding.ruleId || "").toLowerCase();
|
|
20
|
+
return /sql|inject|xss|csrf|traversal|auth|secret|crypt|ssrf|deserial/i.test(rid);
|
|
21
|
+
}
|
|
22
|
+
// ─── Approval engine ───────────────────────────────────────────────────────
|
|
23
|
+
function evaluateApproval(verdict, policy) {
|
|
24
|
+
const findings = verdict.findings || [];
|
|
25
|
+
const violations = [];
|
|
26
|
+
const approved = [];
|
|
27
|
+
const flagged = [];
|
|
28
|
+
// Check score threshold
|
|
29
|
+
if ((verdict.overallScore ?? 0) < policy.requireMinScore) {
|
|
30
|
+
violations.push(`Score ${verdict.overallScore} is below minimum ${policy.requireMinScore}`);
|
|
31
|
+
}
|
|
32
|
+
// Classify findings
|
|
33
|
+
const severityCounts = { critical: 0, high: 0, medium: 0, low: 0 };
|
|
34
|
+
for (const f of findings) {
|
|
35
|
+
const sev = f.severity || "low";
|
|
36
|
+
severityCounts[sev] = (severityCounts[sev] || 0) + 1;
|
|
37
|
+
const sevLevel = SEVERITY_ORDER[sev] ?? 3;
|
|
38
|
+
// Critical and high are always flagged
|
|
39
|
+
if (sevLevel <= 1) {
|
|
40
|
+
flagged.push(f);
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
// Security findings blocked if policy says so
|
|
44
|
+
if (policy.blockOnSecurity && isSecurity(f)) {
|
|
45
|
+
flagged.push(f);
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
// Low findings auto-approved
|
|
49
|
+
if (sev === "low") {
|
|
50
|
+
approved.push(f);
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
// Medium finding — check against allowed rules
|
|
54
|
+
if (policy.allowedRules.length > 0 && policy.allowedRules.includes(f.ruleId)) {
|
|
55
|
+
approved.push(f);
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
flagged.push(f);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// Check threshold violations
|
|
62
|
+
if (severityCounts["critical"] > 0) {
|
|
63
|
+
violations.push(`${severityCounts["critical"]} critical finding(s) detected`);
|
|
64
|
+
}
|
|
65
|
+
if (severityCounts["high"] > 0) {
|
|
66
|
+
violations.push(`${severityCounts["high"]} high finding(s) detected`);
|
|
67
|
+
}
|
|
68
|
+
if (severityCounts["medium"] > policy.maxMediumFindings) {
|
|
69
|
+
violations.push(`${severityCounts["medium"]} medium findings exceed max of ${policy.maxMediumFindings}`);
|
|
70
|
+
}
|
|
71
|
+
if (severityCounts["low"] > policy.maxLowFindings) {
|
|
72
|
+
violations.push(`${severityCounts["low"]} low findings exceed max of ${policy.maxLowFindings}`);
|
|
73
|
+
}
|
|
74
|
+
const isApproved = violations.length === 0 || (findings.length === 0 && policy.autoApproveClean);
|
|
75
|
+
let reason;
|
|
76
|
+
if (isApproved && findings.length === 0) {
|
|
77
|
+
reason = "Clean review — no findings";
|
|
78
|
+
}
|
|
79
|
+
else if (isApproved) {
|
|
80
|
+
reason = "All findings within policy thresholds";
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
reason = violations.join("; ");
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
approved: isApproved,
|
|
87
|
+
reason,
|
|
88
|
+
autoApproved: approved.length,
|
|
89
|
+
manualRequired: flagged.length,
|
|
90
|
+
policyViolations: violations,
|
|
91
|
+
findings: { approved, flagged },
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
// ─── CLI ────────────────────────────────────────────────────────────────────
|
|
95
|
+
export function runAutoApprove(argv) {
|
|
96
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
97
|
+
console.log(`
|
|
98
|
+
judges auto-approve — Auto-approve findings below threshold
|
|
99
|
+
|
|
100
|
+
Usage:
|
|
101
|
+
judges auto-approve --input verdict.json
|
|
102
|
+
judges auto-approve --input verdict.json --policy policy.json
|
|
103
|
+
judges auto-approve --input verdict.json --format json
|
|
104
|
+
|
|
105
|
+
Options:
|
|
106
|
+
--input <file> TribunalVerdict JSON file (required)
|
|
107
|
+
--policy <file> Approval policy JSON file (optional, uses defaults)
|
|
108
|
+
--min-score <n> Minimum score for approval (default: 70)
|
|
109
|
+
--format json JSON output
|
|
110
|
+
--help, -h Show this help
|
|
111
|
+
|
|
112
|
+
Policy defaults:
|
|
113
|
+
maxLowFindings: 10, maxMediumFindings: 3, requireMinScore: 70,
|
|
114
|
+
blockOnSecurity: true, autoApproveClean: true
|
|
115
|
+
|
|
116
|
+
Auto-approves low-severity findings and flags critical/high findings
|
|
117
|
+
for manual review. Reduces noise while maintaining safety.
|
|
118
|
+
`);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
const inputPath = argv.find((_a, i) => argv[i - 1] === "--input");
|
|
122
|
+
const policyPath = argv.find((_a, i) => argv[i - 1] === "--policy");
|
|
123
|
+
const minScoreStr = argv.find((_a, i) => argv[i - 1] === "--min-score");
|
|
124
|
+
const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
|
|
125
|
+
if (!inputPath) {
|
|
126
|
+
console.error("Error: --input is required. Provide a verdict JSON file.");
|
|
127
|
+
process.exitCode = 1;
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
let verdict;
|
|
131
|
+
try {
|
|
132
|
+
verdict = JSON.parse(readFileSync(inputPath, "utf-8"));
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
console.error(`Error: Cannot read or parse ${inputPath}`);
|
|
136
|
+
process.exitCode = 1;
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
let policy = defaultPolicy();
|
|
140
|
+
if (policyPath) {
|
|
141
|
+
try {
|
|
142
|
+
const custom = JSON.parse(readFileSync(policyPath, "utf-8"));
|
|
143
|
+
policy = { ...policy, ...custom };
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
console.error(`Error: Cannot read policy file ${policyPath}`);
|
|
147
|
+
process.exitCode = 1;
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
if (minScoreStr) {
|
|
152
|
+
policy.requireMinScore = parseInt(minScoreStr, 10);
|
|
153
|
+
}
|
|
154
|
+
const result = evaluateApproval(verdict, policy);
|
|
155
|
+
if (format === "json") {
|
|
156
|
+
console.log(JSON.stringify({
|
|
157
|
+
...result,
|
|
158
|
+
findings: { autoApproved: result.findings.approved.length, flagged: result.findings.flagged.length },
|
|
159
|
+
}, null, 2));
|
|
160
|
+
if (!result.approved)
|
|
161
|
+
process.exitCode = 1;
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
const icon = result.approved ? "✅" : "❌";
|
|
165
|
+
console.log(`\n Auto-Approval Result: ${icon} ${result.approved ? "APPROVED" : "REQUIRES REVIEW"}\n ─────────────────────────────`);
|
|
166
|
+
console.log(` Reason: ${result.reason}`);
|
|
167
|
+
console.log(` Score: ${verdict.overallScore ?? 0}/100`);
|
|
168
|
+
console.log(` Auto-approved: ${result.autoApproved} finding(s)`);
|
|
169
|
+
console.log(` Manual review: ${result.manualRequired} finding(s)`);
|
|
170
|
+
if (result.policyViolations.length > 0) {
|
|
171
|
+
console.log(`\n Policy Violations:`);
|
|
172
|
+
for (const v of result.policyViolations) {
|
|
173
|
+
console.log(` ❌ ${v}`);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
if (result.findings.flagged.length > 0) {
|
|
177
|
+
console.log(`\n Flagged Findings:`);
|
|
178
|
+
for (const f of result.findings.flagged.slice(0, 10)) {
|
|
179
|
+
console.log(` 🔴 [${f.severity}] ${f.ruleId}: ${f.title}`);
|
|
180
|
+
}
|
|
181
|
+
if (result.findings.flagged.length > 10) {
|
|
182
|
+
console.log(` ... and ${result.findings.flagged.length - 10} more`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
console.log();
|
|
186
|
+
if (!result.approved)
|
|
187
|
+
process.exitCode = 1;
|
|
188
|
+
}
|
|
189
|
+
//# sourceMappingURL=auto-approve.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auto-approve.js","sourceRoot":"","sources":["../../src/commands/auto-approve.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff-explain.d.ts","sourceRoot":"","sources":["../../src/commands/diff-explain.ts"],"names":[],"mappings":"AAAA;;GAEG;AAyFH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAoFnD"}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Diff-explain — Explain why specific diff changes were flagged.
|
|
3
|
+
*/
|
|
4
|
+
import { readFileSync } from "fs";
|
|
5
|
+
// ─── Knowledge base for common rules ────────────────────────────────────────
|
|
6
|
+
const RULE_EXPLANATIONS = {
|
|
7
|
+
"sql-injection": {
|
|
8
|
+
risk: "Attacker can manipulate database queries to extract, modify, or delete data",
|
|
9
|
+
whyFlagged: "User input is concatenated into SQL queries without parameterization",
|
|
10
|
+
action: "Use parameterized queries or prepared statements instead of string concatenation",
|
|
11
|
+
},
|
|
12
|
+
"xss-vulnerability": {
|
|
13
|
+
risk: "Attacker can inject malicious scripts that execute in other users' browsers",
|
|
14
|
+
whyFlagged: "User-supplied data is rendered in HTML without proper escaping",
|
|
15
|
+
action: "Sanitize and escape all user input before rendering in HTML context",
|
|
16
|
+
},
|
|
17
|
+
"hardcoded-secret": {
|
|
18
|
+
risk: "Credentials exposed in source code can be harvested from version control",
|
|
19
|
+
whyFlagged: "String patterns matching API keys, passwords, or tokens detected in code",
|
|
20
|
+
action: "Move secrets to environment variables or a secrets manager",
|
|
21
|
+
},
|
|
22
|
+
"path-traversal": {
|
|
23
|
+
risk: "Attacker can access files outside intended directories",
|
|
24
|
+
whyFlagged: "File paths constructed from user input without validation",
|
|
25
|
+
action: "Validate and normalize file paths, restrict to allowed directories",
|
|
26
|
+
},
|
|
27
|
+
"insecure-random": {
|
|
28
|
+
risk: "Predictable random values weaken security mechanisms",
|
|
29
|
+
whyFlagged: "Math.random() or similar used for security-sensitive operations",
|
|
30
|
+
action: "Use crypto.getRandomValues() or crypto.randomBytes() for security",
|
|
31
|
+
},
|
|
32
|
+
"error-info-leak": {
|
|
33
|
+
risk: "Internal error details can reveal system architecture to attackers",
|
|
34
|
+
whyFlagged: "Error messages expose stack traces, file paths, or internal details",
|
|
35
|
+
action: "Return generic error messages to users, log details server-side",
|
|
36
|
+
},
|
|
37
|
+
"missing-auth": {
|
|
38
|
+
risk: "Unauthorized access to protected resources or operations",
|
|
39
|
+
whyFlagged: "Endpoint or function lacks authentication/authorization checks",
|
|
40
|
+
action: "Add authentication middleware and verify user permissions",
|
|
41
|
+
},
|
|
42
|
+
"unsafe-deserialization": {
|
|
43
|
+
risk: "Attacker can execute arbitrary code via crafted serialized data",
|
|
44
|
+
whyFlagged: "Deserialization of untrusted data detected",
|
|
45
|
+
action: "Validate and whitelist types before deserialization, use safe formats like JSON",
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
// ─── Explanation engine ─────────────────────────────────────────────────────
|
|
49
|
+
function explainFinding(finding) {
|
|
50
|
+
const ruleId = finding.ruleId || "unknown";
|
|
51
|
+
const known = RULE_EXPLANATIONS[ruleId];
|
|
52
|
+
const explanation = known
|
|
53
|
+
? known.whyFlagged
|
|
54
|
+
: `This code was flagged by the '${ruleId}' rule. ${finding.description || "Review the identified pattern for potential issues."}`;
|
|
55
|
+
const risk = known
|
|
56
|
+
? known.risk
|
|
57
|
+
: `Potential ${finding.severity || "medium"}-severity issue that may affect code quality or security.`;
|
|
58
|
+
const action = known
|
|
59
|
+
? known.action
|
|
60
|
+
: finding.recommendation || "Review and address the finding according to best practices.";
|
|
61
|
+
return {
|
|
62
|
+
finding: { ruleId, severity: finding.severity || "medium", title: finding.title },
|
|
63
|
+
context: finding.description || "",
|
|
64
|
+
explanation,
|
|
65
|
+
risk,
|
|
66
|
+
suggestedAction: action,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
// ─── CLI ────────────────────────────────────────────────────────────────────
|
|
70
|
+
export function runDiffExplain(argv) {
|
|
71
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
72
|
+
console.log(`
|
|
73
|
+
judges diff-explain — Explain why changes were flagged
|
|
74
|
+
|
|
75
|
+
Usage:
|
|
76
|
+
judges diff-explain --input verdict.json
|
|
77
|
+
judges diff-explain --input verdict.json --rule sql-injection
|
|
78
|
+
judges diff-explain --input verdict.json --severity critical
|
|
79
|
+
judges diff-explain --format json
|
|
80
|
+
|
|
81
|
+
Options:
|
|
82
|
+
--input <file> TribunalVerdict JSON file (required)
|
|
83
|
+
--rule <id> Explain only findings for a specific rule
|
|
84
|
+
--severity <level> Filter by severity: critical, high, medium, low
|
|
85
|
+
--limit <n> Maximum findings to explain (default: 20)
|
|
86
|
+
--format json JSON output
|
|
87
|
+
--help, -h Show this help
|
|
88
|
+
|
|
89
|
+
Provides plain-language explanations of why code was flagged,
|
|
90
|
+
what the risk is, and what action to take. Useful for developers
|
|
91
|
+
unfamiliar with specific security or quality patterns.
|
|
92
|
+
`);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const inputPath = argv.find((_a, i) => argv[i - 1] === "--input");
|
|
96
|
+
const ruleFilter = argv.find((_a, i) => argv[i - 1] === "--rule");
|
|
97
|
+
const sevFilter = argv.find((_a, i) => argv[i - 1] === "--severity");
|
|
98
|
+
const limitStr = argv.find((_a, i) => argv[i - 1] === "--limit");
|
|
99
|
+
const limit = limitStr ? parseInt(limitStr, 10) : 20;
|
|
100
|
+
const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
|
|
101
|
+
if (!inputPath) {
|
|
102
|
+
console.error("Error: --input is required. Provide a verdict JSON file.");
|
|
103
|
+
process.exitCode = 1;
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
let verdict;
|
|
107
|
+
try {
|
|
108
|
+
verdict = JSON.parse(readFileSync(inputPath, "utf-8"));
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
console.error(`Error: Cannot read or parse ${inputPath}`);
|
|
112
|
+
process.exitCode = 1;
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
let findings = verdict.findings || [];
|
|
116
|
+
if (ruleFilter) {
|
|
117
|
+
findings = findings.filter((f) => f.ruleId === ruleFilter);
|
|
118
|
+
}
|
|
119
|
+
if (sevFilter) {
|
|
120
|
+
findings = findings.filter((f) => f.severity === sevFilter);
|
|
121
|
+
}
|
|
122
|
+
findings = findings.slice(0, limit);
|
|
123
|
+
if (findings.length === 0) {
|
|
124
|
+
console.log("No findings to explain.");
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
const explanations = findings.map(explainFinding);
|
|
128
|
+
if (format === "json") {
|
|
129
|
+
console.log(JSON.stringify({ count: explanations.length, explanations }, null, 2));
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
console.log(`\n Diff Explanations (${explanations.length} finding(s))\n ─────────────────────────────`);
|
|
133
|
+
for (const exp of explanations) {
|
|
134
|
+
const sevIcon = { critical: "🔴", high: "🟠", medium: "🟡", low: "🔵" };
|
|
135
|
+
const icon = sevIcon[exp.finding.severity] || "⬜";
|
|
136
|
+
console.log(`\n ${icon} [${exp.finding.severity}] ${exp.finding.ruleId}: ${exp.finding.title}`);
|
|
137
|
+
console.log(` Why flagged: ${exp.explanation}`);
|
|
138
|
+
console.log(` Risk: ${exp.risk}`);
|
|
139
|
+
console.log(` Action: ${exp.suggestedAction}`);
|
|
140
|
+
}
|
|
141
|
+
console.log();
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=diff-explain.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff-explain.js","sourceRoot":"","sources":["../../src/commands/diff-explain.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Finding-group — Group related findings into actionable clusters.
|
|
3
|
+
*/
|
|
4
|
+
import type { Finding } from "../types.js";
|
|
5
|
+
export interface FindingCluster {
|
|
6
|
+
id: string;
|
|
7
|
+
label: string;
|
|
8
|
+
pattern: string;
|
|
9
|
+
count: number;
|
|
10
|
+
severities: Record<string, number>;
|
|
11
|
+
findings: Finding[];
|
|
12
|
+
files: string[];
|
|
13
|
+
recommendation: string;
|
|
14
|
+
}
|
|
15
|
+
export declare function runFindingGroup(argv: string[]): void;
|
|
16
|
+
//# sourceMappingURL=finding-group.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finding-group.d.ts","sourceRoot":"","sources":["../../src/commands/finding-group.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAI3C,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;CACxB;AAiFD,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAoGpD"}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Finding-group — Group related findings into actionable clusters.
|
|
3
|
+
*/
|
|
4
|
+
import { readFileSync } from "fs";
|
|
5
|
+
// ─── Grouping strategies ────────────────────────────────────────────────────
|
|
6
|
+
function groupByRule(findings) {
|
|
7
|
+
const groups = new Map();
|
|
8
|
+
for (const f of findings) {
|
|
9
|
+
const key = f.ruleId || "unknown";
|
|
10
|
+
const existing = groups.get(key) || [];
|
|
11
|
+
existing.push(f);
|
|
12
|
+
groups.set(key, existing);
|
|
13
|
+
}
|
|
14
|
+
return groups;
|
|
15
|
+
}
|
|
16
|
+
function groupBySeverity(findings) {
|
|
17
|
+
const groups = new Map();
|
|
18
|
+
for (const f of findings) {
|
|
19
|
+
const key = f.severity || "unknown";
|
|
20
|
+
const existing = groups.get(key) || [];
|
|
21
|
+
existing.push(f);
|
|
22
|
+
groups.set(key, existing);
|
|
23
|
+
}
|
|
24
|
+
return groups;
|
|
25
|
+
}
|
|
26
|
+
function groupByCategory(findings) {
|
|
27
|
+
const groups = new Map();
|
|
28
|
+
for (const f of findings) {
|
|
29
|
+
const ruleId = f.ruleId || "";
|
|
30
|
+
let category = "other";
|
|
31
|
+
if (/sql|inject|xss|csrf|traversal|auth|secret|crypt/i.test(ruleId))
|
|
32
|
+
category = "security";
|
|
33
|
+
else if (/perf|cache|memory|leak|optim/i.test(ruleId))
|
|
34
|
+
category = "performance";
|
|
35
|
+
else if (/error|exception|throw|catch/i.test(ruleId))
|
|
36
|
+
category = "error-handling";
|
|
37
|
+
else if (/doc|comment|jsdoc|readme/i.test(ruleId))
|
|
38
|
+
category = "documentation";
|
|
39
|
+
else if (/test|assert|mock|coverage/i.test(ruleId))
|
|
40
|
+
category = "testing";
|
|
41
|
+
else if (/lint|format|naming|style/i.test(ruleId))
|
|
42
|
+
category = "code-style";
|
|
43
|
+
const existing = groups.get(category) || [];
|
|
44
|
+
existing.push(f);
|
|
45
|
+
groups.set(category, existing);
|
|
46
|
+
}
|
|
47
|
+
return groups;
|
|
48
|
+
}
|
|
49
|
+
// ─── Build clusters ─────────────────────────────────────────────────────────
|
|
50
|
+
function buildClusters(groups, strategy) {
|
|
51
|
+
const clusters = [];
|
|
52
|
+
let idx = 0;
|
|
53
|
+
for (const [key, findings] of groups) {
|
|
54
|
+
idx++;
|
|
55
|
+
const severities = {};
|
|
56
|
+
for (const f of findings) {
|
|
57
|
+
const s = f.severity || "unknown";
|
|
58
|
+
severities[s] = (severities[s] || 0) + 1;
|
|
59
|
+
}
|
|
60
|
+
const topFinding = findings.sort((a, b) => {
|
|
61
|
+
const order = { critical: 0, high: 1, medium: 2, low: 3 };
|
|
62
|
+
return (order[a.severity] ?? 4) - (order[b.severity] ?? 4);
|
|
63
|
+
})[0];
|
|
64
|
+
clusters.push({
|
|
65
|
+
id: `${strategy}-${idx}`,
|
|
66
|
+
label: key,
|
|
67
|
+
pattern: strategy,
|
|
68
|
+
count: findings.length,
|
|
69
|
+
severities,
|
|
70
|
+
findings,
|
|
71
|
+
files: [],
|
|
72
|
+
recommendation: topFinding?.recommendation || "Review grouped findings",
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
return clusters.sort((a, b) => b.count - a.count);
|
|
76
|
+
}
|
|
77
|
+
// ─── CLI ────────────────────────────────────────────────────────────────────
|
|
78
|
+
export function runFindingGroup(argv) {
|
|
79
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
80
|
+
console.log(`
|
|
81
|
+
judges finding-group — Group related findings into actionable clusters
|
|
82
|
+
|
|
83
|
+
Usage:
|
|
84
|
+
judges finding-group --input findings.json
|
|
85
|
+
judges finding-group --input findings.json --strategy severity
|
|
86
|
+
judges finding-group --input findings.json --format json
|
|
87
|
+
|
|
88
|
+
Options:
|
|
89
|
+
--input <file> JSON file with findings array (required)
|
|
90
|
+
--strategy <type> Grouping strategy: rule, severity, category (default: rule)
|
|
91
|
+
--format json JSON output
|
|
92
|
+
--min-count <n> Minimum findings to form a cluster (default: 1)
|
|
93
|
+
--help, -h Show this help
|
|
94
|
+
|
|
95
|
+
Strategies:
|
|
96
|
+
rule Group by rule ID (most specific)
|
|
97
|
+
severity Group by severity level
|
|
98
|
+
category Group by inferred category (security, performance, etc.)
|
|
99
|
+
|
|
100
|
+
Groups related findings to help you fix issues systematically rather
|
|
101
|
+
than one by one.
|
|
102
|
+
`);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const inputPath = argv.find((_a, i) => argv[i - 1] === "--input");
|
|
106
|
+
const strategy = argv.find((_a, i) => argv[i - 1] === "--strategy") || "rule";
|
|
107
|
+
const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
|
|
108
|
+
const minCountStr = argv.find((_a, i) => argv[i - 1] === "--min-count");
|
|
109
|
+
const minCount = minCountStr ? parseInt(minCountStr, 10) : 1;
|
|
110
|
+
if (!inputPath) {
|
|
111
|
+
console.error("Error: --input is required. Provide a JSON file with findings.");
|
|
112
|
+
process.exitCode = 1;
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
let findings;
|
|
116
|
+
try {
|
|
117
|
+
const raw = readFileSync(inputPath, "utf-8");
|
|
118
|
+
const parsed = JSON.parse(raw);
|
|
119
|
+
findings = Array.isArray(parsed) ? parsed : parsed.findings || [];
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
console.error(`Error: Cannot read or parse ${inputPath}`);
|
|
123
|
+
process.exitCode = 1;
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
if (findings.length === 0) {
|
|
127
|
+
console.log("No findings to group.");
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
let groups;
|
|
131
|
+
switch (strategy) {
|
|
132
|
+
case "severity":
|
|
133
|
+
groups = groupBySeverity(findings);
|
|
134
|
+
break;
|
|
135
|
+
case "category":
|
|
136
|
+
groups = groupByCategory(findings);
|
|
137
|
+
break;
|
|
138
|
+
default:
|
|
139
|
+
groups = groupByRule(findings);
|
|
140
|
+
}
|
|
141
|
+
let clusters = buildClusters(groups, strategy);
|
|
142
|
+
clusters = clusters.filter((c) => c.count >= minCount);
|
|
143
|
+
if (format === "json") {
|
|
144
|
+
console.log(JSON.stringify({
|
|
145
|
+
strategy,
|
|
146
|
+
totalFindings: findings.length,
|
|
147
|
+
clusterCount: clusters.length,
|
|
148
|
+
clusters: clusters.map((c) => ({ ...c, findings: undefined })),
|
|
149
|
+
}, null, 2));
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
console.log(`\n Finding Groups (strategy: ${strategy})\n ─────────────────────────────`);
|
|
153
|
+
console.log(` Total findings: ${findings.length}`);
|
|
154
|
+
console.log(` Clusters: ${clusters.length}\n`);
|
|
155
|
+
for (const cluster of clusters) {
|
|
156
|
+
const severitySummary = Object.entries(cluster.severities)
|
|
157
|
+
.map(([s, n]) => `${n} ${s}`)
|
|
158
|
+
.join(", ");
|
|
159
|
+
console.log(` [${cluster.id}] ${cluster.label} — ${cluster.count} finding(s)`);
|
|
160
|
+
console.log(` Severities: ${severitySummary}`);
|
|
161
|
+
console.log(` Recommendation: ${cluster.recommendation}`);
|
|
162
|
+
console.log();
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
//# sourceMappingURL=finding-group.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finding-group.js","sourceRoot":"","sources":["../../src/commands/finding-group.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fix-suggest.d.ts","sourceRoot":"","sources":["../../src/commands/fix-suggest.ts"],"names":[],"mappings":"AAAA;;GAEG;AAuHH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAoFlD"}
|