@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.
Files changed (42) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/cli.d.ts.map +1 -1
  3. package/dist/cli.js +63 -0
  4. package/dist/cli.js.map +1 -1
  5. package/dist/commands/finding-cwe-map.d.ts +5 -0
  6. package/dist/commands/finding-cwe-map.d.ts.map +1 -0
  7. package/dist/commands/finding-cwe-map.js +134 -0
  8. package/dist/commands/finding-cwe-map.js.map +1 -0
  9. package/dist/commands/finding-false-neg-check.d.ts +9 -0
  10. package/dist/commands/finding-false-neg-check.d.ts.map +1 -0
  11. package/dist/commands/finding-false-neg-check.js +140 -0
  12. package/dist/commands/finding-false-neg-check.js.map +1 -0
  13. package/dist/commands/finding-pattern-match.d.ts +5 -0
  14. package/dist/commands/finding-pattern-match.d.ts.map +1 -0
  15. package/dist/commands/finding-pattern-match.js +166 -0
  16. package/dist/commands/finding-pattern-match.js.map +1 -0
  17. package/dist/commands/finding-risk-matrix.d.ts +5 -0
  18. package/dist/commands/finding-risk-matrix.d.ts.map +1 -0
  19. package/dist/commands/finding-risk-matrix.js +127 -0
  20. package/dist/commands/finding-risk-matrix.js.map +1 -0
  21. package/dist/commands/review-dependency-graph.d.ts +5 -0
  22. package/dist/commands/review-dependency-graph.d.ts.map +1 -0
  23. package/dist/commands/review-dependency-graph.js +95 -0
  24. package/dist/commands/review-dependency-graph.js.map +1 -0
  25. package/dist/commands/review-diff-stats.d.ts +5 -0
  26. package/dist/commands/review-diff-stats.d.ts.map +1 -0
  27. package/dist/commands/review-diff-stats.js +91 -0
  28. package/dist/commands/review-diff-stats.js.map +1 -0
  29. package/dist/commands/review-exclude-vendor.d.ts +5 -0
  30. package/dist/commands/review-exclude-vendor.d.ts.map +1 -0
  31. package/dist/commands/review-exclude-vendor.js +159 -0
  32. package/dist/commands/review-exclude-vendor.js.map +1 -0
  33. package/dist/commands/review-file-stats.d.ts +5 -0
  34. package/dist/commands/review-file-stats.d.ts.map +1 -0
  35. package/dist/commands/review-file-stats.js +131 -0
  36. package/dist/commands/review-file-stats.js.map +1 -0
  37. package/dist/commands/review-rule-filter.d.ts +5 -0
  38. package/dist/commands/review-rule-filter.d.ts.map +1 -0
  39. package/dist/commands/review-rule-filter.js +117 -0
  40. package/dist/commands/review-rule-filter.js.map +1 -0
  41. package/package.json +1 -1
  42. 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,5 @@
1
+ /**
2
+ * Finding-pattern-match — Match findings against custom patterns.
3
+ */
4
+ export declare function runFindingPatternMatch(argv: string[]): void;
5
+ //# sourceMappingURL=finding-pattern-match.d.ts.map
@@ -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,5 @@
1
+ /**
2
+ * Finding-risk-matrix — Generate risk matrices from findings.
3
+ */
4
+ export declare function runFindingRiskMatrix(argv: string[]): void;
5
+ //# sourceMappingURL=finding-risk-matrix.d.ts.map
@@ -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