@kevinrabun/judges 3.80.0 → 3.82.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 +26 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +126 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/finding-age-tracker.d.ts +8 -0
- package/dist/commands/finding-age-tracker.d.ts.map +1 -0
- package/dist/commands/finding-age-tracker.js +153 -0
- package/dist/commands/finding-age-tracker.js.map +1 -0
- 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-duplicate-rule.d.ts +5 -0
- package/dist/commands/finding-duplicate-rule.d.ts.map +1 -0
- package/dist/commands/finding-duplicate-rule.js +104 -0
- package/dist/commands/finding-duplicate-rule.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-line-blame.d.ts +8 -0
- package/dist/commands/finding-line-blame.d.ts.map +1 -0
- package/dist/commands/finding-line-blame.js +133 -0
- package/dist/commands/finding-line-blame.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/finding-summary-digest.d.ts +8 -0
- package/dist/commands/finding-summary-digest.d.ts.map +1 -0
- package/dist/commands/finding-summary-digest.js +146 -0
- package/dist/commands/finding-summary-digest.js.map +1 -0
- package/dist/commands/review-code-owner.d.ts +8 -0
- package/dist/commands/review-code-owner.d.ts.map +1 -0
- package/dist/commands/review-code-owner.js +165 -0
- package/dist/commands/review-code-owner.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-export-pdf.d.ts +8 -0
- package/dist/commands/review-export-pdf.d.ts.map +1 -0
- package/dist/commands/review-export-pdf.js +132 -0
- package/dist/commands/review-export-pdf.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-parallel-files.d.ts +8 -0
- package/dist/commands/review-parallel-files.d.ts.map +1 -0
- package/dist/commands/review-parallel-files.js +135 -0
- package/dist/commands/review-parallel-files.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/dist/commands/review-scope-lock.d.ts +8 -0
- package/dist/commands/review-scope-lock.d.ts.map +1 -0
- package/dist/commands/review-scope-lock.js +139 -0
- package/dist/commands/review-scope-lock.js.map +1 -0
- package/dist/commands/review-watch-mode.d.ts +8 -0
- package/dist/commands/review-watch-mode.d.ts.map +1 -0
- package/dist/commands/review-watch-mode.js +133 -0
- package/dist/commands/review-watch-mode.js.map +1 -0
- package/package.json +1 -1
- package/server.json +2 -2
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Finding-age-tracker — Track the age of findings over time.
|
|
3
|
+
*
|
|
4
|
+
* Records when findings first appear and tracks their age,
|
|
5
|
+
* helping prioritize long-standing issues.
|
|
6
|
+
*/
|
|
7
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
|
|
8
|
+
import { dirname, join } from "path";
|
|
9
|
+
// ─── Helpers ────────────────────────────────────────────────────────────────
|
|
10
|
+
function ageFile() {
|
|
11
|
+
return join(process.cwd(), ".judges", "finding-ages.json");
|
|
12
|
+
}
|
|
13
|
+
function loadAges() {
|
|
14
|
+
const f = ageFile();
|
|
15
|
+
if (!existsSync(f))
|
|
16
|
+
return [];
|
|
17
|
+
try {
|
|
18
|
+
return JSON.parse(readFileSync(f, "utf-8"));
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return [];
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function saveAges(ages) {
|
|
25
|
+
const f = ageFile();
|
|
26
|
+
const d = dirname(f);
|
|
27
|
+
if (!existsSync(d))
|
|
28
|
+
mkdirSync(d, { recursive: true });
|
|
29
|
+
writeFileSync(f, JSON.stringify(ages, null, 2));
|
|
30
|
+
}
|
|
31
|
+
function daysBetween(d1, d2) {
|
|
32
|
+
const ms = new Date(d2).getTime() - new Date(d1).getTime();
|
|
33
|
+
return Math.floor(ms / (1000 * 60 * 60 * 24));
|
|
34
|
+
}
|
|
35
|
+
function ageLabel(days) {
|
|
36
|
+
if (days === 0)
|
|
37
|
+
return "new";
|
|
38
|
+
if (days <= 7)
|
|
39
|
+
return `${days}d`;
|
|
40
|
+
if (days <= 30)
|
|
41
|
+
return `${Math.floor(days / 7)}w`;
|
|
42
|
+
return `${Math.floor(days / 30)}mo`;
|
|
43
|
+
}
|
|
44
|
+
// ─── CLI ────────────────────────────────────────────────────────────────────
|
|
45
|
+
export function runFindingAgeTracker(argv) {
|
|
46
|
+
const sub = argv[0];
|
|
47
|
+
if (!sub || sub === "--help" || sub === "-h") {
|
|
48
|
+
console.log(`
|
|
49
|
+
judges finding-age-tracker — Track finding ages
|
|
50
|
+
|
|
51
|
+
Usage:
|
|
52
|
+
judges finding-age-tracker update --file <verdict.json>
|
|
53
|
+
judges finding-age-tracker show [--min-age <days>] [--format table|json]
|
|
54
|
+
judges finding-age-tracker clear
|
|
55
|
+
|
|
56
|
+
Options:
|
|
57
|
+
--file <path> Verdict JSON to record (for update)
|
|
58
|
+
--min-age <days> Show only findings older than N days
|
|
59
|
+
--format <fmt> Output format: table (default), json
|
|
60
|
+
--help, -h Show this help
|
|
61
|
+
`);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (sub === "update") {
|
|
65
|
+
const fileIdx = argv.indexOf("--file");
|
|
66
|
+
const filePath = fileIdx >= 0 ? argv[fileIdx + 1] : undefined;
|
|
67
|
+
if (!filePath) {
|
|
68
|
+
console.error("Error: --file required");
|
|
69
|
+
process.exitCode = 1;
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
if (!existsSync(filePath)) {
|
|
73
|
+
console.error(`Error: file not found: ${filePath}`);
|
|
74
|
+
process.exitCode = 1;
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
let verdict;
|
|
78
|
+
try {
|
|
79
|
+
verdict = JSON.parse(readFileSync(filePath, "utf-8"));
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
console.error("Error: invalid JSON");
|
|
83
|
+
process.exitCode = 1;
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const ages = loadAges();
|
|
87
|
+
const now = new Date().toISOString();
|
|
88
|
+
const ageMap = new Map(ages.map((a) => [`${a.ruleId}:${a.title}`, a]));
|
|
89
|
+
for (const f of verdict.findings) {
|
|
90
|
+
const key = `${f.ruleId}:${f.title}`;
|
|
91
|
+
const existing = ageMap.get(key);
|
|
92
|
+
if (existing) {
|
|
93
|
+
existing.lastSeen = now;
|
|
94
|
+
existing.occurrences++;
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
ageMap.set(key, {
|
|
98
|
+
ruleId: f.ruleId,
|
|
99
|
+
title: f.title,
|
|
100
|
+
firstSeen: now,
|
|
101
|
+
lastSeen: now,
|
|
102
|
+
occurrences: 1,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
saveAges([...ageMap.values()]);
|
|
107
|
+
console.log(`Updated: ${verdict.findings.length} findings recorded (${ageMap.size} total tracked)`);
|
|
108
|
+
}
|
|
109
|
+
else if (sub === "show") {
|
|
110
|
+
const minAgeIdx = argv.indexOf("--min-age");
|
|
111
|
+
const formatIdx = argv.indexOf("--format");
|
|
112
|
+
const minAge = minAgeIdx >= 0 ? parseInt(argv[minAgeIdx + 1], 10) : 0;
|
|
113
|
+
const format = formatIdx >= 0 ? argv[formatIdx + 1] : "table";
|
|
114
|
+
const ages = loadAges();
|
|
115
|
+
if (ages.length === 0) {
|
|
116
|
+
console.log("No age data recorded. Run 'update' first.");
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
const now = new Date().toISOString();
|
|
120
|
+
const filtered = ages
|
|
121
|
+
.map((a) => ({ ...a, ageDays: daysBetween(a.firstSeen, now) }))
|
|
122
|
+
.filter((a) => a.ageDays >= minAge)
|
|
123
|
+
.sort((a, b) => b.ageDays - a.ageDays);
|
|
124
|
+
if (format === "json") {
|
|
125
|
+
console.log(JSON.stringify(filtered, null, 2));
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
console.log(`\nFinding Age Tracker (${filtered.length} findings)`);
|
|
129
|
+
console.log("═".repeat(70));
|
|
130
|
+
console.log(`${"Age".padEnd(8)} ${"Seen".padEnd(6)} ${"Rule".padEnd(25)} Title`);
|
|
131
|
+
console.log("─".repeat(70));
|
|
132
|
+
for (const a of filtered) {
|
|
133
|
+
const age = ageLabel(a.ageDays).padEnd(8);
|
|
134
|
+
const seen = String(a.occurrences).padEnd(6);
|
|
135
|
+
const rule = a.ruleId.length > 23 ? a.ruleId.slice(0, 23) + "…" : a.ruleId;
|
|
136
|
+
const title = a.title.length > 25 ? a.title.slice(0, 25) + "…" : a.title;
|
|
137
|
+
console.log(`${age} ${seen} ${rule.padEnd(25)} ${title}`);
|
|
138
|
+
}
|
|
139
|
+
console.log("─".repeat(70));
|
|
140
|
+
const avgAge = filtered.length > 0 ? (filtered.reduce((s, a) => s + a.ageDays, 0) / filtered.length).toFixed(1) : "0";
|
|
141
|
+
console.log(`Average age: ${avgAge} days`);
|
|
142
|
+
console.log("═".repeat(70));
|
|
143
|
+
}
|
|
144
|
+
else if (sub === "clear") {
|
|
145
|
+
saveAges([]);
|
|
146
|
+
console.log("Finding age data cleared.");
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
console.error(`Unknown subcommand: ${sub}. Use --help for usage.`);
|
|
150
|
+
process.exitCode = 1;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
//# sourceMappingURL=finding-age-tracker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finding-age-tracker.js","sourceRoot":"","sources":["../../src/commands/finding-age-tracker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finding-cwe-map.d.ts","sourceRoot":"","sources":["../../src/commands/finding-cwe-map.ts"],"names":[],"mappings":"AAAA;;GAEG;AAwDH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CA+FrD"}
|
|
@@ -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":"AAAA;;GAEG;AAGH,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAE9C,8EAA8E;AAE9E,MAAM,eAAe,GAAoD;IACvE,SAAS,EAAE;QACT,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,eAAe,EAAE;QACxC,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,sBAAsB,EAAE;KAChD;IACD,eAAe,EAAE,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,eAAe,EAAE,CAAC;IAC3D,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,sBAAsB,EAAE,CAAC;IACtD,sBAAsB,EAAE,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,sBAAsB,EAAE,CAAC;IACzE,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,4BAA4B,EAAE,CAAC;IAC9D,gBAAgB,EAAE,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,gBAAgB,EAAE,CAAC;IAC7D,iBAAiB,EAAE,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,mCAAmC,EAAE,CAAC;IAClF,cAAc,EAAE,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,yBAAyB,EAAE,CAAC;IACrE,aAAa,EAAE,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,uBAAuB,EAAE,CAAC;IAClE,SAAS,EAAE,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,+BAA+B,EAAE,CAAC;IACtE,UAAU,EAAE,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,+BAA+B,EAAE,CAAC;IACvE,QAAQ,EAAE,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,4BAA4B,EAAE,CAAC;IAClE,eAAe,EAAE,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,mCAAmC,EAAE,CAAC;IAChF,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,6BAA6B,EAAE,CAAC;IAC/D,gBAAgB,EAAE,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,gBAAgB,EAAE,CAAC;IAC9D,cAAc,EAAE,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,0BAA0B,EAAE,CAAC;IACtE,aAAa,EAAE,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,2BAA2B,EAAE,CAAC;IACtE,wBAAwB,EAAE,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,mCAAmC,EAAE,CAAC;IACzF,aAAa,EAAE,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,kCAAkC,EAAE,CAAC;IAC7E,UAAU,EAAE,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,gCAAgC,EAAE,CAAC;IACxE,eAAe,EAAE,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,mCAAmC,EAAE,CAAC;IAChF,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,6CAA6C,EAAE,CAAC;IAC9E,SAAS,EAAE,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,+BAA+B,EAAE,CAAC;CACvE,CAAC;AAEF,SAAS,eAAe,CAAC,OAAgB;IACvC,MAAM,IAAI,GAAG,GAAG,OAAO,CAAC,MAAM,IAAI,EAAE,IAAI,OAAO,CAAC,KAAK,IAAI,EAAE,IAAI,OAAO,CAAC,WAAW,IAAI,EAAE,EAAE,CAAC,WAAW,EAAE,CAAC;IACzG,MAAM,OAAO,GAAoC,EAAE,CAAC;IACpD,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAE/B,KAAK,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC,EAAE,CAAC;QAC9D,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;YAC3B,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;gBACrB,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;oBACrB,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;oBAChB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;gBAClB,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,+EAA+E;AAE/E,MAAM,UAAU,gBAAgB,CAAC,IAAc;IAC7C,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxE,OAAO,CAAC,GAAG,CAAC;;;;;;;;;;;;;CAaf,CAAC,CAAC;QACC,OAAO;IACT,CAAC;IAED,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,EAAU,EAAE,CAAS,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,QAAQ,CAAC,CAAC;IAC5E,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAC;QACxC,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACrB,OAAO;IACT,CAAC;IACD,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACtB,OAAO,CAAC,KAAK,CAAC,0BAA0B,IAAI,EAAE,CAAC,CAAC;QAChD,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACrB,OAAO;IACT,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,EAAU,EAAE,CAAS,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC;IAChF,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,EAAU,EAAE,CAAS,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,UAAU,CAAC,IAAI,MAAM,CAAC;IAE1F,IAAI,OAAwB,CAAC;IAC7B,IAAI,CAAC;QACH,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;IACpD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,KAAK,CAAC,6BAA6B,CAAC,CAAC;QAC7C,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACrB,OAAO;IACT,CAAC;IAED,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAC;IACxC,IAAI,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,eAAe,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAEvE,IAAI,SAAS,EAAE,CAAC;QACd,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC;IACzE,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAExD,gBAAgB;IAChB,MAAM,OAAO,GAAG,IAAI,GAAG,EAA2C,CAAC;IACnE,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;YACvB,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;YACpC,IAAI,QAAQ;gBAAE,QAAQ,CAAC,KAAK,EAAE,CAAC;;gBAC1B,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;QACtD,CAAC;IACH,CAAC;IAED,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;QACtB,OAAO,CAAC,GAAG,CACT,IAAI,CAAC,SAAS,CACZ;YACE,KAAK,EAAE,QAAQ,CAAC,MAAM;YACtB,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,UAAU,EAAE,CAAC,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;YACtG,QAAQ,EAAE,MAAM;SACjB,EACD,IAAI,EACJ,CAAC,CACF,CACF,CAAC;QACF,OAAO;IACT,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAC9B,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;IAC5B,OAAO,CAAC,GAAG,CAAC,KAAK,OAAO,CAAC,MAAM,OAAO,QAAQ,CAAC,MAAM,qCAAqC,CAAC,CAAC;IAC5F,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;IAE5B,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;IAChC,KAAK,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC;QACzF,OAAO,CAAC,GAAG,CAAC,OAAO,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;IAC9E,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC;IACpC,KAAK,MAAM,CAAC,IAAI,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;QACrC,MAAM,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnD,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,IAAI,SAAS,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,MAAM,EAAE,CAAC,CAAC;IACvE,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,GAAG,EAAE;QAAE,OAAO,CAAC,GAAG,CAAC,eAAe,OAAO,CAAC,MAAM,GAAG,EAAE,OAAO,CAAC,CAAC;IAChF,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;AAC9B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finding-duplicate-rule.d.ts","sourceRoot":"","sources":["../../src/commands/finding-duplicate-rule.ts"],"names":[],"mappings":"AAAA;;GAEG;AA4DH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAiE5D"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Finding-duplicate-rule — Detect duplicate or overlapping rules in findings.
|
|
3
|
+
*/
|
|
4
|
+
import { readFileSync, existsSync } from "fs";
|
|
5
|
+
// ─── Helpers ────────────────────────────────────────────────────────────────
|
|
6
|
+
function detectDuplicates(verdict) {
|
|
7
|
+
const groups = new Map();
|
|
8
|
+
for (const f of verdict.findings) {
|
|
9
|
+
// Group by normalized title (lowercase, trimmed)
|
|
10
|
+
const key = f.title.toLowerCase().trim();
|
|
11
|
+
const group = groups.get(key) || { key, count: 0, ruleIds: [], titles: [], lineOverlap: false };
|
|
12
|
+
group.count++;
|
|
13
|
+
if (!group.ruleIds.includes(f.ruleId))
|
|
14
|
+
group.ruleIds.push(f.ruleId);
|
|
15
|
+
if (!group.titles.includes(f.title))
|
|
16
|
+
group.titles.push(f.title);
|
|
17
|
+
groups.set(key, group);
|
|
18
|
+
}
|
|
19
|
+
// Check for line number overlaps between different rule IDs
|
|
20
|
+
const lineMap = new Map();
|
|
21
|
+
for (const f of verdict.findings) {
|
|
22
|
+
if (f.lineNumbers) {
|
|
23
|
+
for (const ln of f.lineNumbers) {
|
|
24
|
+
const rules = lineMap.get(ln) || [];
|
|
25
|
+
if (!rules.includes(f.ruleId))
|
|
26
|
+
rules.push(f.ruleId);
|
|
27
|
+
lineMap.set(ln, rules);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
// Flag groups where multiple rules point to same lines
|
|
32
|
+
for (const [_ln, rules] of lineMap) {
|
|
33
|
+
if (rules.length > 1) {
|
|
34
|
+
for (const [, group] of groups) {
|
|
35
|
+
if (rules.some((r) => group.ruleIds.includes(r))) {
|
|
36
|
+
group.lineOverlap = true;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return [...groups.values()]
|
|
42
|
+
.filter((g) => g.count > 1 || g.ruleIds.length > 1 || g.lineOverlap)
|
|
43
|
+
.sort((a, b) => b.count - a.count);
|
|
44
|
+
}
|
|
45
|
+
// ─── CLI ────────────────────────────────────────────────────────────────────
|
|
46
|
+
export function runFindingDuplicateRule(argv) {
|
|
47
|
+
const fileIdx = argv.indexOf("--file");
|
|
48
|
+
const formatIdx = argv.indexOf("--format");
|
|
49
|
+
const filePath = fileIdx >= 0 ? argv[fileIdx + 1] : undefined;
|
|
50
|
+
const format = formatIdx >= 0 ? argv[formatIdx + 1] : "table";
|
|
51
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
52
|
+
console.log(`
|
|
53
|
+
judges finding-duplicate-rule — Detect duplicate or overlapping rules
|
|
54
|
+
|
|
55
|
+
Usage:
|
|
56
|
+
judges finding-duplicate-rule --file <verdict.json> [--format table|json]
|
|
57
|
+
|
|
58
|
+
Options:
|
|
59
|
+
--file <path> Path to verdict JSON file (required)
|
|
60
|
+
--format <fmt> Output format: table (default), json
|
|
61
|
+
--help, -h Show this help
|
|
62
|
+
`);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
if (!filePath) {
|
|
66
|
+
console.error("Error: --file required");
|
|
67
|
+
process.exitCode = 1;
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (!existsSync(filePath)) {
|
|
71
|
+
console.error(`Error: file not found: ${filePath}`);
|
|
72
|
+
process.exitCode = 1;
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
let verdict;
|
|
76
|
+
try {
|
|
77
|
+
verdict = JSON.parse(readFileSync(filePath, "utf-8"));
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
console.error("Error: invalid JSON");
|
|
81
|
+
process.exitCode = 1;
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const duplicates = detectDuplicates(verdict);
|
|
85
|
+
if (format === "json") {
|
|
86
|
+
console.log(JSON.stringify(duplicates, null, 2));
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (duplicates.length === 0) {
|
|
90
|
+
console.log("No duplicate or overlapping rules detected.");
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
console.log(`\nDuplicate/Overlapping Rules (${duplicates.length} groups)`);
|
|
94
|
+
console.log("═".repeat(70));
|
|
95
|
+
for (const g of duplicates) {
|
|
96
|
+
const overlap = g.lineOverlap ? " [LINE OVERLAP]" : "";
|
|
97
|
+
console.log(`\n "${g.titles[0]}" × ${g.count}${overlap}`);
|
|
98
|
+
console.log(` Rules: ${g.ruleIds.join(", ")}`);
|
|
99
|
+
}
|
|
100
|
+
console.log("\n" + "═".repeat(70));
|
|
101
|
+
const totalDups = duplicates.reduce((s, g) => s + g.count - 1, 0);
|
|
102
|
+
console.log(`${totalDups} duplicate findings across ${duplicates.length} groups`);
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=finding-duplicate-rule.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finding-duplicate-rule.js","sourceRoot":"","sources":["../../src/commands/finding-duplicate-rule.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,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Finding-line-blame — Map findings to git blame information.
|
|
3
|
+
*
|
|
4
|
+
* Shows who last modified lines associated with findings,
|
|
5
|
+
* helping attribute findings to specific changes/authors.
|
|
6
|
+
*/
|
|
7
|
+
export declare function runFindingLineBlame(argv: string[]): void;
|
|
8
|
+
//# sourceMappingURL=finding-line-blame.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finding-line-blame.d.ts","sourceRoot":"","sources":["../../src/commands/finding-line-blame.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AA2DH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAoGxD"}
|