@kevinrabun/judges 3.98.0 → 3.100.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-auto-suppress.d.ts +5 -0
- package/dist/commands/finding-auto-suppress.d.ts.map +1 -0
- package/dist/commands/finding-auto-suppress.js +127 -0
- package/dist/commands/finding-auto-suppress.js.map +1 -0
- package/dist/commands/finding-auto-triage.d.ts +5 -0
- package/dist/commands/finding-auto-triage.d.ts.map +1 -0
- package/dist/commands/finding-auto-triage.js +109 -0
- package/dist/commands/finding-auto-triage.js.map +1 -0
- package/dist/commands/finding-change-impact.d.ts +5 -0
- package/dist/commands/finding-change-impact.d.ts.map +1 -0
- package/dist/commands/finding-change-impact.js +108 -0
- package/dist/commands/finding-change-impact.js.map +1 -0
- package/dist/commands/finding-context-enrich.d.ts +5 -0
- package/dist/commands/finding-context-enrich.d.ts.map +1 -0
- package/dist/commands/finding-context-enrich.js +90 -0
- package/dist/commands/finding-context-enrich.js.map +1 -0
- package/dist/commands/finding-dismiss-workflow.d.ts +5 -0
- package/dist/commands/finding-dismiss-workflow.d.ts.map +1 -0
- package/dist/commands/finding-dismiss-workflow.js +120 -0
- package/dist/commands/finding-dismiss-workflow.js.map +1 -0
- package/dist/commands/finding-false-positive-learn.d.ts +5 -0
- package/dist/commands/finding-false-positive-learn.d.ts.map +1 -0
- package/dist/commands/finding-false-positive-learn.js +86 -0
- package/dist/commands/finding-false-positive-learn.js.map +1 -0
- package/dist/commands/finding-reachability-check.d.ts +5 -0
- package/dist/commands/finding-reachability-check.d.ts.map +1 -0
- package/dist/commands/finding-reachability-check.js +103 -0
- package/dist/commands/finding-reachability-check.js.map +1 -0
- package/dist/commands/review-audit-export.d.ts +5 -0
- package/dist/commands/review-audit-export.d.ts.map +1 -0
- package/dist/commands/review-audit-export.js +94 -0
- package/dist/commands/review-audit-export.js.map +1 -0
- package/dist/commands/review-data-retention.d.ts +5 -0
- package/dist/commands/review-data-retention.d.ts.map +1 -0
- package/dist/commands/review-data-retention.js +120 -0
- package/dist/commands/review-data-retention.js.map +1 -0
- package/dist/commands/review-deployment-gate.d.ts +5 -0
- package/dist/commands/review-deployment-gate.d.ts.map +1 -0
- package/dist/commands/review-deployment-gate.js +95 -0
- package/dist/commands/review-deployment-gate.js.map +1 -0
- package/dist/commands/review-environment-config.d.ts +5 -0
- package/dist/commands/review-environment-config.d.ts.map +1 -0
- package/dist/commands/review-environment-config.js +103 -0
- package/dist/commands/review-environment-config.js.map +1 -0
- package/dist/commands/review-multi-repo-sync.d.ts +5 -0
- package/dist/commands/review-multi-repo-sync.d.ts.map +1 -0
- package/dist/commands/review-multi-repo-sync.js +116 -0
- package/dist/commands/review-multi-repo-sync.js.map +1 -0
- package/dist/commands/review-permission-model.d.ts +5 -0
- package/dist/commands/review-permission-model.d.ts.map +1 -0
- package/dist/commands/review-permission-model.js +150 -0
- package/dist/commands/review-permission-model.js.map +1 -0
- package/dist/commands/review-pipeline-status.d.ts +5 -0
- package/dist/commands/review-pipeline-status.d.ts.map +1 -0
- package/dist/commands/review-pipeline-status.js +55 -0
- package/dist/commands/review-pipeline-status.js.map +1 -0
- package/dist/commands/review-repo-onboard.d.ts +5 -0
- package/dist/commands/review-repo-onboard.d.ts.map +1 -0
- package/dist/commands/review-repo-onboard.js +115 -0
- package/dist/commands/review-repo-onboard.js.map +1 -0
- package/dist/commands/review-review-comments.d.ts +5 -0
- package/dist/commands/review-review-comments.d.ts.map +1 -0
- package/dist/commands/review-review-comments.js +85 -0
- package/dist/commands/review-review-comments.js.map +1 -0
- package/dist/commands/review-session-replay.d.ts +5 -0
- package/dist/commands/review-session-replay.d.ts.map +1 -0
- package/dist/commands/review-session-replay.js +82 -0
- package/dist/commands/review-session-replay.js.map +1 -0
- package/dist/commands/review-stakeholder-report.d.ts +5 -0
- package/dist/commands/review-stakeholder-report.d.ts.map +1 -0
- package/dist/commands/review-stakeholder-report.js +76 -0
- package/dist/commands/review-stakeholder-report.js.map +1 -0
- package/package.json +1 -1
- package/server.json +2 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finding-auto-suppress.d.ts","sourceRoot":"","sources":["../../src/commands/finding-auto-suppress.ts"],"names":[],"mappings":"AAAA;;GAEG;AAsBH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAqI3D"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Finding-auto-suppress — Automatically suppress findings matching criteria.
|
|
3
|
+
*/
|
|
4
|
+
import { readFileSync, writeFileSync, existsSync } from "fs";
|
|
5
|
+
// ─── CLI ────────────────────────────────────────────────────────────────────
|
|
6
|
+
export function runFindingAutoSuppress(argv) {
|
|
7
|
+
const storeIdx = argv.indexOf("--store");
|
|
8
|
+
const storePath = storeIdx >= 0 ? argv[storeIdx + 1] : ".judges-suppressions.json";
|
|
9
|
+
const formatIdx = argv.indexOf("--format");
|
|
10
|
+
const format = formatIdx >= 0 ? argv[formatIdx + 1] : "table";
|
|
11
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
12
|
+
console.log(`
|
|
13
|
+
judges finding-auto-suppress — Automatically suppress findings
|
|
14
|
+
|
|
15
|
+
Usage:
|
|
16
|
+
judges finding-auto-suppress [--store <path>]
|
|
17
|
+
[--add-rule <id> --field <f> --pattern <p> --reason <reason>]
|
|
18
|
+
[--remove-rule <id>] [--apply <report> --out <path>]
|
|
19
|
+
[--format table|json]
|
|
20
|
+
|
|
21
|
+
Options:
|
|
22
|
+
--store <path> Suppression store (default: .judges-suppressions.json)
|
|
23
|
+
--add-rule <id> Add suppression rule
|
|
24
|
+
--field <f> Match field: ruleId, severity, title
|
|
25
|
+
--pattern <p> Match pattern (substring)
|
|
26
|
+
--reason <reason> Suppression reason
|
|
27
|
+
--remove-rule <id> Remove rule by id
|
|
28
|
+
--apply <report> Apply suppressions to report, output filtered findings
|
|
29
|
+
--out <path> Write filtered report to file
|
|
30
|
+
--format <fmt> Output format: table (default), json
|
|
31
|
+
--help, -h Show this help
|
|
32
|
+
`);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
let store;
|
|
36
|
+
if (existsSync(storePath)) {
|
|
37
|
+
store = JSON.parse(readFileSync(storePath, "utf-8"));
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
store = { rules: [], lastUpdated: new Date().toISOString().split("T")[0] };
|
|
41
|
+
}
|
|
42
|
+
// Add rule
|
|
43
|
+
const addIdx = argv.indexOf("--add-rule");
|
|
44
|
+
if (addIdx >= 0) {
|
|
45
|
+
const id = argv[addIdx + 1];
|
|
46
|
+
const fieldIdx = argv.indexOf("--field");
|
|
47
|
+
const patternIdx = argv.indexOf("--pattern");
|
|
48
|
+
const reasonIdx = argv.indexOf("--reason");
|
|
49
|
+
const rule = {
|
|
50
|
+
id,
|
|
51
|
+
field: (fieldIdx >= 0 ? argv[fieldIdx + 1] : "ruleId"),
|
|
52
|
+
pattern: patternIdx >= 0 ? argv[patternIdx + 1] : "",
|
|
53
|
+
reason: reasonIdx >= 0 ? argv[reasonIdx + 1] : "",
|
|
54
|
+
createdAt: new Date().toISOString().split("T")[0],
|
|
55
|
+
};
|
|
56
|
+
const existingIdx = store.rules.findIndex((r) => r.id === id);
|
|
57
|
+
if (existingIdx >= 0) {
|
|
58
|
+
store.rules[existingIdx] = rule;
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
store.rules.push(rule);
|
|
62
|
+
}
|
|
63
|
+
store.lastUpdated = new Date().toISOString().split("T")[0];
|
|
64
|
+
writeFileSync(storePath, JSON.stringify(store, null, 2));
|
|
65
|
+
console.log(`Suppression rule "${id}" saved.`);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
// Remove rule
|
|
69
|
+
const removeIdx = argv.indexOf("--remove-rule");
|
|
70
|
+
if (removeIdx >= 0) {
|
|
71
|
+
const id = argv[removeIdx + 1];
|
|
72
|
+
store.rules = store.rules.filter((r) => r.id !== id);
|
|
73
|
+
store.lastUpdated = new Date().toISOString().split("T")[0];
|
|
74
|
+
writeFileSync(storePath, JSON.stringify(store, null, 2));
|
|
75
|
+
console.log(`Rule "${id}" removed.`);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
// Apply suppressions to report
|
|
79
|
+
const applyIdx = argv.indexOf("--apply");
|
|
80
|
+
if (applyIdx >= 0) {
|
|
81
|
+
const reportPath = argv[applyIdx + 1];
|
|
82
|
+
if (!existsSync(reportPath)) {
|
|
83
|
+
console.error(`Report not found: ${reportPath}`);
|
|
84
|
+
process.exitCode = 1;
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const report = JSON.parse(readFileSync(reportPath, "utf-8"));
|
|
88
|
+
const findings = report.findings ?? [];
|
|
89
|
+
let suppressed = 0;
|
|
90
|
+
const filtered = findings.filter((f) => {
|
|
91
|
+
for (const rule of store.rules) {
|
|
92
|
+
const fieldValue = String(f[rule.field] ?? "");
|
|
93
|
+
if (fieldValue.includes(rule.pattern)) {
|
|
94
|
+
suppressed++;
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return true;
|
|
99
|
+
});
|
|
100
|
+
const outIdx = argv.indexOf("--out");
|
|
101
|
+
if (outIdx >= 0) {
|
|
102
|
+
const outPath = argv[outIdx + 1];
|
|
103
|
+
writeFileSync(outPath, JSON.stringify({ ...report, findings: filtered }, null, 2));
|
|
104
|
+
console.log(`Filtered report written to: ${outPath}`);
|
|
105
|
+
}
|
|
106
|
+
console.log(`Suppressed: ${suppressed} | Remaining: ${filtered.length} | Total: ${findings.length}`);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
// List rules
|
|
110
|
+
if (format === "json") {
|
|
111
|
+
console.log(JSON.stringify(store, null, 2));
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
console.log(`\nSuppression Rules`);
|
|
115
|
+
console.log("═".repeat(65));
|
|
116
|
+
if (store.rules.length === 0) {
|
|
117
|
+
console.log(" No suppression rules. Use --add-rule to create one.");
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
for (const r of store.rules) {
|
|
121
|
+
console.log(` ${r.id.padEnd(20)} ${r.field}:${r.pattern}`);
|
|
122
|
+
console.log(` Reason: ${r.reason} (${r.createdAt})`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
console.log("═".repeat(65));
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=finding-auto-suppress.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finding-auto-suppress.js","sourceRoot":"","sources":["../../src/commands/finding-auto-suppress.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finding-auto-triage.d.ts","sourceRoot":"","sources":["../../src/commands/finding-auto-triage.ts"],"names":[],"mappings":"AAAA;;GAEG;AA+DH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CA6EzD"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Finding-auto-triage — Automatically triage findings by severity, confidence, and context.
|
|
3
|
+
*/
|
|
4
|
+
import { readFileSync, writeFileSync, existsSync } from "fs";
|
|
5
|
+
// ─── Helpers ────────────────────────────────────────────────────────────────
|
|
6
|
+
function matchesRule(finding, rule) {
|
|
7
|
+
if (rule.field === "severity") {
|
|
8
|
+
if (rule.operator === "eq")
|
|
9
|
+
return finding.severity === rule.value;
|
|
10
|
+
}
|
|
11
|
+
if (rule.field === "ruleId") {
|
|
12
|
+
if (rule.operator === "eq")
|
|
13
|
+
return finding.ruleId === rule.value;
|
|
14
|
+
if (rule.operator === "contains")
|
|
15
|
+
return finding.ruleId.includes(String(rule.value));
|
|
16
|
+
}
|
|
17
|
+
if (rule.field === "confidence") {
|
|
18
|
+
const conf = finding.confidence ?? 0;
|
|
19
|
+
if (rule.operator === "gte")
|
|
20
|
+
return conf >= Number(rule.value);
|
|
21
|
+
if (rule.operator === "lte")
|
|
22
|
+
return conf <= Number(rule.value);
|
|
23
|
+
}
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
function triageFindings(findings, config) {
|
|
27
|
+
const sorted = [...config.rules].sort((a, b) => b.priority - a.priority);
|
|
28
|
+
return findings.map((f) => {
|
|
29
|
+
const matched = sorted.find((r) => matchesRule(f, r));
|
|
30
|
+
return {
|
|
31
|
+
ruleId: f.ruleId,
|
|
32
|
+
title: f.title,
|
|
33
|
+
severity: f.severity,
|
|
34
|
+
action: matched ? matched.action : config.defaultAction,
|
|
35
|
+
matchedRule: matched ? `${matched.field} ${matched.operator} ${matched.value}` : "default",
|
|
36
|
+
};
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
// ─── CLI ────────────────────────────────────────────────────────────────────
|
|
40
|
+
export function runFindingAutoTriage(argv) {
|
|
41
|
+
const configIdx = argv.indexOf("--config");
|
|
42
|
+
const configPath = configIdx >= 0 ? argv[configIdx + 1] : ".judges-triage.json";
|
|
43
|
+
const findingsIdx = argv.indexOf("--findings");
|
|
44
|
+
const findingsPath = findingsIdx >= 0 ? argv[findingsIdx + 1] : "";
|
|
45
|
+
const formatIdx = argv.indexOf("--format");
|
|
46
|
+
const format = formatIdx >= 0 ? argv[formatIdx + 1] : "table";
|
|
47
|
+
const initMode = argv.includes("--init");
|
|
48
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
49
|
+
console.log(`
|
|
50
|
+
judges finding-auto-triage — Automatically triage findings
|
|
51
|
+
|
|
52
|
+
Usage:
|
|
53
|
+
judges finding-auto-triage --findings <path> [--config <path>] [--format table|json]
|
|
54
|
+
judges finding-auto-triage --init [--config <path>]
|
|
55
|
+
|
|
56
|
+
Options:
|
|
57
|
+
--findings <path> Path to findings JSON file
|
|
58
|
+
--config <path> Triage rules config (default: .judges-triage.json)
|
|
59
|
+
--init Create default triage config
|
|
60
|
+
--format <fmt> Output format: table (default), json
|
|
61
|
+
--help, -h Show this help
|
|
62
|
+
`);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
if (initMode) {
|
|
66
|
+
const defaultConfig = {
|
|
67
|
+
rules: [
|
|
68
|
+
{ field: "severity", operator: "eq", value: "critical", action: "accept", priority: 100 },
|
|
69
|
+
{ field: "severity", operator: "eq", value: "high", action: "accept", priority: 90 },
|
|
70
|
+
{ field: "confidence", operator: "lte", value: 0.3, action: "ignore", priority: 80 },
|
|
71
|
+
],
|
|
72
|
+
defaultAction: "defer",
|
|
73
|
+
lastUpdated: new Date().toISOString(),
|
|
74
|
+
};
|
|
75
|
+
writeFileSync(configPath, JSON.stringify(defaultConfig, null, 2));
|
|
76
|
+
console.log(`Created default triage config: ${configPath}`);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
if (!findingsPath || !existsSync(findingsPath)) {
|
|
80
|
+
console.error("Provide --findings <path> to a valid findings JSON file.");
|
|
81
|
+
process.exitCode = 1;
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
if (!existsSync(configPath)) {
|
|
85
|
+
console.error(`Triage config not found: ${configPath}. Run with --init to create one.`);
|
|
86
|
+
process.exitCode = 1;
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const findings = JSON.parse(readFileSync(findingsPath, "utf-8"));
|
|
90
|
+
const config = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
91
|
+
const results = triageFindings(findings, config);
|
|
92
|
+
if (format === "json") {
|
|
93
|
+
console.log(JSON.stringify(results, null, 2));
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
console.log(`\nAuto-Triage Results (${results.length} findings)`);
|
|
97
|
+
console.log("═".repeat(80));
|
|
98
|
+
console.log(` ${"Rule ID".padEnd(25)} ${"Severity".padEnd(12)} ${"Action".padEnd(10)} Matched Rule`);
|
|
99
|
+
console.log(" " + "─".repeat(75));
|
|
100
|
+
for (const r of results) {
|
|
101
|
+
console.log(` ${r.ruleId.padEnd(25)} ${r.severity.padEnd(12)} ${r.action.padEnd(10)} ${r.matchedRule}`);
|
|
102
|
+
}
|
|
103
|
+
const accepted = results.filter((r) => r.action === "accept").length;
|
|
104
|
+
const deferred = results.filter((r) => r.action === "defer").length;
|
|
105
|
+
const ignored = results.filter((r) => r.action === "ignore").length;
|
|
106
|
+
console.log(`\n Accept: ${accepted} | Defer: ${deferred} | Ignore: ${ignored}`);
|
|
107
|
+
console.log("═".repeat(80));
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=finding-auto-triage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finding-auto-triage.js","sourceRoot":"","sources":["../../src/commands/finding-auto-triage.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finding-change-impact.d.ts","sourceRoot":"","sources":["../../src/commands/finding-change-impact.ts"],"names":[],"mappings":"AAAA;;GAEG;AA4EH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CA4D3D"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Finding-change-impact — Assess the impact of code changes on existing findings.
|
|
3
|
+
*/
|
|
4
|
+
import { readFileSync, existsSync } from "fs";
|
|
5
|
+
// ─── Helpers ────────────────────────────────────────────────────────────────
|
|
6
|
+
function assessImpact(findings, changes) {
|
|
7
|
+
const results = [];
|
|
8
|
+
const changedLineSets = new Map();
|
|
9
|
+
for (const ch of changes) {
|
|
10
|
+
const s = new Set([...ch.linesAdded, ...ch.linesRemoved]);
|
|
11
|
+
changedLineSets.set(ch.path, s);
|
|
12
|
+
}
|
|
13
|
+
for (const f of findings) {
|
|
14
|
+
const fLines = f.lineNumbers ?? [];
|
|
15
|
+
let bestImpact = "none";
|
|
16
|
+
const affected = [];
|
|
17
|
+
let matchedPath = "";
|
|
18
|
+
for (const ch of changes) {
|
|
19
|
+
const changedLines = changedLineSets.get(ch.path);
|
|
20
|
+
if (!changedLines)
|
|
21
|
+
continue;
|
|
22
|
+
const overlap = fLines.filter((ln) => changedLines.has(ln));
|
|
23
|
+
if (overlap.length > 0) {
|
|
24
|
+
bestImpact = "direct";
|
|
25
|
+
affected.push(...overlap);
|
|
26
|
+
matchedPath = ch.path;
|
|
27
|
+
}
|
|
28
|
+
else if (fLines.length > 0 && bestImpact === "none") {
|
|
29
|
+
const nearThreshold = 5;
|
|
30
|
+
const nearby = fLines.some((ln) => {
|
|
31
|
+
for (const cl of changedLines) {
|
|
32
|
+
if (Math.abs(ln - cl) <= nearThreshold)
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
return false;
|
|
36
|
+
});
|
|
37
|
+
if (nearby) {
|
|
38
|
+
bestImpact = "indirect";
|
|
39
|
+
matchedPath = ch.path;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
results.push({
|
|
44
|
+
ruleId: f.ruleId,
|
|
45
|
+
title: f.title,
|
|
46
|
+
severity: f.severity,
|
|
47
|
+
impactLevel: bestImpact,
|
|
48
|
+
affectedLines: affected,
|
|
49
|
+
filePath: matchedPath,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
return results;
|
|
53
|
+
}
|
|
54
|
+
// ─── CLI ────────────────────────────────────────────────────────────────────
|
|
55
|
+
export function runFindingChangeImpact(argv) {
|
|
56
|
+
const findingsIdx = argv.indexOf("--findings");
|
|
57
|
+
const findingsPath = findingsIdx >= 0 ? argv[findingsIdx + 1] : "";
|
|
58
|
+
const changesIdx = argv.indexOf("--changes");
|
|
59
|
+
const changesPath = changesIdx >= 0 ? argv[changesIdx + 1] : "";
|
|
60
|
+
const formatIdx = argv.indexOf("--format");
|
|
61
|
+
const format = formatIdx >= 0 ? argv[formatIdx + 1] : "table";
|
|
62
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
63
|
+
console.log(`
|
|
64
|
+
judges finding-change-impact — Assess impact of code changes on findings
|
|
65
|
+
|
|
66
|
+
Usage:
|
|
67
|
+
judges finding-change-impact --findings <path> --changes <path> [--format table|json]
|
|
68
|
+
|
|
69
|
+
Options:
|
|
70
|
+
--findings <path> Path to findings JSON file
|
|
71
|
+
--changes <path> Path to changes JSON file (array of {path, linesAdded, linesRemoved})
|
|
72
|
+
--format <fmt> Output format: table (default), json
|
|
73
|
+
--help, -h Show this help
|
|
74
|
+
`);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
if (!findingsPath || !existsSync(findingsPath)) {
|
|
78
|
+
console.error("Provide --findings <path> to a valid findings JSON file.");
|
|
79
|
+
process.exitCode = 1;
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
if (!changesPath || !existsSync(changesPath)) {
|
|
83
|
+
console.error("Provide --changes <path> to a valid changes JSON file.");
|
|
84
|
+
process.exitCode = 1;
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const findings = JSON.parse(readFileSync(findingsPath, "utf-8"));
|
|
88
|
+
const changes = JSON.parse(readFileSync(changesPath, "utf-8"));
|
|
89
|
+
const results = assessImpact(findings, changes);
|
|
90
|
+
if (format === "json") {
|
|
91
|
+
console.log(JSON.stringify(results, null, 2));
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
console.log(`\nChange Impact Analysis (${results.length} findings)`);
|
|
95
|
+
console.log("═".repeat(80));
|
|
96
|
+
console.log(` ${"Rule ID".padEnd(25)} ${"Severity".padEnd(12)} ${"Impact".padEnd(12)} Affected Lines`);
|
|
97
|
+
console.log(" " + "─".repeat(75));
|
|
98
|
+
for (const r of results) {
|
|
99
|
+
const lines = r.affectedLines.length > 0 ? r.affectedLines.join(", ") : "—";
|
|
100
|
+
console.log(` ${r.ruleId.padEnd(25)} ${r.severity.padEnd(12)} ${r.impactLevel.padEnd(12)} ${lines}`);
|
|
101
|
+
}
|
|
102
|
+
const direct = results.filter((r) => r.impactLevel === "direct").length;
|
|
103
|
+
const indirect = results.filter((r) => r.impactLevel === "indirect").length;
|
|
104
|
+
const none = results.filter((r) => r.impactLevel === "none").length;
|
|
105
|
+
console.log(`\n Direct: ${direct} | Indirect: ${indirect} | None: ${none}`);
|
|
106
|
+
console.log("═".repeat(80));
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=finding-change-impact.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finding-change-impact.js","sourceRoot":"","sources":["../../src/commands/finding-change-impact.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finding-context-enrich.d.ts","sourceRoot":"","sources":["../../src/commands/finding-context-enrich.ts"],"names":[],"mappings":"AAAA;;GAEG;AA4CH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CA2E5D"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Finding-context-enrich — Enrich findings with surrounding code context and metadata.
|
|
3
|
+
*/
|
|
4
|
+
import { readFileSync, existsSync } from "fs";
|
|
5
|
+
// ─── Helpers ────────────────────────────────────────────────────────────────
|
|
6
|
+
function extractContext(filePath, lineNumbers, contextSize) {
|
|
7
|
+
if (!existsSync(filePath)) {
|
|
8
|
+
return { snippet: [], start: 0, end: 0 };
|
|
9
|
+
}
|
|
10
|
+
const lines = readFileSync(filePath, "utf-8").split("\n");
|
|
11
|
+
if (lineNumbers.length === 0) {
|
|
12
|
+
return { snippet: [], start: 0, end: 0 };
|
|
13
|
+
}
|
|
14
|
+
const minLine = Math.max(0, Math.min(...lineNumbers) - 1 - contextSize);
|
|
15
|
+
const maxLine = Math.min(lines.length, Math.max(...lineNumbers) + contextSize);
|
|
16
|
+
return {
|
|
17
|
+
snippet: lines.slice(minLine, maxLine),
|
|
18
|
+
start: minLine + 1,
|
|
19
|
+
end: maxLine,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
// ─── CLI ────────────────────────────────────────────────────────────────────
|
|
23
|
+
export function runFindingContextEnrich(argv) {
|
|
24
|
+
const findingsIdx = argv.indexOf("--findings");
|
|
25
|
+
const findingsPath = findingsIdx >= 0 ? argv[findingsIdx + 1] : "";
|
|
26
|
+
const fileIdx = argv.indexOf("--file");
|
|
27
|
+
const filePath = fileIdx >= 0 ? argv[fileIdx + 1] : "";
|
|
28
|
+
const contextIdx = argv.indexOf("--context");
|
|
29
|
+
const contextSize = contextIdx >= 0 ? parseInt(argv[contextIdx + 1], 10) : 3;
|
|
30
|
+
const formatIdx = argv.indexOf("--format");
|
|
31
|
+
const format = formatIdx >= 0 ? argv[formatIdx + 1] : "table";
|
|
32
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
33
|
+
console.log(`
|
|
34
|
+
judges finding-context-enrich — Enrich findings with code context
|
|
35
|
+
|
|
36
|
+
Usage:
|
|
37
|
+
judges finding-context-enrich --findings <path> --file <path> [--context <n>] [--format table|json]
|
|
38
|
+
|
|
39
|
+
Options:
|
|
40
|
+
--findings <path> Path to findings JSON file
|
|
41
|
+
--file <path> Source file to extract context from
|
|
42
|
+
--context <n> Lines of context around findings (default: 3)
|
|
43
|
+
--format <fmt> Output format: table (default), json
|
|
44
|
+
--help, -h Show this help
|
|
45
|
+
`);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (!findingsPath || !existsSync(findingsPath)) {
|
|
49
|
+
console.error("Provide --findings <path> to a valid findings JSON file.");
|
|
50
|
+
process.exitCode = 1;
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const findings = JSON.parse(readFileSync(findingsPath, "utf-8"));
|
|
54
|
+
const enriched = [];
|
|
55
|
+
for (const f of findings) {
|
|
56
|
+
const lineNumbers = f.lineNumbers ?? [];
|
|
57
|
+
const ctx = filePath ? extractContext(filePath, lineNumbers, contextSize) : { snippet: [], start: 0, end: 0 };
|
|
58
|
+
enriched.push({
|
|
59
|
+
ruleId: f.ruleId,
|
|
60
|
+
title: f.title,
|
|
61
|
+
severity: f.severity,
|
|
62
|
+
description: f.description,
|
|
63
|
+
recommendation: f.recommendation,
|
|
64
|
+
codeSnippet: ctx.snippet,
|
|
65
|
+
lineStart: ctx.start,
|
|
66
|
+
lineEnd: ctx.end,
|
|
67
|
+
contextLines: contextSize,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
if (format === "json") {
|
|
71
|
+
console.log(JSON.stringify(enriched, null, 2));
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
console.log(`\nEnriched Findings (${enriched.length})`);
|
|
75
|
+
console.log("═".repeat(80));
|
|
76
|
+
for (const e of enriched) {
|
|
77
|
+
console.log(`\n [${e.severity.toUpperCase()}] ${e.ruleId}: ${e.title}`);
|
|
78
|
+
console.log(` ${e.description}`);
|
|
79
|
+
console.log(` Recommendation: ${e.recommendation}`);
|
|
80
|
+
if (e.codeSnippet.length > 0) {
|
|
81
|
+
console.log(` Code (lines ${e.lineStart}–${e.lineEnd}):`);
|
|
82
|
+
for (let i = 0; i < e.codeSnippet.length; i++) {
|
|
83
|
+
console.log(` ${String(e.lineStart + i).padStart(4)} | ${e.codeSnippet[i]}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
console.log(" " + "─".repeat(75));
|
|
87
|
+
}
|
|
88
|
+
console.log("═".repeat(80));
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=finding-context-enrich.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finding-context-enrich.js","sourceRoot":"","sources":["../../src/commands/finding-context-enrich.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finding-dismiss-workflow.d.ts","sourceRoot":"","sources":["../../src/commands/finding-dismiss-workflow.ts"],"names":[],"mappings":"AAAA;;GAEG;AAsBH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CA8H9D"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Finding-dismiss-workflow — Manage finding dismissal workflows.
|
|
3
|
+
*/
|
|
4
|
+
import { readFileSync, writeFileSync, existsSync } from "fs";
|
|
5
|
+
// ─── CLI ────────────────────────────────────────────────────────────────────
|
|
6
|
+
export function runFindingDismissWorkflow(argv) {
|
|
7
|
+
const storeIdx = argv.indexOf("--store");
|
|
8
|
+
const storePath = storeIdx >= 0 ? argv[storeIdx + 1] : ".judges-dismissals.json";
|
|
9
|
+
const formatIdx = argv.indexOf("--format");
|
|
10
|
+
const format = formatIdx >= 0 ? argv[formatIdx + 1] : "table";
|
|
11
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
12
|
+
console.log(`
|
|
13
|
+
judges finding-dismiss-workflow — Manage finding dismissals
|
|
14
|
+
|
|
15
|
+
Usage:
|
|
16
|
+
judges finding-dismiss-workflow [--store <path>]
|
|
17
|
+
[--dismiss <ruleId> --reason <r> --by <name> --note <text> --expires <date>]
|
|
18
|
+
[--revoke <ruleId>] [--audit] [--format table|json]
|
|
19
|
+
|
|
20
|
+
Options:
|
|
21
|
+
--store <path> Dismissal store (default: .judges-dismissals.json)
|
|
22
|
+
--dismiss <ruleId> Dismiss a finding
|
|
23
|
+
--reason <r> Reason: false-positive, accepted-risk, wont-fix, duplicate
|
|
24
|
+
--by <name> Who dismissed it
|
|
25
|
+
--note <text> Additional note
|
|
26
|
+
--expires <date> Expiry date (YYYY-MM-DD)
|
|
27
|
+
--revoke <ruleId> Revoke a dismissal
|
|
28
|
+
--audit Show audit log of all dismissals
|
|
29
|
+
--format <fmt> Output format: table (default), json
|
|
30
|
+
--help, -h Show this help
|
|
31
|
+
`);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
let store;
|
|
35
|
+
if (existsSync(storePath)) {
|
|
36
|
+
store = JSON.parse(readFileSync(storePath, "utf-8"));
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
store = { dismissals: [], lastUpdated: new Date().toISOString().split("T")[0] };
|
|
40
|
+
}
|
|
41
|
+
// Dismiss
|
|
42
|
+
const dismissIdx = argv.indexOf("--dismiss");
|
|
43
|
+
if (dismissIdx >= 0) {
|
|
44
|
+
const ruleId = argv[dismissIdx + 1];
|
|
45
|
+
const reasonIdx = argv.indexOf("--reason");
|
|
46
|
+
const byIdx = argv.indexOf("--by");
|
|
47
|
+
const noteIdx = argv.indexOf("--note");
|
|
48
|
+
const expiresIdx = argv.indexOf("--expires");
|
|
49
|
+
const dismissal = {
|
|
50
|
+
ruleId,
|
|
51
|
+
reason: (reasonIdx >= 0 ? argv[reasonIdx + 1] : "accepted-risk"),
|
|
52
|
+
dismissedBy: byIdx >= 0 ? argv[byIdx + 1] : "unknown",
|
|
53
|
+
dismissedAt: new Date().toISOString().split("T")[0],
|
|
54
|
+
expiresAt: expiresIdx >= 0 ? argv[expiresIdx + 1] : "9999-12-31",
|
|
55
|
+
note: noteIdx >= 0 ? argv[noteIdx + 1] : "",
|
|
56
|
+
};
|
|
57
|
+
store.dismissals.push(dismissal);
|
|
58
|
+
store.lastUpdated = new Date().toISOString().split("T")[0];
|
|
59
|
+
writeFileSync(storePath, JSON.stringify(store, null, 2));
|
|
60
|
+
console.log(`Dismissed: ${ruleId} (${dismissal.reason})`);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
// Revoke
|
|
64
|
+
const revokeIdx = argv.indexOf("--revoke");
|
|
65
|
+
if (revokeIdx >= 0) {
|
|
66
|
+
const ruleId = argv[revokeIdx + 1];
|
|
67
|
+
const before = store.dismissals.length;
|
|
68
|
+
store.dismissals = store.dismissals.filter((d) => d.ruleId !== ruleId);
|
|
69
|
+
store.lastUpdated = new Date().toISOString().split("T")[0];
|
|
70
|
+
writeFileSync(storePath, JSON.stringify(store, null, 2));
|
|
71
|
+
console.log(`Revoked ${before - store.dismissals.length} dismissal(s) for: ${ruleId}`);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
// Audit log
|
|
75
|
+
if (argv.includes("--audit")) {
|
|
76
|
+
const today = new Date().toISOString().split("T")[0];
|
|
77
|
+
const active = store.dismissals.filter((d) => d.expiresAt >= today);
|
|
78
|
+
const expired = store.dismissals.filter((d) => d.expiresAt < today);
|
|
79
|
+
if (format === "json") {
|
|
80
|
+
console.log(JSON.stringify({ active, expired }, null, 2));
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
console.log(`\nDismissal Audit`);
|
|
84
|
+
console.log("═".repeat(70));
|
|
85
|
+
if (active.length > 0) {
|
|
86
|
+
console.log(" Active:");
|
|
87
|
+
for (const d of active) {
|
|
88
|
+
console.log(` ${d.ruleId.padEnd(25)} [${d.reason}] by ${d.dismissedBy} (${d.dismissedAt})`);
|
|
89
|
+
if (d.note.length > 0)
|
|
90
|
+
console.log(` Note: ${d.note}`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (expired.length > 0) {
|
|
94
|
+
console.log(" Expired:");
|
|
95
|
+
for (const d of expired) {
|
|
96
|
+
console.log(` ${d.ruleId.padEnd(25)} [${d.reason}] expired ${d.expiresAt}`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
console.log(`\n Active: ${active.length} | Expired: ${expired.length}`);
|
|
100
|
+
console.log("═".repeat(70));
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
// List
|
|
104
|
+
if (format === "json") {
|
|
105
|
+
console.log(JSON.stringify(store, null, 2));
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
console.log(`\nDismissals`);
|
|
109
|
+
console.log("═".repeat(65));
|
|
110
|
+
if (store.dismissals.length === 0) {
|
|
111
|
+
console.log(" No dismissals. Use --dismiss <ruleId> to add one.");
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
for (const d of store.dismissals) {
|
|
115
|
+
console.log(` ${d.ruleId.padEnd(25)} [${d.reason}] by ${d.dismissedBy}`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
console.log("═".repeat(65));
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=finding-dismiss-workflow.js.map
|