@kevinrabun/judges 3.63.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 +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/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/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/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-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-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/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 @@
|
|
|
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"}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fix-suggest — Generate concrete code fix suggestions for findings.
|
|
3
|
+
*/
|
|
4
|
+
import { readFileSync } from "fs";
|
|
5
|
+
// ─── Fix patterns database ──────────────────────────────────────────────────
|
|
6
|
+
const FIX_PATTERNS = {
|
|
7
|
+
"sql-injection": {
|
|
8
|
+
problem: "User input concatenated directly into SQL query string",
|
|
9
|
+
fixDesc: "Use parameterized queries with placeholders",
|
|
10
|
+
before: "db.query(`SELECT * FROM users WHERE id = ${userId}`)",
|
|
11
|
+
after: 'db.query("SELECT * FROM users WHERE id = ?", [userId])',
|
|
12
|
+
refs: ["https://cheatsheetseries.owasp.org/cheatsheets/Query_Parameterization_Cheat_Sheet.html"],
|
|
13
|
+
},
|
|
14
|
+
"xss-vulnerability": {
|
|
15
|
+
problem: "User-supplied data rendered without escaping",
|
|
16
|
+
fixDesc: "Sanitize output using context-appropriate encoding",
|
|
17
|
+
before: "element.innerHTML = userInput;",
|
|
18
|
+
after: "element.textContent = userInput;",
|
|
19
|
+
refs: ["https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html"],
|
|
20
|
+
},
|
|
21
|
+
"hardcoded-secret": {
|
|
22
|
+
problem: "Secret value hardcoded in source file",
|
|
23
|
+
fixDesc: "Move secret to environment variable or secrets manager",
|
|
24
|
+
before: 'const API_KEY = "sk-abc123...";',
|
|
25
|
+
after: "const API_KEY = process.env.API_KEY;",
|
|
26
|
+
refs: ["https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html"],
|
|
27
|
+
},
|
|
28
|
+
"path-traversal": {
|
|
29
|
+
problem: "File path constructed from user input without validation",
|
|
30
|
+
fixDesc: "Normalize path and validate it stays within allowed directory",
|
|
31
|
+
before: "const file = path.join(uploadDir, req.params.filename);",
|
|
32
|
+
after: "const file = path.join(uploadDir, path.basename(req.params.filename));",
|
|
33
|
+
refs: ["https://owasp.org/www-community/attacks/Path_Traversal"],
|
|
34
|
+
},
|
|
35
|
+
"insecure-random": {
|
|
36
|
+
problem: "Math.random() used for security-sensitive value",
|
|
37
|
+
fixDesc: "Use cryptographically secure random number generator",
|
|
38
|
+
before: "const token = Math.random().toString(36);",
|
|
39
|
+
after: 'const token = crypto.randomBytes(32).toString("hex");',
|
|
40
|
+
refs: ["CWE-338: Use of Cryptographically Weak PRNG"],
|
|
41
|
+
},
|
|
42
|
+
"missing-auth": {
|
|
43
|
+
problem: "Endpoint lacks authentication check",
|
|
44
|
+
fixDesc: "Add authentication middleware before handler",
|
|
45
|
+
before: "app.get('/api/data', handler);",
|
|
46
|
+
after: "app.get('/api/data', authMiddleware, handler);",
|
|
47
|
+
refs: ["https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html"],
|
|
48
|
+
},
|
|
49
|
+
"error-info-leak": {
|
|
50
|
+
problem: "Detailed error information sent to client",
|
|
51
|
+
fixDesc: "Return generic error response, log details server-side",
|
|
52
|
+
before: "res.status(500).json({ error: err.stack });",
|
|
53
|
+
after: 'res.status(500).json({ error: "Internal server error" });',
|
|
54
|
+
refs: ["https://cheatsheetseries.owasp.org/cheatsheets/Error_Handling_Cheat_Sheet.html"],
|
|
55
|
+
},
|
|
56
|
+
"unsafe-deserialization": {
|
|
57
|
+
problem: "Deserialization of untrusted data",
|
|
58
|
+
fixDesc: "Validate input format and use safe deserialization",
|
|
59
|
+
before: "const obj = eval('(' + input + ')');",
|
|
60
|
+
after: "const obj = JSON.parse(input);",
|
|
61
|
+
refs: ["https://cheatsheetseries.owasp.org/cheatsheets/Deserialization_Cheat_Sheet.html"],
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
// ─── Suggestion engine ─────────────────────────────────────────────────────
|
|
65
|
+
function suggestFix(finding) {
|
|
66
|
+
const ruleId = finding.ruleId || "unknown";
|
|
67
|
+
const known = FIX_PATTERNS[ruleId];
|
|
68
|
+
if (known) {
|
|
69
|
+
return {
|
|
70
|
+
ruleId,
|
|
71
|
+
severity: finding.severity || "medium",
|
|
72
|
+
title: finding.title,
|
|
73
|
+
problem: known.problem,
|
|
74
|
+
fixDescription: known.fixDesc,
|
|
75
|
+
beforePattern: known.before,
|
|
76
|
+
afterPattern: known.after,
|
|
77
|
+
confidence: "high",
|
|
78
|
+
references: known.refs,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
// Generate generic suggestion from finding data
|
|
82
|
+
return {
|
|
83
|
+
ruleId,
|
|
84
|
+
severity: finding.severity || "medium",
|
|
85
|
+
title: finding.title,
|
|
86
|
+
problem: finding.description || "Code pattern flagged for review",
|
|
87
|
+
fixDescription: finding.recommendation || "Apply the recommended fix pattern",
|
|
88
|
+
beforePattern: "",
|
|
89
|
+
afterPattern: "",
|
|
90
|
+
confidence: "medium",
|
|
91
|
+
references: [],
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
// ─── CLI ────────────────────────────────────────────────────────────────────
|
|
95
|
+
export function runFixSuggest(argv) {
|
|
96
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
97
|
+
console.log(`
|
|
98
|
+
judges fix-suggest — Generate concrete fix suggestions
|
|
99
|
+
|
|
100
|
+
Usage:
|
|
101
|
+
judges fix-suggest --input verdict.json
|
|
102
|
+
judges fix-suggest --input verdict.json --rule sql-injection
|
|
103
|
+
judges fix-suggest --input verdict.json --severity critical
|
|
104
|
+
judges fix-suggest --format json
|
|
105
|
+
|
|
106
|
+
Options:
|
|
107
|
+
--input <file> TribunalVerdict JSON file (required)
|
|
108
|
+
--rule <id> Suggest fixes only for a specific rule
|
|
109
|
+
--severity <level> Filter by severity
|
|
110
|
+
--limit <n> Maximum suggestions (default: 20)
|
|
111
|
+
--format json JSON output
|
|
112
|
+
--help, -h Show this help
|
|
113
|
+
|
|
114
|
+
Generates concrete before/after code patterns for each finding,
|
|
115
|
+
with links to OWASP/CWE references where applicable.
|
|
116
|
+
`);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
const inputPath = argv.find((_a, i) => argv[i - 1] === "--input");
|
|
120
|
+
const ruleFilter = argv.find((_a, i) => argv[i - 1] === "--rule");
|
|
121
|
+
const sevFilter = argv.find((_a, i) => argv[i - 1] === "--severity");
|
|
122
|
+
const limitStr = argv.find((_a, i) => argv[i - 1] === "--limit");
|
|
123
|
+
const limit = limitStr ? parseInt(limitStr, 10) : 20;
|
|
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 findings = verdict.findings || [];
|
|
140
|
+
if (ruleFilter)
|
|
141
|
+
findings = findings.filter((f) => f.ruleId === ruleFilter);
|
|
142
|
+
if (sevFilter)
|
|
143
|
+
findings = findings.filter((f) => f.severity === sevFilter);
|
|
144
|
+
findings = findings.slice(0, limit);
|
|
145
|
+
if (findings.length === 0) {
|
|
146
|
+
console.log("No findings to suggest fixes for.");
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
const suggestions = findings.map(suggestFix);
|
|
150
|
+
if (format === "json") {
|
|
151
|
+
console.log(JSON.stringify({ count: suggestions.length, suggestions }, null, 2));
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
console.log(`\n Fix Suggestions (${suggestions.length})\n ─────────────────────────────`);
|
|
155
|
+
for (const s of suggestions) {
|
|
156
|
+
const sevIcon = { critical: "🔴", high: "🟠", medium: "🟡", low: "🔵" };
|
|
157
|
+
const icon = sevIcon[s.severity] || "⬜";
|
|
158
|
+
console.log(`\n ${icon} [${s.severity}] ${s.ruleId}: ${s.title}`);
|
|
159
|
+
console.log(` Problem: ${s.problem}`);
|
|
160
|
+
console.log(` Fix: ${s.fixDescription}`);
|
|
161
|
+
if (s.beforePattern) {
|
|
162
|
+
console.log(` Before: ${s.beforePattern}`);
|
|
163
|
+
console.log(` After: ${s.afterPattern}`);
|
|
164
|
+
}
|
|
165
|
+
console.log(` Confidence: ${s.confidence}`);
|
|
166
|
+
if (s.references.length > 0) {
|
|
167
|
+
console.log(` References: ${s.references.join(", ")}`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
console.log();
|
|
171
|
+
}
|
|
172
|
+
//# sourceMappingURL=fix-suggest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fix-suggest.js","sourceRoot":"","sources":["../../src/commands/fix-suggest.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"multi-lang-review.d.ts","sourceRoot":"","sources":["../../src/commands/multi-lang-review.ts"],"names":[],"mappings":"AAAA;;GAEG;AAsMH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAsFvD"}
|