@kevinrabun/judges 3.80.0 → 3.81.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 +13 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +63 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/finding-cwe-map.d.ts +5 -0
- package/dist/commands/finding-cwe-map.d.ts.map +1 -0
- package/dist/commands/finding-cwe-map.js +134 -0
- package/dist/commands/finding-cwe-map.js.map +1 -0
- package/dist/commands/finding-false-neg-check.d.ts +9 -0
- package/dist/commands/finding-false-neg-check.d.ts.map +1 -0
- package/dist/commands/finding-false-neg-check.js +140 -0
- package/dist/commands/finding-false-neg-check.js.map +1 -0
- package/dist/commands/finding-pattern-match.d.ts +5 -0
- package/dist/commands/finding-pattern-match.d.ts.map +1 -0
- package/dist/commands/finding-pattern-match.js +166 -0
- package/dist/commands/finding-pattern-match.js.map +1 -0
- package/dist/commands/finding-risk-matrix.d.ts +5 -0
- package/dist/commands/finding-risk-matrix.d.ts.map +1 -0
- package/dist/commands/finding-risk-matrix.js +127 -0
- package/dist/commands/finding-risk-matrix.js.map +1 -0
- package/dist/commands/review-dependency-graph.d.ts +5 -0
- package/dist/commands/review-dependency-graph.d.ts.map +1 -0
- package/dist/commands/review-dependency-graph.js +95 -0
- package/dist/commands/review-dependency-graph.js.map +1 -0
- package/dist/commands/review-diff-stats.d.ts +5 -0
- package/dist/commands/review-diff-stats.d.ts.map +1 -0
- package/dist/commands/review-diff-stats.js +91 -0
- package/dist/commands/review-diff-stats.js.map +1 -0
- package/dist/commands/review-exclude-vendor.d.ts +5 -0
- package/dist/commands/review-exclude-vendor.d.ts.map +1 -0
- package/dist/commands/review-exclude-vendor.js +159 -0
- package/dist/commands/review-exclude-vendor.js.map +1 -0
- package/dist/commands/review-file-stats.d.ts +5 -0
- package/dist/commands/review-file-stats.d.ts.map +1 -0
- package/dist/commands/review-file-stats.js +131 -0
- package/dist/commands/review-file-stats.js.map +1 -0
- package/dist/commands/review-rule-filter.d.ts +5 -0
- package/dist/commands/review-rule-filter.d.ts.map +1 -0
- package/dist/commands/review-rule-filter.js +117 -0
- package/dist/commands/review-rule-filter.js.map +1 -0
- package/package.json +1 -1
- package/server.json +2 -2
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Finding-cwe-map — Map findings to CWE (Common Weakness Enumeration) identifiers.
|
|
3
|
+
*/
|
|
4
|
+
import { readFileSync, existsSync } from "fs";
|
|
5
|
+
// ─── CWE Mapping ───────────────────────────────────────────────────────────
|
|
6
|
+
const KEYWORD_CWE_MAP = {
|
|
7
|
+
injection: [
|
|
8
|
+
{ cwe: "CWE-89", name: "SQL Injection" },
|
|
9
|
+
{ cwe: "CWE-78", name: "OS Command Injection" },
|
|
10
|
+
],
|
|
11
|
+
"sql injection": [{ cwe: "CWE-89", name: "SQL Injection" }],
|
|
12
|
+
xss: [{ cwe: "CWE-79", name: "Cross-site Scripting" }],
|
|
13
|
+
"cross-site scripting": [{ cwe: "CWE-79", name: "Cross-site Scripting" }],
|
|
14
|
+
csrf: [{ cwe: "CWE-352", name: "Cross-Site Request Forgery" }],
|
|
15
|
+
"path traversal": [{ cwe: "CWE-22", name: "Path Traversal" }],
|
|
16
|
+
"buffer overflow": [{ cwe: "CWE-120", name: "Buffer Copy without Checking Size" }],
|
|
17
|
+
authentication: [{ cwe: "CWE-287", name: "Improper Authentication" }],
|
|
18
|
+
authorization: [{ cwe: "CWE-862", name: "Missing Authorization" }],
|
|
19
|
+
hardcoded: [{ cwe: "CWE-798", name: "Use of Hard-coded Credentials" }],
|
|
20
|
+
credential: [{ cwe: "CWE-798", name: "Use of Hard-coded Credentials" }],
|
|
21
|
+
password: [{ cwe: "CWE-521", name: "Weak Password Requirements" }],
|
|
22
|
+
deserialization: [{ cwe: "CWE-502", name: "Deserialization of Untrusted Data" }],
|
|
23
|
+
ssrf: [{ cwe: "CWE-918", name: "Server-Side Request Forgery" }],
|
|
24
|
+
"race condition": [{ cwe: "CWE-362", name: "Race Condition" }],
|
|
25
|
+
"null pointer": [{ cwe: "CWE-476", name: "NULL Pointer Dereference" }],
|
|
26
|
+
"memory leak": [{ cwe: "CWE-401", name: "Missing Release of Memory" }],
|
|
27
|
+
"information disclosure": [{ cwe: "CWE-200", name: "Exposure of Sensitive Information" }],
|
|
28
|
+
cryptographic: [{ cwe: "CWE-327", name: "Use of a Broken Crypto Algorithm" }],
|
|
29
|
+
encryption: [{ cwe: "CWE-326", name: "Inadequate Encryption Strength" }],
|
|
30
|
+
"open redirect": [{ cwe: "CWE-601", name: "URL Redirection to Untrusted Site" }],
|
|
31
|
+
xml: [{ cwe: "CWE-611", name: "Improper Restriction of XML External Entity" }],
|
|
32
|
+
privilege: [{ cwe: "CWE-269", name: "Improper Privilege Management" }],
|
|
33
|
+
};
|
|
34
|
+
function mapFindingToCwe(finding) {
|
|
35
|
+
const text = `${finding.ruleId || ""} ${finding.title || ""} ${finding.description || ""}`.toLowerCase();
|
|
36
|
+
const matches = [];
|
|
37
|
+
const seen = new Set();
|
|
38
|
+
for (const [keyword, cwes] of Object.entries(KEYWORD_CWE_MAP)) {
|
|
39
|
+
if (text.includes(keyword)) {
|
|
40
|
+
for (const c of cwes) {
|
|
41
|
+
if (!seen.has(c.cwe)) {
|
|
42
|
+
seen.add(c.cwe);
|
|
43
|
+
matches.push(c);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return matches;
|
|
49
|
+
}
|
|
50
|
+
// ─── CLI ────────────────────────────────────────────────────────────────────
|
|
51
|
+
export function runFindingCweMap(argv) {
|
|
52
|
+
if (argv.includes("--help") || argv.includes("-h") || argv.length === 0) {
|
|
53
|
+
console.log(`
|
|
54
|
+
judges finding-cwe-map — Map findings to CWE identifiers
|
|
55
|
+
|
|
56
|
+
Usage:
|
|
57
|
+
judges finding-cwe-map --file <results.json> [options]
|
|
58
|
+
|
|
59
|
+
Options:
|
|
60
|
+
--file <path> Result file (required)
|
|
61
|
+
--cwe <id> Filter to specific CWE (e.g., CWE-89)
|
|
62
|
+
--format json JSON output
|
|
63
|
+
--help, -h Show this help
|
|
64
|
+
|
|
65
|
+
Maps security findings to their corresponding CWE identifiers.
|
|
66
|
+
`);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const file = argv.find((_a, i) => argv[i - 1] === "--file");
|
|
70
|
+
if (!file) {
|
|
71
|
+
console.error("Error: --file required");
|
|
72
|
+
process.exitCode = 1;
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
if (!existsSync(file)) {
|
|
76
|
+
console.error(`Error: file not found: ${file}`);
|
|
77
|
+
process.exitCode = 1;
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const cweFilter = argv.find((_a, i) => argv[i - 1] === "--cwe");
|
|
81
|
+
const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
|
|
82
|
+
let verdict;
|
|
83
|
+
try {
|
|
84
|
+
verdict = JSON.parse(readFileSync(file, "utf-8"));
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
console.error("Error: could not parse file");
|
|
88
|
+
process.exitCode = 1;
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
const findings = verdict.findings || [];
|
|
92
|
+
let mapped = findings.map((f) => ({ ...f, cwes: mapFindingToCwe(f) }));
|
|
93
|
+
if (cweFilter) {
|
|
94
|
+
mapped = mapped.filter((f) => f.cwes.some((c) => c.cwe === cweFilter));
|
|
95
|
+
}
|
|
96
|
+
const withCwe = mapped.filter((f) => f.cwes.length > 0);
|
|
97
|
+
// CWE frequency
|
|
98
|
+
const cweFreq = new Map();
|
|
99
|
+
for (const f of withCwe) {
|
|
100
|
+
for (const c of f.cwes) {
|
|
101
|
+
const existing = cweFreq.get(c.cwe);
|
|
102
|
+
if (existing)
|
|
103
|
+
existing.count++;
|
|
104
|
+
else
|
|
105
|
+
cweFreq.set(c.cwe, { name: c.name, count: 1 });
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (format === "json") {
|
|
109
|
+
console.log(JSON.stringify({
|
|
110
|
+
total: findings.length,
|
|
111
|
+
mapped: withCwe.length,
|
|
112
|
+
cweSummary: [...cweFreq.entries()].map(([cwe, info]) => ({ cwe, name: info.name, count: info.count })),
|
|
113
|
+
findings: mapped,
|
|
114
|
+
}, null, 2));
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
console.log(`\nCWE Mapping:`);
|
|
118
|
+
console.log("═".repeat(70));
|
|
119
|
+
console.log(` ${withCwe.length} of ${findings.length} findings mapped to CWE identifiers`);
|
|
120
|
+
console.log("─".repeat(70));
|
|
121
|
+
console.log("\n CWE Summary:");
|
|
122
|
+
for (const [cwe, info] of [...cweFreq.entries()].sort((a, b) => b[1].count - a[1].count)) {
|
|
123
|
+
console.log(` ${cwe.padEnd(12)} ${info.name.padEnd(40)} x${info.count}`);
|
|
124
|
+
}
|
|
125
|
+
console.log("\n Mapped Findings:");
|
|
126
|
+
for (const f of withCwe.slice(0, 15)) {
|
|
127
|
+
const cweStr = f.cwes.map((c) => c.cwe).join(", ");
|
|
128
|
+
console.log(` ${(f.ruleId || "unknown").padEnd(22)} → ${cweStr}`);
|
|
129
|
+
}
|
|
130
|
+
if (withCwe.length > 15)
|
|
131
|
+
console.log(` ... and ${withCwe.length - 15} more`);
|
|
132
|
+
console.log("═".repeat(70));
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=finding-cwe-map.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finding-cwe-map.js","sourceRoot":"","sources":["../../src/commands/finding-cwe-map.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Finding-false-neg-check — Check for potential false negatives.
|
|
3
|
+
*
|
|
4
|
+
* Analyzes code for common vulnerability patterns that may have been
|
|
5
|
+
* missed by the current judge panel. Uses keyword heuristics to flag
|
|
6
|
+
* lines that warrant manual review.
|
|
7
|
+
*/
|
|
8
|
+
export declare function runFindingFalseNegCheck(argv: string[]): void;
|
|
9
|
+
//# sourceMappingURL=finding-false-neg-check.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finding-false-neg-check.d.ts","sourceRoot":"","sources":["../../src/commands/finding-false-neg-check.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AA+EH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAkF5D"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Finding-false-neg-check — Check for potential false negatives.
|
|
3
|
+
*
|
|
4
|
+
* Analyzes code for common vulnerability patterns that may have been
|
|
5
|
+
* missed by the current judge panel. Uses keyword heuristics to flag
|
|
6
|
+
* lines that warrant manual review.
|
|
7
|
+
*/
|
|
8
|
+
import { readFileSync, existsSync } from "fs";
|
|
9
|
+
// ─── Patterns ───────────────────────────────────────────────────────────────
|
|
10
|
+
const SUSPICIOUS_PATTERNS = [
|
|
11
|
+
{ regex: /eval\s*\(/, category: "injection", reason: "Dynamic code evaluation" },
|
|
12
|
+
{ regex: /innerHTML\s*=/, category: "xss", reason: "Direct innerHTML assignment" },
|
|
13
|
+
{ regex: /dangerouslySetInnerHTML/, category: "xss", reason: "React dangerous HTML" },
|
|
14
|
+
{ regex: /document\.write\s*\(/, category: "xss", reason: "Document write usage" },
|
|
15
|
+
{ regex: /exec\s*\(/, category: "command-injection", reason: "Command execution" },
|
|
16
|
+
{ regex: /child_process/, category: "command-injection", reason: "Child process usage" },
|
|
17
|
+
{ regex: /SELECT\s.*FROM\s.*WHERE/i, category: "sql-injection", reason: "Raw SQL query" },
|
|
18
|
+
{ regex: /password\s*[:=]\s*['"]/, category: "hardcoded-secret", reason: "Hardcoded password" },
|
|
19
|
+
{ regex: /api[_-]?key\s*[:=]\s*['"]/, category: "hardcoded-secret", reason: "Hardcoded API key" },
|
|
20
|
+
{ regex: /secret\s*[:=]\s*['"]/, category: "hardcoded-secret", reason: "Hardcoded secret" },
|
|
21
|
+
{ regex: /Math\.random\s*\(/, category: "weak-crypto", reason: "Math.random for security" },
|
|
22
|
+
{ regex: /createHash\s*\(\s*['"]md5['"]/, category: "weak-crypto", reason: "MD5 hash usage" },
|
|
23
|
+
{ regex: /createHash\s*\(\s*['"]sha1['"]/, category: "weak-crypto", reason: "SHA1 hash usage" },
|
|
24
|
+
{
|
|
25
|
+
regex: /disable.*ssl|verify\s*=\s*false|rejectUnauthorized.*false/i,
|
|
26
|
+
category: "tls",
|
|
27
|
+
reason: "TLS verification disabled",
|
|
28
|
+
},
|
|
29
|
+
{ regex: /cors\(\s*\)/, category: "cors", reason: "Permissive CORS" },
|
|
30
|
+
{ regex: /chmod\s+777/, category: "permissions", reason: "World-writable permissions" },
|
|
31
|
+
{ regex: /TODO.*security|FIXME.*vuln|HACK.*auth/i, category: "todo", reason: "Security-related TODO" },
|
|
32
|
+
{ regex: /console\.(log|debug)\s*\(.*password/i, category: "logging", reason: "Password in logs" },
|
|
33
|
+
];
|
|
34
|
+
// ─── Helpers ────────────────────────────────────────────────────────────────
|
|
35
|
+
function scanFile(filePath) {
|
|
36
|
+
const content = readFileSync(filePath, "utf-8");
|
|
37
|
+
const lines = content.split("\n");
|
|
38
|
+
const candidates = [];
|
|
39
|
+
for (let i = 0; i < lines.length; i++) {
|
|
40
|
+
const line = lines[i];
|
|
41
|
+
for (const pat of SUSPICIOUS_PATTERNS) {
|
|
42
|
+
if (pat.regex.test(line)) {
|
|
43
|
+
candidates.push({
|
|
44
|
+
lineNumber: i + 1,
|
|
45
|
+
lineContent: line.trim().slice(0, 120),
|
|
46
|
+
pattern: pat.regex.source,
|
|
47
|
+
category: pat.category,
|
|
48
|
+
reason: pat.reason,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return candidates;
|
|
54
|
+
}
|
|
55
|
+
function crossCheckVerdict(candidates, verdict) {
|
|
56
|
+
// Filter out candidates that already have findings for the same line/category
|
|
57
|
+
const coveredLines = new Set();
|
|
58
|
+
for (const f of verdict.findings) {
|
|
59
|
+
if (f.lineNumbers) {
|
|
60
|
+
for (const ln of f.lineNumbers)
|
|
61
|
+
coveredLines.add(ln);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return candidates.filter((c) => !coveredLines.has(c.lineNumber));
|
|
65
|
+
}
|
|
66
|
+
// ─── CLI ────────────────────────────────────────────────────────────────────
|
|
67
|
+
export function runFindingFalseNegCheck(argv) {
|
|
68
|
+
const fileIdx = argv.indexOf("--file");
|
|
69
|
+
const verdictIdx = argv.indexOf("--verdict");
|
|
70
|
+
const formatIdx = argv.indexOf("--format");
|
|
71
|
+
const categoryIdx = argv.indexOf("--category");
|
|
72
|
+
const filePath = fileIdx >= 0 ? argv[fileIdx + 1] : undefined;
|
|
73
|
+
const verdictPath = verdictIdx >= 0 ? argv[verdictIdx + 1] : undefined;
|
|
74
|
+
const format = formatIdx >= 0 ? argv[formatIdx + 1] : "table";
|
|
75
|
+
const filterCategory = categoryIdx >= 0 ? argv[categoryIdx + 1] : undefined;
|
|
76
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
77
|
+
console.log(`
|
|
78
|
+
judges finding-false-neg-check — Check for potential false negatives
|
|
79
|
+
|
|
80
|
+
Usage:
|
|
81
|
+
judges finding-false-neg-check --file <source> [--verdict <verdict.json>]
|
|
82
|
+
[--format table|json] [--category <cat>]
|
|
83
|
+
|
|
84
|
+
Options:
|
|
85
|
+
--file <path> Source file to scan for suspicious patterns (required)
|
|
86
|
+
--verdict <path> Verdict JSON to cross-check (optional)
|
|
87
|
+
--format <fmt> Output format: table (default), json
|
|
88
|
+
--category <cat> Filter by category (injection, xss, hardcoded-secret, etc.)
|
|
89
|
+
--help, -h Show this help
|
|
90
|
+
`);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
if (!filePath) {
|
|
94
|
+
console.error("Error: --file required");
|
|
95
|
+
process.exitCode = 1;
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
if (!existsSync(filePath)) {
|
|
99
|
+
console.error(`Error: file not found: ${filePath}`);
|
|
100
|
+
process.exitCode = 1;
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
let candidates = scanFile(filePath);
|
|
104
|
+
if (verdictPath && existsSync(verdictPath)) {
|
|
105
|
+
try {
|
|
106
|
+
const verdict = JSON.parse(readFileSync(verdictPath, "utf-8"));
|
|
107
|
+
candidates = crossCheckVerdict(candidates, verdict);
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
/* skip cross-check */
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (filterCategory) {
|
|
114
|
+
candidates = candidates.filter((c) => c.category === filterCategory);
|
|
115
|
+
}
|
|
116
|
+
if (format === "json") {
|
|
117
|
+
console.log(JSON.stringify(candidates, null, 2));
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
if (candidates.length === 0) {
|
|
121
|
+
console.log("No potential false negatives detected.");
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
console.log(`\nPotential False Negatives in ${filePath}`);
|
|
125
|
+
console.log("═".repeat(70));
|
|
126
|
+
console.log(`${"Line".padEnd(7)} ${"Category".padEnd(20)} Reason`);
|
|
127
|
+
console.log("─".repeat(70));
|
|
128
|
+
for (const c of candidates) {
|
|
129
|
+
console.log(`${String(c.lineNumber).padEnd(7)} ${c.category.padEnd(20)} ${c.reason}`);
|
|
130
|
+
console.log(` ${c.lineContent}`);
|
|
131
|
+
}
|
|
132
|
+
console.log("─".repeat(70));
|
|
133
|
+
const categories = new Map();
|
|
134
|
+
for (const c of candidates) {
|
|
135
|
+
categories.set(c.category, (categories.get(c.category) || 0) + 1);
|
|
136
|
+
}
|
|
137
|
+
console.log(`${candidates.length} suspicious patterns found across ${categories.size} categories`);
|
|
138
|
+
console.log("═".repeat(70));
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=finding-false-neg-check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finding-false-neg-check.js","sourceRoot":"","sources":["../../src/commands/finding-false-neg-check.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finding-pattern-match.d.ts","sourceRoot":"","sources":["../../src/commands/finding-pattern-match.ts"],"names":[],"mappings":"AAAA;;GAEG;AAgDH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAiI3D"}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Finding-pattern-match — Match findings against custom patterns.
|
|
3
|
+
*/
|
|
4
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
|
|
5
|
+
import { dirname, join } from "path";
|
|
6
|
+
// ─── Helpers ────────────────────────────────────────────────────────────────
|
|
7
|
+
function patternsFile() {
|
|
8
|
+
return join(process.cwd(), ".judges", "match-patterns.json");
|
|
9
|
+
}
|
|
10
|
+
function loadPatterns() {
|
|
11
|
+
const f = patternsFile();
|
|
12
|
+
if (!existsSync(f))
|
|
13
|
+
return [];
|
|
14
|
+
try {
|
|
15
|
+
return JSON.parse(readFileSync(f, "utf-8"));
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return [];
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function savePatterns(patterns) {
|
|
22
|
+
const f = patternsFile();
|
|
23
|
+
const d = dirname(f);
|
|
24
|
+
if (!existsSync(d))
|
|
25
|
+
mkdirSync(d, { recursive: true });
|
|
26
|
+
writeFileSync(f, JSON.stringify(patterns, null, 2));
|
|
27
|
+
}
|
|
28
|
+
function matchFinding(finding, pattern) {
|
|
29
|
+
if (pattern.rulePattern && !finding.ruleId.includes(pattern.rulePattern))
|
|
30
|
+
return false;
|
|
31
|
+
if (pattern.titlePattern && !finding.title.toLowerCase().includes(pattern.titlePattern.toLowerCase()))
|
|
32
|
+
return false;
|
|
33
|
+
if (pattern.severities.length > 0 && !pattern.severities.includes(finding.severity))
|
|
34
|
+
return false;
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
// ─── CLI ────────────────────────────────────────────────────────────────────
|
|
38
|
+
export function runFindingPatternMatch(argv) {
|
|
39
|
+
const sub = argv[0];
|
|
40
|
+
if (!sub || sub === "--help" || sub === "-h") {
|
|
41
|
+
console.log(`
|
|
42
|
+
judges finding-pattern-match — Match findings against custom patterns
|
|
43
|
+
|
|
44
|
+
Usage:
|
|
45
|
+
judges finding-pattern-match apply --file <results.json> [--format json]
|
|
46
|
+
judges finding-pattern-match add --name <name> [--rule <pattern>] [--title <pattern>] [--severity <list>] [--action <act>]
|
|
47
|
+
judges finding-pattern-match list
|
|
48
|
+
judges finding-pattern-match remove --name <name>
|
|
49
|
+
judges finding-pattern-match clear
|
|
50
|
+
|
|
51
|
+
Options:
|
|
52
|
+
--name <name> Pattern name
|
|
53
|
+
--rule <pattern> Substring to match in ruleId
|
|
54
|
+
--title <pattern> Substring to match in title
|
|
55
|
+
--severity <list> Comma-separated severities
|
|
56
|
+
--action <act> Action: flag, suppress, escalate (default: flag)
|
|
57
|
+
--format json JSON output
|
|
58
|
+
--help, -h Show this help
|
|
59
|
+
`);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const args = argv.slice(1);
|
|
63
|
+
const patterns = loadPatterns();
|
|
64
|
+
if (sub === "apply") {
|
|
65
|
+
const file = args.find((_a, i) => args[i - 1] === "--file");
|
|
66
|
+
const format = args.find((_a, i) => args[i - 1] === "--format") || "text";
|
|
67
|
+
if (!file) {
|
|
68
|
+
console.error("Error: --file required");
|
|
69
|
+
process.exitCode = 1;
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
if (!existsSync(file)) {
|
|
73
|
+
console.error(`Error: file not found: ${file}`);
|
|
74
|
+
process.exitCode = 1;
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
let verdict;
|
|
78
|
+
try {
|
|
79
|
+
verdict = JSON.parse(readFileSync(file, "utf-8"));
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
console.error("Error: could not parse file");
|
|
83
|
+
process.exitCode = 1;
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const findings = verdict.findings || [];
|
|
87
|
+
const matches = findings.map((f) => {
|
|
88
|
+
const matched = patterns.filter((p) => matchFinding(f, p));
|
|
89
|
+
return { ...f, matchedPatterns: matched.map((p) => p.name), actions: matched.map((p) => p.action) };
|
|
90
|
+
});
|
|
91
|
+
const withMatches = matches.filter((m) => m.matchedPatterns.length > 0);
|
|
92
|
+
if (format === "json") {
|
|
93
|
+
console.log(JSON.stringify({ total: findings.length, matched: withMatches.length, findings: matches }, null, 2));
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
console.log(`\nPattern Match Results:`);
|
|
97
|
+
console.log("═".repeat(65));
|
|
98
|
+
console.log(` ${withMatches.length} of ${findings.length} findings matched ${patterns.length} patterns`);
|
|
99
|
+
console.log("─".repeat(65));
|
|
100
|
+
for (const m of withMatches.slice(0, 20)) {
|
|
101
|
+
const acts = [...new Set(m.actions)].join(", ");
|
|
102
|
+
console.log(` ${m.ruleId.padEnd(22)} [${acts}] patterns: ${m.matchedPatterns.join(", ")}`);
|
|
103
|
+
}
|
|
104
|
+
if (withMatches.length > 20)
|
|
105
|
+
console.log(` ... and ${withMatches.length - 20} more`);
|
|
106
|
+
console.log("═".repeat(65));
|
|
107
|
+
}
|
|
108
|
+
else if (sub === "add") {
|
|
109
|
+
const name = args.find((_a, i) => args[i - 1] === "--name");
|
|
110
|
+
if (!name) {
|
|
111
|
+
console.error("Error: --name required");
|
|
112
|
+
process.exitCode = 1;
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
const ruleP = args.find((_a, i) => args[i - 1] === "--rule") || "";
|
|
116
|
+
const titleP = args.find((_a, i) => args[i - 1] === "--title") || "";
|
|
117
|
+
const sevStr = args.find((_a, i) => args[i - 1] === "--severity") || "";
|
|
118
|
+
const action = args.find((_a, i) => args[i - 1] === "--action") || "flag";
|
|
119
|
+
patterns.push({
|
|
120
|
+
name,
|
|
121
|
+
rulePattern: ruleP,
|
|
122
|
+
titlePattern: titleP,
|
|
123
|
+
severities: sevStr ? sevStr.split(",") : [],
|
|
124
|
+
action,
|
|
125
|
+
});
|
|
126
|
+
savePatterns(patterns);
|
|
127
|
+
console.log(`Added pattern: ${name}`);
|
|
128
|
+
}
|
|
129
|
+
else if (sub === "list") {
|
|
130
|
+
if (patterns.length === 0) {
|
|
131
|
+
console.log("No patterns defined.");
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
console.log(`\nMatch Patterns (${patterns.length}):`);
|
|
135
|
+
console.log("═".repeat(55));
|
|
136
|
+
for (const p of patterns) {
|
|
137
|
+
console.log(` ${p.name.padEnd(18)} [${p.action}] rule:${p.rulePattern || "*"} title:${p.titlePattern || "*"}`);
|
|
138
|
+
}
|
|
139
|
+
console.log("═".repeat(55));
|
|
140
|
+
}
|
|
141
|
+
else if (sub === "remove") {
|
|
142
|
+
const name = args.find((_a, i) => args[i - 1] === "--name");
|
|
143
|
+
if (!name) {
|
|
144
|
+
console.error("Error: --name required");
|
|
145
|
+
process.exitCode = 1;
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
const filtered = patterns.filter((p) => p.name !== name);
|
|
149
|
+
if (filtered.length === patterns.length) {
|
|
150
|
+
console.error(`Pattern "${name}" not found.`);
|
|
151
|
+
process.exitCode = 1;
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
savePatterns(filtered);
|
|
155
|
+
console.log(`Removed pattern: ${name}`);
|
|
156
|
+
}
|
|
157
|
+
else if (sub === "clear") {
|
|
158
|
+
savePatterns([]);
|
|
159
|
+
console.log("All patterns cleared.");
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
console.error(`Unknown subcommand: ${sub}. Use --help for usage.`);
|
|
163
|
+
process.exitCode = 1;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
//# sourceMappingURL=finding-pattern-match.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finding-pattern-match.js","sourceRoot":"","sources":["../../src/commands/finding-pattern-match.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finding-risk-matrix.d.ts","sourceRoot":"","sources":["../../src/commands/finding-risk-matrix.ts"],"names":[],"mappings":"AAAA;;GAEG;AAwEH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CA2EzD"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Finding-risk-matrix — Generate risk matrices from findings.
|
|
3
|
+
*/
|
|
4
|
+
import { readFileSync, existsSync } from "fs";
|
|
5
|
+
// ─── Constants ──────────────────────────────────────────────────────────────
|
|
6
|
+
const SEVERITY_LEVELS = ["critical", "high", "medium", "low", "info"];
|
|
7
|
+
const CONFIDENCE_LEVELS = ["high", "medium", "low"];
|
|
8
|
+
// ─── Helpers ────────────────────────────────────────────────────────────────
|
|
9
|
+
function loadVerdict(filePath) {
|
|
10
|
+
if (!existsSync(filePath))
|
|
11
|
+
return null;
|
|
12
|
+
try {
|
|
13
|
+
return JSON.parse(readFileSync(filePath, "utf-8"));
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function buildMatrix(verdict) {
|
|
20
|
+
const cells = [];
|
|
21
|
+
for (const sev of SEVERITY_LEVELS) {
|
|
22
|
+
for (const conf of CONFIDENCE_LEVELS) {
|
|
23
|
+
const matching = verdict.findings.filter((f) => {
|
|
24
|
+
const fSev = (f.severity || "medium").toLowerCase();
|
|
25
|
+
const fConf = f.confidence !== undefined && f.confidence !== null
|
|
26
|
+
? f.confidence >= 0.8
|
|
27
|
+
? "high"
|
|
28
|
+
: f.confidence >= 0.5
|
|
29
|
+
? "medium"
|
|
30
|
+
: "low"
|
|
31
|
+
: "medium";
|
|
32
|
+
return fSev === sev && fConf === conf;
|
|
33
|
+
});
|
|
34
|
+
if (matching.length > 0) {
|
|
35
|
+
cells.push({
|
|
36
|
+
severity: sev,
|
|
37
|
+
confidence: conf,
|
|
38
|
+
count: matching.length,
|
|
39
|
+
findings: matching.map((f) => f.title),
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return cells;
|
|
45
|
+
}
|
|
46
|
+
function riskScore(severity, confidence) {
|
|
47
|
+
const sevWeight = { critical: 5, high: 4, medium: 3, low: 2, info: 1 };
|
|
48
|
+
const confWeight = { high: 3, medium: 2, low: 1 };
|
|
49
|
+
const score = (sevWeight[severity] || 1) * (confWeight[confidence] || 1);
|
|
50
|
+
if (score >= 12)
|
|
51
|
+
return "CRITICAL";
|
|
52
|
+
if (score >= 8)
|
|
53
|
+
return "HIGH";
|
|
54
|
+
if (score >= 4)
|
|
55
|
+
return "MEDIUM";
|
|
56
|
+
return "LOW";
|
|
57
|
+
}
|
|
58
|
+
// ─── CLI ────────────────────────────────────────────────────────────────────
|
|
59
|
+
export function runFindingRiskMatrix(argv) {
|
|
60
|
+
const fileIdx = argv.indexOf("--file");
|
|
61
|
+
const formatIdx = argv.indexOf("--format");
|
|
62
|
+
const filePath = fileIdx >= 0 ? argv[fileIdx + 1] : undefined;
|
|
63
|
+
const format = formatIdx >= 0 ? argv[formatIdx + 1] : "table";
|
|
64
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
65
|
+
console.log(`
|
|
66
|
+
judges finding-risk-matrix — Generate risk matrix from findings
|
|
67
|
+
|
|
68
|
+
Usage:
|
|
69
|
+
judges finding-risk-matrix --file <verdict.json> [--format table|json|markdown]
|
|
70
|
+
|
|
71
|
+
Options:
|
|
72
|
+
--file <path> Path to verdict JSON file (required)
|
|
73
|
+
--format <fmt> Output format: table (default), json, markdown
|
|
74
|
+
--help, -h Show this help
|
|
75
|
+
`);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
if (!filePath) {
|
|
79
|
+
console.error("Error: --file required");
|
|
80
|
+
process.exitCode = 1;
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
const verdict = loadVerdict(filePath);
|
|
84
|
+
if (!verdict) {
|
|
85
|
+
console.error(`Error: cannot load ${filePath}`);
|
|
86
|
+
process.exitCode = 1;
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const cells = buildMatrix(verdict);
|
|
90
|
+
if (format === "json") {
|
|
91
|
+
console.log(JSON.stringify(cells, null, 2));
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
if (format === "markdown") {
|
|
95
|
+
console.log("| Severity | Confidence | Count | Risk Level |");
|
|
96
|
+
console.log("|----------|------------|-------|------------|");
|
|
97
|
+
for (const c of cells) {
|
|
98
|
+
console.log(`| ${c.severity} | ${c.confidence} | ${c.count} | ${riskScore(c.severity, c.confidence)} |`);
|
|
99
|
+
}
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
// Table format
|
|
103
|
+
console.log("\nRisk Matrix");
|
|
104
|
+
console.log("═".repeat(60));
|
|
105
|
+
console.log(`${"Severity".padEnd(12)} ${"Confidence".padEnd(12)} ${"Count".padEnd(8)} Risk Level`);
|
|
106
|
+
console.log("─".repeat(60));
|
|
107
|
+
const sorted = [...cells].sort((a, b) => {
|
|
108
|
+
const sevOrder = SEVERITY_LEVELS.indexOf(a.severity) - SEVERITY_LEVELS.indexOf(b.severity);
|
|
109
|
+
if (sevOrder !== 0)
|
|
110
|
+
return sevOrder;
|
|
111
|
+
return CONFIDENCE_LEVELS.indexOf(a.confidence) - CONFIDENCE_LEVELS.indexOf(b.confidence);
|
|
112
|
+
});
|
|
113
|
+
for (const c of sorted) {
|
|
114
|
+
const risk = riskScore(c.severity, c.confidence);
|
|
115
|
+
console.log(`${c.severity.padEnd(12)} ${c.confidence.padEnd(12)} ${String(c.count).padEnd(8)} ${risk}`);
|
|
116
|
+
for (const t of c.findings.slice(0, 3)) {
|
|
117
|
+
console.log(` → ${t}`);
|
|
118
|
+
}
|
|
119
|
+
if (c.findings.length > 3)
|
|
120
|
+
console.log(` … and ${c.findings.length - 3} more`);
|
|
121
|
+
}
|
|
122
|
+
console.log("─".repeat(60));
|
|
123
|
+
const total = cells.reduce((s, c) => s + c.count, 0);
|
|
124
|
+
console.log(`Total findings: ${total}`);
|
|
125
|
+
console.log("═".repeat(60));
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=finding-risk-matrix.js.map
|