@kevinrabun/judges 3.86.0 → 3.88.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 +32 -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-analysis.d.ts +5 -0
- package/dist/commands/finding-age-analysis.d.ts.map +1 -0
- package/dist/commands/finding-age-analysis.js +145 -0
- package/dist/commands/finding-age-analysis.js.map +1 -0
- package/dist/commands/finding-code-smell.d.ts +5 -0
- package/dist/commands/finding-code-smell.d.ts.map +1 -0
- package/dist/commands/finding-code-smell.js +114 -0
- package/dist/commands/finding-code-smell.js.map +1 -0
- package/dist/commands/finding-correlation.d.ts +5 -0
- package/dist/commands/finding-correlation.d.ts.map +1 -0
- package/dist/commands/finding-correlation.js +104 -0
- package/dist/commands/finding-correlation.js.map +1 -0
- package/dist/commands/finding-dependency-tree.d.ts +5 -0
- package/dist/commands/finding-dependency-tree.d.ts.map +1 -0
- package/dist/commands/finding-dependency-tree.js +117 -0
- package/dist/commands/finding-dependency-tree.js.map +1 -0
- package/dist/commands/finding-owner-assign.d.ts +5 -0
- package/dist/commands/finding-owner-assign.d.ts.map +1 -0
- package/dist/commands/finding-owner-assign.js +134 -0
- package/dist/commands/finding-owner-assign.js.map +1 -0
- package/dist/commands/finding-pattern-library.d.ts +5 -0
- package/dist/commands/finding-pattern-library.d.ts.map +1 -0
- package/dist/commands/finding-pattern-library.js +146 -0
- package/dist/commands/finding-pattern-library.js.map +1 -0
- package/dist/commands/finding-related-rules.d.ts +5 -0
- package/dist/commands/finding-related-rules.d.ts.map +1 -0
- package/dist/commands/finding-related-rules.js +152 -0
- package/dist/commands/finding-related-rules.js.map +1 -0
- package/dist/commands/finding-rule-explain.d.ts +5 -0
- package/dist/commands/finding-rule-explain.d.ts.map +1 -0
- package/dist/commands/finding-rule-explain.js +141 -0
- package/dist/commands/finding-rule-explain.js.map +1 -0
- package/dist/commands/finding-suppression-audit.d.ts +5 -0
- package/dist/commands/finding-suppression-audit.d.ts.map +1 -0
- package/dist/commands/finding-suppression-audit.js +138 -0
- package/dist/commands/finding-suppression-audit.js.map +1 -0
- package/dist/commands/review-ci-integration.d.ts +5 -0
- package/dist/commands/review-ci-integration.d.ts.map +1 -0
- package/dist/commands/review-ci-integration.js +126 -0
- package/dist/commands/review-ci-integration.js.map +1 -0
- package/dist/commands/review-comparative.d.ts +5 -0
- package/dist/commands/review-comparative.d.ts.map +1 -0
- package/dist/commands/review-comparative.js +150 -0
- package/dist/commands/review-comparative.js.map +1 -0
- package/dist/commands/review-custom-rule.d.ts +5 -0
- package/dist/commands/review-custom-rule.d.ts.map +1 -0
- package/dist/commands/review-custom-rule.js +170 -0
- package/dist/commands/review-custom-rule.js.map +1 -0
- package/dist/commands/review-lock-file.d.ts +5 -0
- package/dist/commands/review-lock-file.d.ts.map +1 -0
- package/dist/commands/review-lock-file.js +154 -0
- package/dist/commands/review-lock-file.js.map +1 -0
- package/dist/commands/review-notification.d.ts +5 -0
- package/dist/commands/review-notification.d.ts.map +1 -0
- package/dist/commands/review-notification.js +127 -0
- package/dist/commands/review-notification.js.map +1 -0
- package/dist/commands/review-plugin-list.d.ts +5 -0
- package/dist/commands/review-plugin-list.d.ts.map +1 -0
- package/dist/commands/review-plugin-list.js +100 -0
- package/dist/commands/review-plugin-list.js.map +1 -0
- package/dist/commands/review-status-badge.d.ts +5 -0
- package/dist/commands/review-status-badge.d.ts.map +1 -0
- package/dist/commands/review-status-badge.js +121 -0
- package/dist/commands/review-status-badge.js.map +1 -0
- package/dist/commands/review-template-export.d.ts +5 -0
- package/dist/commands/review-template-export.d.ts.map +1 -0
- package/dist/commands/review-template-export.js +147 -0
- package/dist/commands/review-template-export.js.map +1 -0
- package/dist/commands/review-token-budget.d.ts +5 -0
- package/dist/commands/review-token-budget.d.ts.map +1 -0
- package/dist/commands/review-token-budget.js +118 -0
- package/dist/commands/review-token-budget.js.map +1 -0
- package/package.json +1 -1
- package/server.json +2 -2
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Finding-owner-assign — Assign ownership of findings based on configurable rules.
|
|
3
|
+
*/
|
|
4
|
+
import { readFileSync, existsSync, writeFileSync } from "fs";
|
|
5
|
+
// ─── Helpers ────────────────────────────────────────────────────────────────
|
|
6
|
+
function loadOwnerRules(configPath) {
|
|
7
|
+
if (!existsSync(configPath))
|
|
8
|
+
return [];
|
|
9
|
+
try {
|
|
10
|
+
const data = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
11
|
+
if (Array.isArray(data.rules)) {
|
|
12
|
+
return data.rules;
|
|
13
|
+
}
|
|
14
|
+
return [];
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return [];
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function assignOwners(verdict, rules) {
|
|
21
|
+
const assignments = [];
|
|
22
|
+
for (const f of verdict.findings) {
|
|
23
|
+
let owner = "unassigned";
|
|
24
|
+
let matchedPattern = "";
|
|
25
|
+
for (const rule of rules) {
|
|
26
|
+
const ruleIdLower = f.ruleId.toLowerCase();
|
|
27
|
+
const titleLower = f.title.toLowerCase();
|
|
28
|
+
const patLower = rule.pattern.toLowerCase();
|
|
29
|
+
if (ruleIdLower.includes(patLower) || titleLower.includes(patLower)) {
|
|
30
|
+
owner = rule.owner;
|
|
31
|
+
matchedPattern = rule.pattern;
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
// fallback: assign by severity
|
|
36
|
+
if (owner === "unassigned") {
|
|
37
|
+
const sev = (f.severity || "medium").toLowerCase();
|
|
38
|
+
if (sev === "critical" || sev === "high") {
|
|
39
|
+
owner = "security-team";
|
|
40
|
+
matchedPattern = `severity:${sev}`;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
assignments.push({
|
|
44
|
+
ruleId: f.ruleId,
|
|
45
|
+
title: f.title,
|
|
46
|
+
severity: (f.severity || "medium").toLowerCase(),
|
|
47
|
+
owner,
|
|
48
|
+
matchedPattern,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
return assignments;
|
|
52
|
+
}
|
|
53
|
+
// ─── CLI ────────────────────────────────────────────────────────────────────
|
|
54
|
+
export function runFindingOwnerAssign(argv) {
|
|
55
|
+
const fileIdx = argv.indexOf("--file");
|
|
56
|
+
const configIdx = argv.indexOf("--config");
|
|
57
|
+
const formatIdx = argv.indexOf("--format");
|
|
58
|
+
const outIdx = argv.indexOf("--output");
|
|
59
|
+
const filePath = fileIdx >= 0 ? argv[fileIdx + 1] : undefined;
|
|
60
|
+
const configPath = configIdx >= 0 ? argv[configIdx + 1] : ".judges-owners.json";
|
|
61
|
+
const format = formatIdx >= 0 ? argv[formatIdx + 1] : "table";
|
|
62
|
+
const outputPath = outIdx >= 0 ? argv[outIdx + 1] : undefined;
|
|
63
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
64
|
+
console.log(`
|
|
65
|
+
judges finding-owner-assign — Assign finding owners
|
|
66
|
+
|
|
67
|
+
Usage:
|
|
68
|
+
judges finding-owner-assign --file <verdict.json> [--config <owners.json>]
|
|
69
|
+
[--format table|json] [--output <file>]
|
|
70
|
+
|
|
71
|
+
Options:
|
|
72
|
+
--file <path> Path to verdict JSON file (required)
|
|
73
|
+
--config <path> Owner rules config (default: .judges-owners.json)
|
|
74
|
+
--format <fmt> Output format: table (default), json
|
|
75
|
+
--output <path> Write assignments to file
|
|
76
|
+
--help, -h Show this help
|
|
77
|
+
|
|
78
|
+
Config format:
|
|
79
|
+
{ "rules": [{ "pattern": "AUTH", "owner": "security-team" }] }
|
|
80
|
+
`);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
if (!filePath) {
|
|
84
|
+
console.error("Error: --file required");
|
|
85
|
+
process.exitCode = 1;
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (!existsSync(filePath)) {
|
|
89
|
+
console.error(`Error: not found: ${filePath}`);
|
|
90
|
+
process.exitCode = 1;
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
let verdict;
|
|
94
|
+
try {
|
|
95
|
+
verdict = JSON.parse(readFileSync(filePath, "utf-8"));
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
console.error("Error: invalid JSON");
|
|
99
|
+
process.exitCode = 1;
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const rules = loadOwnerRules(configPath);
|
|
103
|
+
const assignments = assignOwners(verdict, rules);
|
|
104
|
+
if (outputPath) {
|
|
105
|
+
writeFileSync(outputPath, JSON.stringify(assignments, null, 2));
|
|
106
|
+
console.log(`Wrote ${assignments.length} assignments to ${outputPath}`);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
if (format === "json") {
|
|
110
|
+
console.log(JSON.stringify(assignments, null, 2));
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
// group by owner
|
|
114
|
+
const byOwner = new Map();
|
|
115
|
+
for (const a of assignments) {
|
|
116
|
+
const arr = byOwner.get(a.owner) || [];
|
|
117
|
+
arr.push(a);
|
|
118
|
+
byOwner.set(a.owner, arr);
|
|
119
|
+
}
|
|
120
|
+
console.log(`\nFinding Owner Assignments (${assignments.length} findings)`);
|
|
121
|
+
console.log("═".repeat(75));
|
|
122
|
+
for (const [owner, items] of byOwner) {
|
|
123
|
+
console.log(`\n Owner: ${owner} (${items.length} findings)`);
|
|
124
|
+
console.log(" " + "─".repeat(70));
|
|
125
|
+
console.log(` ${"Rule".padEnd(20)} ${"Severity".padEnd(10)} Title`);
|
|
126
|
+
for (const a of items) {
|
|
127
|
+
const rule = a.ruleId.length > 18 ? a.ruleId.slice(0, 18) + "…" : a.ruleId;
|
|
128
|
+
const title = a.title.length > 35 ? a.title.slice(0, 35) + "…" : a.title;
|
|
129
|
+
console.log(` ${rule.padEnd(20)} ${a.severity.padEnd(10)} ${title}`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
console.log("\n" + "═".repeat(75));
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=finding-owner-assign.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finding-owner-assign.js","sourceRoot":"","sources":["../../src/commands/finding-owner-assign.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finding-pattern-library.d.ts","sourceRoot":"","sources":["../../src/commands/finding-pattern-library.ts"],"names":[],"mappings":"AAAA;;GAEG;AA0EH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CA6G7D"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Finding-pattern-library — Manage a local library of finding patterns for reuse.
|
|
3
|
+
*/
|
|
4
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
|
|
5
|
+
import { dirname } from "path";
|
|
6
|
+
// ─── Helpers ────────────────────────────────────────────────────────────────
|
|
7
|
+
function loadLibrary(libPath) {
|
|
8
|
+
if (!existsSync(libPath)) {
|
|
9
|
+
return { version: 1, patterns: [] };
|
|
10
|
+
}
|
|
11
|
+
try {
|
|
12
|
+
return JSON.parse(readFileSync(libPath, "utf-8"));
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return { version: 1, patterns: [] };
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function saveLibrary(libPath, lib) {
|
|
19
|
+
const dir = dirname(libPath);
|
|
20
|
+
if (!existsSync(dir)) {
|
|
21
|
+
mkdirSync(dir, { recursive: true });
|
|
22
|
+
}
|
|
23
|
+
writeFileSync(libPath, JSON.stringify(lib, null, 2));
|
|
24
|
+
}
|
|
25
|
+
function importPatterns(lib, verdict) {
|
|
26
|
+
const now = new Date().toISOString();
|
|
27
|
+
let added = 0;
|
|
28
|
+
for (const f of verdict.findings) {
|
|
29
|
+
const existing = lib.patterns.find((p) => p.ruleId === f.ruleId);
|
|
30
|
+
if (existing) {
|
|
31
|
+
existing.occurrences++;
|
|
32
|
+
existing.lastSeen = now;
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
lib.patterns.push({
|
|
36
|
+
ruleId: f.ruleId,
|
|
37
|
+
title: f.title,
|
|
38
|
+
severity: (f.severity || "medium").toLowerCase(),
|
|
39
|
+
description: f.description,
|
|
40
|
+
recommendation: f.recommendation,
|
|
41
|
+
occurrences: 1,
|
|
42
|
+
firstSeen: now,
|
|
43
|
+
lastSeen: now,
|
|
44
|
+
});
|
|
45
|
+
added++;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return added;
|
|
49
|
+
}
|
|
50
|
+
// ─── CLI ────────────────────────────────────────────────────────────────────
|
|
51
|
+
export function runFindingPatternLibrary(argv) {
|
|
52
|
+
const actionIdx = argv.indexOf("--action");
|
|
53
|
+
const fileIdx = argv.indexOf("--file");
|
|
54
|
+
const libIdx = argv.indexOf("--library");
|
|
55
|
+
const formatIdx = argv.indexOf("--format");
|
|
56
|
+
const searchIdx = argv.indexOf("--search");
|
|
57
|
+
const action = actionIdx >= 0 ? argv[actionIdx + 1] : "list";
|
|
58
|
+
const filePath = fileIdx >= 0 ? argv[fileIdx + 1] : undefined;
|
|
59
|
+
const libPath = libIdx >= 0 ? argv[libIdx + 1] : ".judges-patterns.json";
|
|
60
|
+
const format = formatIdx >= 0 ? argv[formatIdx + 1] : "table";
|
|
61
|
+
const searchTerm = searchIdx >= 0 ? argv[searchIdx + 1] : undefined;
|
|
62
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
63
|
+
console.log(`
|
|
64
|
+
judges finding-pattern-library — Manage finding pattern library
|
|
65
|
+
|
|
66
|
+
Usage:
|
|
67
|
+
judges finding-pattern-library --action <action> [options]
|
|
68
|
+
|
|
69
|
+
Actions:
|
|
70
|
+
list List patterns in library (default)
|
|
71
|
+
import Import patterns from verdict file
|
|
72
|
+
search Search patterns by keyword
|
|
73
|
+
|
|
74
|
+
Options:
|
|
75
|
+
--action <act> Action: list, import, search
|
|
76
|
+
--file <path> Verdict JSON file (required for import)
|
|
77
|
+
--library <path> Library file (default: .judges-patterns.json)
|
|
78
|
+
--search <term> Search term (for search action)
|
|
79
|
+
--format <fmt> Output format: table (default), json
|
|
80
|
+
--help, -h Show this help
|
|
81
|
+
`);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const lib = loadLibrary(libPath);
|
|
85
|
+
if (action === "import") {
|
|
86
|
+
if (!filePath) {
|
|
87
|
+
console.error("Error: --file required for import");
|
|
88
|
+
process.exitCode = 1;
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (!existsSync(filePath)) {
|
|
92
|
+
console.error(`Error: not found: ${filePath}`);
|
|
93
|
+
process.exitCode = 1;
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
let verdict;
|
|
97
|
+
try {
|
|
98
|
+
verdict = JSON.parse(readFileSync(filePath, "utf-8"));
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
console.error("Error: invalid JSON");
|
|
102
|
+
process.exitCode = 1;
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const added = importPatterns(lib, verdict);
|
|
106
|
+
saveLibrary(libPath, lib);
|
|
107
|
+
console.log(`Imported ${added} new patterns (${lib.patterns.length} total in library)`);
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
if (action === "search") {
|
|
111
|
+
const term = (searchTerm || "").toLowerCase();
|
|
112
|
+
const matches = lib.patterns.filter((p) => p.ruleId.toLowerCase().includes(term) ||
|
|
113
|
+
p.title.toLowerCase().includes(term) ||
|
|
114
|
+
p.description.toLowerCase().includes(term));
|
|
115
|
+
if (format === "json") {
|
|
116
|
+
console.log(JSON.stringify(matches, null, 2));
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
console.log(`\nPattern Search: "${searchTerm}" (${matches.length} matches)`);
|
|
120
|
+
console.log("═".repeat(70));
|
|
121
|
+
for (const p of matches) {
|
|
122
|
+
console.log(` ${p.ruleId.padEnd(20)} ${p.title}`);
|
|
123
|
+
console.log(` Severity: ${p.severity} | Occurrences: ${p.occurrences}`);
|
|
124
|
+
}
|
|
125
|
+
console.log("═".repeat(70));
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
// default: list
|
|
129
|
+
if (format === "json") {
|
|
130
|
+
console.log(JSON.stringify(lib, null, 2));
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
console.log(`\nPattern Library (${lib.patterns.length} patterns)`);
|
|
134
|
+
console.log("═".repeat(75));
|
|
135
|
+
console.log(`${"Rule".padEnd(20)} ${"Severity".padEnd(10)} ${"Seen".padEnd(6)} ${"Last Seen".padEnd(22)} Title`);
|
|
136
|
+
console.log("─".repeat(75));
|
|
137
|
+
const sorted = [...lib.patterns].sort((a, b) => b.occurrences - a.occurrences);
|
|
138
|
+
for (const p of sorted) {
|
|
139
|
+
const rule = p.ruleId.length > 18 ? p.ruleId.slice(0, 18) + "…" : p.ruleId;
|
|
140
|
+
const title = p.title.length > 20 ? p.title.slice(0, 20) + "…" : p.title;
|
|
141
|
+
const lastSeen = p.lastSeen.slice(0, 19);
|
|
142
|
+
console.log(`${rule.padEnd(20)} ${p.severity.padEnd(10)} ${String(p.occurrences).padEnd(6)} ${lastSeen.padEnd(22)} ${title}`);
|
|
143
|
+
}
|
|
144
|
+
console.log("═".repeat(75));
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=finding-pattern-library.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finding-pattern-library.js","sourceRoot":"","sources":["../../src/commands/finding-pattern-library.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finding-related-rules.d.ts","sourceRoot":"","sources":["../../src/commands/finding-related-rules.ts"],"names":[],"mappings":"AAAA;;GAEG;AAkHH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAoE3D"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Finding-related-rules — Find rules related to a given rule ID.
|
|
3
|
+
*/
|
|
4
|
+
import { readFileSync, existsSync } from "fs";
|
|
5
|
+
import { defaultRegistry } from "../judge-registry.js";
|
|
6
|
+
// ─── Helpers ────────────────────────────────────────────────────────────────
|
|
7
|
+
function tokenize(text) {
|
|
8
|
+
return new Set(text
|
|
9
|
+
.toLowerCase()
|
|
10
|
+
.replace(/[^a-z0-9\s]/g, " ")
|
|
11
|
+
.split(/\s+/)
|
|
12
|
+
.filter((w) => w.length > 2));
|
|
13
|
+
}
|
|
14
|
+
function titleSimilarity(a, b) {
|
|
15
|
+
const ta = tokenize(a);
|
|
16
|
+
const tb = tokenize(b);
|
|
17
|
+
if (ta.size === 0 || tb.size === 0)
|
|
18
|
+
return 0;
|
|
19
|
+
let overlap = 0;
|
|
20
|
+
for (const w of ta) {
|
|
21
|
+
if (tb.has(w))
|
|
22
|
+
overlap++;
|
|
23
|
+
}
|
|
24
|
+
return overlap / Math.max(ta.size, tb.size);
|
|
25
|
+
}
|
|
26
|
+
// ─── Analysis ───────────────────────────────────────────────────────────────
|
|
27
|
+
function findRelated(verdict, targetRule) {
|
|
28
|
+
const target = verdict.findings.find((f) => f.ruleId === targetRule);
|
|
29
|
+
if (!target)
|
|
30
|
+
return [];
|
|
31
|
+
const targetPrefix = targetRule.replace(/-\d+$/, "");
|
|
32
|
+
const judges = defaultRegistry.getJudges();
|
|
33
|
+
const targetJudge = judges.find((j) => targetRule.startsWith(j.rulePrefix));
|
|
34
|
+
const results = [];
|
|
35
|
+
const seen = new Set();
|
|
36
|
+
for (const f of verdict.findings) {
|
|
37
|
+
if (f.ruleId === targetRule || seen.has(f.ruleId))
|
|
38
|
+
continue;
|
|
39
|
+
seen.add(f.ruleId);
|
|
40
|
+
const prefix = f.ruleId.replace(/-\d+$/, "");
|
|
41
|
+
const fJudge = judges.find((j) => f.ruleId.startsWith(j.rulePrefix));
|
|
42
|
+
// same judge
|
|
43
|
+
if (targetJudge !== undefined && fJudge !== undefined && targetJudge.id === fJudge.id) {
|
|
44
|
+
results.push({
|
|
45
|
+
ruleId: f.ruleId,
|
|
46
|
+
title: f.title,
|
|
47
|
+
severity: (f.severity || "medium").toLowerCase(),
|
|
48
|
+
relatedness: "same-judge",
|
|
49
|
+
score: 0.8,
|
|
50
|
+
});
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
// same prefix
|
|
54
|
+
if (prefix === targetPrefix) {
|
|
55
|
+
results.push({
|
|
56
|
+
ruleId: f.ruleId,
|
|
57
|
+
title: f.title,
|
|
58
|
+
severity: (f.severity || "medium").toLowerCase(),
|
|
59
|
+
relatedness: "same-judge",
|
|
60
|
+
score: 0.9,
|
|
61
|
+
});
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
// title similarity
|
|
65
|
+
const sim = titleSimilarity(target.title, f.title);
|
|
66
|
+
if (sim >= 0.3) {
|
|
67
|
+
results.push({
|
|
68
|
+
ruleId: f.ruleId,
|
|
69
|
+
title: f.title,
|
|
70
|
+
severity: (f.severity || "medium").toLowerCase(),
|
|
71
|
+
relatedness: "similar-title",
|
|
72
|
+
score: sim,
|
|
73
|
+
});
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
// same severity as co-occurring
|
|
77
|
+
if ((f.severity || "medium").toLowerCase() === (target.severity || "medium").toLowerCase()) {
|
|
78
|
+
results.push({
|
|
79
|
+
ruleId: f.ruleId,
|
|
80
|
+
title: f.title,
|
|
81
|
+
severity: (f.severity || "medium").toLowerCase(),
|
|
82
|
+
relatedness: "co-occurring",
|
|
83
|
+
score: 0.3,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
results.sort((a, b) => b.score - a.score);
|
|
88
|
+
return results;
|
|
89
|
+
}
|
|
90
|
+
// ─── CLI ────────────────────────────────────────────────────────────────────
|
|
91
|
+
export function runFindingRelatedRules(argv) {
|
|
92
|
+
const fileIdx = argv.indexOf("--file");
|
|
93
|
+
const ruleIdx = argv.indexOf("--rule");
|
|
94
|
+
const formatIdx = argv.indexOf("--format");
|
|
95
|
+
const limitIdx = argv.indexOf("--limit");
|
|
96
|
+
const filePath = fileIdx >= 0 ? argv[fileIdx + 1] : undefined;
|
|
97
|
+
const ruleId = ruleIdx >= 0 ? argv[ruleIdx + 1] : undefined;
|
|
98
|
+
const format = formatIdx >= 0 ? argv[formatIdx + 1] : "table";
|
|
99
|
+
const limit = limitIdx >= 0 ? parseInt(argv[limitIdx + 1], 10) : 20;
|
|
100
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
101
|
+
console.log(`
|
|
102
|
+
judges finding-related-rules — Find related rules
|
|
103
|
+
|
|
104
|
+
Usage:
|
|
105
|
+
judges finding-related-rules --file <verdict.json> --rule <RULE-ID>
|
|
106
|
+
[--format table|json] [--limit <n>]
|
|
107
|
+
|
|
108
|
+
Options:
|
|
109
|
+
--file <path> Path to verdict JSON file (required)
|
|
110
|
+
--rule <id> Target rule ID (required)
|
|
111
|
+
--format <fmt> Output format: table (default), json
|
|
112
|
+
--limit <n> Max results (default: 20)
|
|
113
|
+
--help, -h Show this help
|
|
114
|
+
`);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
if (!filePath || !ruleId) {
|
|
118
|
+
console.error("Error: --file and --rule required");
|
|
119
|
+
process.exitCode = 1;
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
if (!existsSync(filePath)) {
|
|
123
|
+
console.error(`Error: not found: ${filePath}`);
|
|
124
|
+
process.exitCode = 1;
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
let verdict;
|
|
128
|
+
try {
|
|
129
|
+
verdict = JSON.parse(readFileSync(filePath, "utf-8"));
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
console.error("Error: invalid JSON");
|
|
133
|
+
process.exitCode = 1;
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
const results = findRelated(verdict, ruleId).slice(0, limit);
|
|
137
|
+
if (format === "json") {
|
|
138
|
+
console.log(JSON.stringify(results, null, 2));
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
console.log(`\nRelated Rules for ${ruleId} (${results.length} found)`);
|
|
142
|
+
console.log("═".repeat(75));
|
|
143
|
+
console.log(`${"Rule".padEnd(20)} ${"Relatedness".padEnd(16)} ${"Score".padEnd(8)} ${"Severity".padEnd(10)} Title`);
|
|
144
|
+
console.log("─".repeat(75));
|
|
145
|
+
for (const r of results) {
|
|
146
|
+
const rule = r.ruleId.length > 18 ? r.ruleId.slice(0, 18) + "…" : r.ruleId;
|
|
147
|
+
const title = r.title.length > 25 ? r.title.slice(0, 25) + "…" : r.title;
|
|
148
|
+
console.log(`${rule.padEnd(20)} ${r.relatedness.padEnd(16)} ${r.score.toFixed(2).padEnd(8)} ${r.severity.padEnd(10)} ${title}`);
|
|
149
|
+
}
|
|
150
|
+
console.log("═".repeat(75));
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=finding-related-rules.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finding-related-rules.js","sourceRoot":"","sources":["../../src/commands/finding-related-rules.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finding-rule-explain.d.ts","sourceRoot":"","sources":["../../src/commands/finding-rule-explain.ts"],"names":[],"mappings":"AAAA;;GAEG;AA+EH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CA8F1D"}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Finding-rule-explain — Explain rules in detail with examples and remediation.
|
|
3
|
+
*/
|
|
4
|
+
import { readFileSync, existsSync } from "fs";
|
|
5
|
+
import { defaultRegistry } from "../judge-registry.js";
|
|
6
|
+
// ─── Analysis ───────────────────────────────────────────────────────────────
|
|
7
|
+
function explainRules(verdict, targetRule) {
|
|
8
|
+
const judges = defaultRegistry.getJudges();
|
|
9
|
+
const explanations = [];
|
|
10
|
+
const ruleMap = new Map();
|
|
11
|
+
for (const f of verdict.findings) {
|
|
12
|
+
if (targetRule !== undefined && f.ruleId !== targetRule)
|
|
13
|
+
continue;
|
|
14
|
+
const existing = ruleMap.get(f.ruleId);
|
|
15
|
+
if (existing) {
|
|
16
|
+
existing.occurrences++;
|
|
17
|
+
const lines = f.lineNumbers || [];
|
|
18
|
+
for (const ln of lines) {
|
|
19
|
+
if (!existing.affectedLines.includes(ln)) {
|
|
20
|
+
existing.affectedLines.push(ln);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
// find the judge for this rule
|
|
26
|
+
let judgeName = "unknown";
|
|
27
|
+
let domain = "unknown";
|
|
28
|
+
for (const j of judges) {
|
|
29
|
+
if (f.ruleId.startsWith(j.rulePrefix)) {
|
|
30
|
+
judgeName = j.name;
|
|
31
|
+
domain = j.domain;
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const explanation = {
|
|
36
|
+
ruleId: f.ruleId,
|
|
37
|
+
title: f.title,
|
|
38
|
+
severity: (f.severity || "medium").toLowerCase(),
|
|
39
|
+
judge: judgeName,
|
|
40
|
+
domain,
|
|
41
|
+
description: f.description,
|
|
42
|
+
recommendation: f.recommendation,
|
|
43
|
+
occurrences: 1,
|
|
44
|
+
affectedLines: [...(f.lineNumbers || [])],
|
|
45
|
+
};
|
|
46
|
+
ruleMap.set(f.ruleId, explanation);
|
|
47
|
+
explanations.push(explanation);
|
|
48
|
+
}
|
|
49
|
+
explanations.sort((a, b) => {
|
|
50
|
+
const sevOrder = { critical: 0, high: 1, medium: 2, low: 3, info: 4 };
|
|
51
|
+
return (sevOrder[a.severity] || 3) - (sevOrder[b.severity] || 3);
|
|
52
|
+
});
|
|
53
|
+
return explanations;
|
|
54
|
+
}
|
|
55
|
+
// ─── CLI ────────────────────────────────────────────────────────────────────
|
|
56
|
+
export function runFindingRuleExplain(argv) {
|
|
57
|
+
const fileIdx = argv.indexOf("--file");
|
|
58
|
+
const ruleIdx = argv.indexOf("--rule");
|
|
59
|
+
const formatIdx = argv.indexOf("--format");
|
|
60
|
+
const filePath = fileIdx >= 0 ? argv[fileIdx + 1] : undefined;
|
|
61
|
+
const targetRule = ruleIdx >= 0 ? argv[ruleIdx + 1] : undefined;
|
|
62
|
+
const format = formatIdx >= 0 ? argv[formatIdx + 1] : "detail";
|
|
63
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
64
|
+
console.log(`
|
|
65
|
+
judges finding-rule-explain — Explain rules in detail
|
|
66
|
+
|
|
67
|
+
Usage:
|
|
68
|
+
judges finding-rule-explain --file <verdict.json> [--rule <RULE-ID>]
|
|
69
|
+
[--format detail|table|json]
|
|
70
|
+
|
|
71
|
+
Options:
|
|
72
|
+
--file <path> Path to verdict JSON file (required)
|
|
73
|
+
--rule <id> Explain a specific rule ID
|
|
74
|
+
--format <fmt> Output format: detail (default), table, json
|
|
75
|
+
--help, -h Show this help
|
|
76
|
+
`);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
if (!filePath) {
|
|
80
|
+
console.error("Error: --file required");
|
|
81
|
+
process.exitCode = 1;
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
if (!existsSync(filePath)) {
|
|
85
|
+
console.error(`Error: not found: ${filePath}`);
|
|
86
|
+
process.exitCode = 1;
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
let verdict;
|
|
90
|
+
try {
|
|
91
|
+
verdict = JSON.parse(readFileSync(filePath, "utf-8"));
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
console.error("Error: invalid JSON");
|
|
95
|
+
process.exitCode = 1;
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
const explanations = explainRules(verdict, targetRule);
|
|
99
|
+
if (explanations.length === 0) {
|
|
100
|
+
console.log(targetRule ? `No findings for rule: ${targetRule}` : "No findings to explain");
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
if (format === "json") {
|
|
104
|
+
console.log(JSON.stringify(explanations, null, 2));
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
if (format === "table") {
|
|
108
|
+
console.log(`\nRule Explanations (${explanations.length} rules)`);
|
|
109
|
+
console.log("═".repeat(80));
|
|
110
|
+
console.log(`${"Rule".padEnd(18)} ${"Severity".padEnd(10)} ${"Judge".padEnd(22)} ${"Occurs".padEnd(8)} Title`);
|
|
111
|
+
console.log("─".repeat(80));
|
|
112
|
+
for (const e of explanations) {
|
|
113
|
+
const rule = e.ruleId.length > 16 ? e.ruleId.slice(0, 16) + "…" : e.ruleId;
|
|
114
|
+
const judge = e.judge.length > 20 ? e.judge.slice(0, 20) + "…" : e.judge;
|
|
115
|
+
const title = e.title.length > 25 ? e.title.slice(0, 25) + "…" : e.title;
|
|
116
|
+
console.log(`${rule.padEnd(18)} ${e.severity.padEnd(10)} ${judge.padEnd(22)} ${String(e.occurrences).padEnd(8)} ${title}`);
|
|
117
|
+
}
|
|
118
|
+
console.log("═".repeat(80));
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
// detail format
|
|
122
|
+
for (const e of explanations) {
|
|
123
|
+
console.log(`\n${"═".repeat(70)}`);
|
|
124
|
+
console.log(` Rule: ${e.ruleId}`);
|
|
125
|
+
console.log(` Title: ${e.title}`);
|
|
126
|
+
console.log(` Severity: ${e.severity.toUpperCase()}`);
|
|
127
|
+
console.log(` Judge: ${e.judge}`);
|
|
128
|
+
console.log(` Domain: ${e.domain}`);
|
|
129
|
+
console.log(` Occurrences: ${e.occurrences}`);
|
|
130
|
+
if (e.affectedLines.length > 0) {
|
|
131
|
+
console.log(` Lines: ${e.affectedLines.join(", ")}`);
|
|
132
|
+
}
|
|
133
|
+
console.log(`${"─".repeat(70)}`);
|
|
134
|
+
console.log(` Description:`);
|
|
135
|
+
console.log(` ${e.description}`);
|
|
136
|
+
console.log(` Recommendation:`);
|
|
137
|
+
console.log(` ${e.recommendation}`);
|
|
138
|
+
}
|
|
139
|
+
console.log(`\n${"═".repeat(70)}`);
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=finding-rule-explain.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finding-rule-explain.js","sourceRoot":"","sources":["../../src/commands/finding-rule-explain.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAE9C,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAgBvD,+EAA+E;AAE/E,SAAS,YAAY,CAAC,OAAwB,EAAE,UAAmB;IACjE,MAAM,MAAM,GAAG,eAAe,CAAC,SAAS,EAAE,CAAC;IAC3C,MAAM,YAAY,GAAsB,EAAE,CAAC;IAC3C,MAAM,OAAO,GAAG,IAAI,GAAG,EAA2B,CAAC;IAEnD,KAAK,MAAM,CAAC,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;QACjC,IAAI,UAAU,KAAK,SAAS,IAAI,CAAC,CAAC,MAAM,KAAK,UAAU;YAAE,SAAS;QAElE,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;QACvC,IAAI,QAAQ,EAAE,CAAC;YACb,QAAQ,CAAC,WAAW,EAAE,CAAC;YACvB,MAAM,KAAK,GAAG,CAAC,CAAC,WAAW,IAAI,EAAE,CAAC;YAClC,KAAK,MAAM,EAAE,IAAI,KAAK,EAAE,CAAC;gBACvB,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC;oBACzC,QAAQ,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBAClC,CAAC;YACH,CAAC;YACD,SAAS;QACX,CAAC;QAED,+BAA+B;QAC/B,IAAI,SAAS,GAAG,SAAS,CAAC;QAC1B,IAAI,MAAM,GAAG,SAAS,CAAC;QACvB,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;YACvB,IAAI,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,EAAE,CAAC;gBACtC,SAAS,GAAG,CAAC,CAAC,IAAI,CAAC;gBACnB,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC;gBAClB,MAAM;YACR,CAAC;QACH,CAAC;QAED,MAAM,WAAW,GAAoB;YACnC,MAAM,EAAE,CAAC,CAAC,MAAM;YAChB,KAAK,EAAE,CAAC,CAAC,KAAK;YACd,QAAQ,EAAE,CAAC,CAAC,CAAC,QAAQ,IAAI,QAAQ,CAAC,CAAC,WAAW,EAAE;YAChD,KAAK,EAAE,SAAS;YAChB,MAAM;YACN,WAAW,EAAE,CAAC,CAAC,WAAW;YAC1B,cAAc,EAAE,CAAC,CAAC,cAAc;YAChC,WAAW,EAAE,CAAC;YACd,aAAa,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC;SAC1C,CAAC;QAEF,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;QACnC,YAAY,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACjC,CAAC;IAED,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACzB,MAAM,QAAQ,GAA2B,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;QAC9F,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;IACnE,CAAC,CAAC,CAAC;IAEH,OAAO,YAAY,CAAC;AACtB,CAAC;AAED,+EAA+E;AAE/E,MAAM,UAAU,qBAAqB,CAAC,IAAc;IAClD,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACvC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACvC,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAC3C,MAAM,QAAQ,GAAG,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC9D,MAAM,UAAU,GAAG,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAChE,MAAM,MAAM,GAAG,SAAS,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;IAE/D,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACnD,OAAO,CAAC,GAAG,CAAC;;;;;;;;;;;;CAYf,CAAC,CAAC;QACC,OAAO;IACT,CAAC;IAED,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAC;QACxC,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACrB,OAAO;IACT,CAAC;IACD,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC1B,OAAO,CAAC,KAAK,CAAC,qBAAqB,QAAQ,EAAE,CAAC,CAAC;QAC/C,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACrB,OAAO;IACT,CAAC;IAED,IAAI,OAAwB,CAAC;IAC7B,IAAI,CAAC;QACH,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC;IACxD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC;QACrC,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACrB,OAAO;IACT,CAAC;IAED,MAAM,YAAY,GAAG,YAAY,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;IAEvD,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9B,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,yBAAyB,UAAU,EAAE,CAAC,CAAC,CAAC,wBAAwB,CAAC,CAAC;QAC3F,OAAO;IACT,CAAC;IAED,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;QACtB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QACnD,OAAO;IACT,CAAC;IAED,IAAI,MAAM,KAAK,OAAO,EAAE,CAAC;QACvB,OAAO,CAAC,GAAG,CAAC,wBAAwB,YAAY,CAAC,MAAM,SAAS,CAAC,CAAC;QAClE,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;QAC5B,OAAO,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;QAC/G,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;QAE5B,KAAK,MAAM,CAAC,IAAI,YAAY,EAAE,CAAC;YAC7B,MAAM,IAAI,GAAG,CAAC,CAAC,MAAM,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;YAC3E,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;YACzE,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;YACzE,OAAO,CAAC,GAAG,CACT,GAAG,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,KAAK,EAAE,CAC9G,CAAC;QACJ,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;QAC5B,OAAO;IACT,CAAC;IAED,gBAAgB;IAChB,KAAK,MAAM,CAAC,IAAI,YAAY,EAAE,CAAC;QAC7B,OAAO,CAAC,GAAG,CAAC,KAAK,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QACnC,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;QACnC,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;QACnC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,QAAQ,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;QACvD,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;QACnC,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;QACrC,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;QAC/C,IAAI,CAAC,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC/B,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACxD,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QACjC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;QAC9B,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;QACpC,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;QACjC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,cAAc,EAAE,CAAC,CAAC;IACzC,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,KAAK,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;AACrC,CAAC"}
|