@kevinrabun/judges 3.47.0 → 3.48.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 +12 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +56 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/audit-trail.d.ts +18 -0
- package/dist/commands/audit-trail.d.ts.map +1 -0
- package/dist/commands/audit-trail.js +155 -0
- package/dist/commands/audit-trail.js.map +1 -0
- package/dist/commands/auto-fix.d.ts +18 -0
- package/dist/commands/auto-fix.d.ts.map +1 -0
- package/dist/commands/auto-fix.js +241 -0
- package/dist/commands/auto-fix.js.map +1 -0
- package/dist/commands/dep-correlate.d.ts +9 -0
- package/dist/commands/dep-correlate.d.ts.map +1 -0
- package/dist/commands/dep-correlate.js +208 -0
- package/dist/commands/dep-correlate.js.map +1 -0
- package/dist/commands/doc-gen.d.ts +8 -0
- package/dist/commands/doc-gen.d.ts.map +1 -0
- package/dist/commands/doc-gen.js +209 -0
- package/dist/commands/doc-gen.js.map +1 -0
- package/dist/commands/judge-author.d.ts +8 -0
- package/dist/commands/judge-author.d.ts.map +1 -0
- package/dist/commands/judge-author.js +261 -0
- package/dist/commands/judge-author.js.map +1 -0
- package/dist/commands/pattern-registry.d.ts +23 -0
- package/dist/commands/pattern-registry.d.ts.map +1 -0
- package/dist/commands/pattern-registry.js +227 -0
- package/dist/commands/pattern-registry.js.map +1 -0
- package/dist/commands/perf-hotspot.d.ts +8 -0
- package/dist/commands/perf-hotspot.d.ts.map +1 -0
- package/dist/commands/perf-hotspot.js +274 -0
- package/dist/commands/perf-hotspot.js.map +1 -0
- package/dist/commands/security-maturity.d.ts +8 -0
- package/dist/commands/security-maturity.d.ts.map +1 -0
- package/dist/commands/security-maturity.js +313 -0
- package/dist/commands/security-maturity.js.map +1 -0
- package/package.json +1 -1
- package/server.json +2 -2
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pattern registry — team knowledge sharing via a local
|
|
3
|
+
* repository of security patterns and anti-patterns.
|
|
4
|
+
*
|
|
5
|
+
* All data stays in .judges-patterns/ directory.
|
|
6
|
+
*/
|
|
7
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync } from "fs";
|
|
8
|
+
import { join } from "path";
|
|
9
|
+
const PATTERN_DIR = ".judges-patterns";
|
|
10
|
+
// ─── Built-in patterns ─────────────────────────────────────────────────────
|
|
11
|
+
const BUILTIN_PATTERNS = [
|
|
12
|
+
{
|
|
13
|
+
id: "sql-parameterized",
|
|
14
|
+
title: "Parameterized SQL Queries",
|
|
15
|
+
category: "injection",
|
|
16
|
+
type: "pattern",
|
|
17
|
+
language: "typescript",
|
|
18
|
+
description: "Always use parameterized queries to prevent SQL injection",
|
|
19
|
+
example: 'db.query("SELECT * FROM users WHERE id = $1", [userId])',
|
|
20
|
+
author: "judges",
|
|
21
|
+
tags: ["sql", "injection", "security"],
|
|
22
|
+
createdAt: "2025-01-01",
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
id: "sql-concat-antipattern",
|
|
26
|
+
title: "String Concatenation in SQL",
|
|
27
|
+
category: "injection",
|
|
28
|
+
type: "anti-pattern",
|
|
29
|
+
language: "typescript",
|
|
30
|
+
description: "Never concatenate user input into SQL queries",
|
|
31
|
+
example: 'db.query("SELECT * FROM users WHERE id = " + userId)',
|
|
32
|
+
fix: 'db.query("SELECT * FROM users WHERE id = $1", [userId])',
|
|
33
|
+
author: "judges",
|
|
34
|
+
tags: ["sql", "injection", "security"],
|
|
35
|
+
createdAt: "2025-01-01",
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
id: "input-validation",
|
|
39
|
+
title: "Input Validation Pattern",
|
|
40
|
+
category: "validation",
|
|
41
|
+
type: "pattern",
|
|
42
|
+
language: "typescript",
|
|
43
|
+
description: "Validate all user input at system boundaries",
|
|
44
|
+
example: 'if (!schema.safeParse(req.body).success) return res.status(400).json({ error: "Invalid input" })',
|
|
45
|
+
author: "judges",
|
|
46
|
+
tags: ["validation", "input", "security"],
|
|
47
|
+
createdAt: "2025-01-01",
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
id: "error-exposure-antipattern",
|
|
51
|
+
title: "Stack Trace Exposure",
|
|
52
|
+
category: "error-handling",
|
|
53
|
+
type: "anti-pattern",
|
|
54
|
+
language: "typescript",
|
|
55
|
+
description: "Never expose stack traces or internal errors to users",
|
|
56
|
+
example: "res.status(500).json({ error: err.stack })",
|
|
57
|
+
fix: 'res.status(500).json({ error: "Internal server error" })',
|
|
58
|
+
author: "judges",
|
|
59
|
+
tags: ["error", "information-leak", "security"],
|
|
60
|
+
createdAt: "2025-01-01",
|
|
61
|
+
},
|
|
62
|
+
];
|
|
63
|
+
// ─── Core ───────────────────────────────────────────────────────────────────
|
|
64
|
+
function ensureDir() {
|
|
65
|
+
if (!existsSync(PATTERN_DIR))
|
|
66
|
+
mkdirSync(PATTERN_DIR, { recursive: true });
|
|
67
|
+
}
|
|
68
|
+
function loadPatterns() {
|
|
69
|
+
ensureDir();
|
|
70
|
+
const files = readdirSync(PATTERN_DIR).filter((f) => f.endsWith(".json"));
|
|
71
|
+
const custom = [];
|
|
72
|
+
for (const f of files) {
|
|
73
|
+
try {
|
|
74
|
+
custom.push(JSON.parse(readFileSync(join(PATTERN_DIR, f), "utf-8")));
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
/* skip invalid files */
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return [...BUILTIN_PATTERNS, ...custom];
|
|
81
|
+
}
|
|
82
|
+
function sanitizeId(id) {
|
|
83
|
+
return id
|
|
84
|
+
.replace(/[^a-zA-Z0-9-]/g, "-")
|
|
85
|
+
.toLowerCase()
|
|
86
|
+
.slice(0, 50);
|
|
87
|
+
}
|
|
88
|
+
export function addPattern(pattern) {
|
|
89
|
+
ensureDir();
|
|
90
|
+
const id = sanitizeId(pattern.title);
|
|
91
|
+
const full = {
|
|
92
|
+
...pattern,
|
|
93
|
+
id,
|
|
94
|
+
createdAt: new Date().toISOString().slice(0, 10),
|
|
95
|
+
};
|
|
96
|
+
writeFileSync(join(PATTERN_DIR, `${id}.json`), JSON.stringify(full, null, 2));
|
|
97
|
+
return full;
|
|
98
|
+
}
|
|
99
|
+
// ─── CLI ────────────────────────────────────────────────────────────────────
|
|
100
|
+
export function runPatternRegistry(argv) {
|
|
101
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
102
|
+
console.log(`
|
|
103
|
+
judges pattern-registry — Team security pattern knowledge repository
|
|
104
|
+
|
|
105
|
+
Usage:
|
|
106
|
+
judges pattern-registry --list
|
|
107
|
+
judges pattern-registry --show sql-parameterized
|
|
108
|
+
judges pattern-registry --add --title "CSRF Token Check" --category csrf --type pattern --lang typescript --desc "Always verify CSRF tokens" --example "verifyCSRF(req.headers['x-csrf-token'])" --author "alice@co.com"
|
|
109
|
+
judges pattern-registry --search injection
|
|
110
|
+
judges pattern-registry --anti-patterns
|
|
111
|
+
|
|
112
|
+
Options:
|
|
113
|
+
--list List all patterns
|
|
114
|
+
--show <id> Show pattern details
|
|
115
|
+
--add Add a new pattern
|
|
116
|
+
--title <text> Pattern title
|
|
117
|
+
--category <name> Category (injection, validation, auth, etc.)
|
|
118
|
+
--type <kind> pattern or anti-pattern
|
|
119
|
+
--lang <language> Programming language
|
|
120
|
+
--desc <text> Description
|
|
121
|
+
--example <code> Code example
|
|
122
|
+
--fix <code> Fix for anti-patterns
|
|
123
|
+
--author <name> Author name
|
|
124
|
+
--search <term> Search patterns by keyword
|
|
125
|
+
--anti-patterns Show only anti-patterns
|
|
126
|
+
--format json JSON output
|
|
127
|
+
--help, -h Show this help
|
|
128
|
+
`);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
|
|
132
|
+
const patterns = loadPatterns();
|
|
133
|
+
// Anti-patterns only
|
|
134
|
+
if (argv.includes("--anti-patterns")) {
|
|
135
|
+
const antiPatterns = patterns.filter((p) => p.type === "anti-pattern");
|
|
136
|
+
if (format === "json") {
|
|
137
|
+
console.log(JSON.stringify(antiPatterns, null, 2));
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
console.log(`\n Anti-Patterns (${antiPatterns.length})\n ──────────────────────────`);
|
|
141
|
+
for (const p of antiPatterns) {
|
|
142
|
+
console.log(` ❌ ${p.id.padEnd(30)} ${p.title}`);
|
|
143
|
+
console.log(` ${p.description}`);
|
|
144
|
+
if (p.fix)
|
|
145
|
+
console.log(` Fix: ${p.fix}`);
|
|
146
|
+
console.log("");
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
// Search
|
|
152
|
+
const searchTerm = argv.find((_a, i) => argv[i - 1] === "--search");
|
|
153
|
+
if (searchTerm) {
|
|
154
|
+
const term = searchTerm.toLowerCase();
|
|
155
|
+
const matches = patterns.filter((p) => p.title.toLowerCase().includes(term) ||
|
|
156
|
+
p.description.toLowerCase().includes(term) ||
|
|
157
|
+
p.tags.some((t) => t.includes(term)) ||
|
|
158
|
+
p.category.includes(term));
|
|
159
|
+
if (format === "json") {
|
|
160
|
+
console.log(JSON.stringify(matches, null, 2));
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
console.log(`\n Search: "${searchTerm}" (${matches.length} matches)\n ──────────────────────────`);
|
|
164
|
+
for (const p of matches) {
|
|
165
|
+
const icon = p.type === "pattern" ? "✅" : "❌";
|
|
166
|
+
console.log(` ${icon} ${p.id.padEnd(30)} ${p.title}`);
|
|
167
|
+
}
|
|
168
|
+
console.log("");
|
|
169
|
+
}
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
// Show specific
|
|
173
|
+
const showId = argv.find((_a, i) => argv[i - 1] === "--show");
|
|
174
|
+
if (showId) {
|
|
175
|
+
const p = patterns.find((pat) => pat.id === showId);
|
|
176
|
+
if (!p) {
|
|
177
|
+
console.error(` Pattern not found: ${showId}`);
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
if (format === "json") {
|
|
181
|
+
console.log(JSON.stringify(p, null, 2));
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
const icon = p.type === "pattern" ? "✅" : "❌";
|
|
185
|
+
console.log(`\n ${icon} ${p.title}`);
|
|
186
|
+
console.log(` ──────────────────────────`);
|
|
187
|
+
console.log(` Category: ${p.category}`);
|
|
188
|
+
console.log(` Type: ${p.type}`);
|
|
189
|
+
console.log(` Language: ${p.language}`);
|
|
190
|
+
console.log(` Author: ${p.author}`);
|
|
191
|
+
console.log(` Tags: ${p.tags.join(", ")}`);
|
|
192
|
+
console.log(`\n ${p.description}`);
|
|
193
|
+
console.log(`\n Example: ${p.example}`);
|
|
194
|
+
if (p.fix)
|
|
195
|
+
console.log(` Fix: ${p.fix}`);
|
|
196
|
+
console.log("");
|
|
197
|
+
}
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
// Add pattern
|
|
201
|
+
if (argv.includes("--add")) {
|
|
202
|
+
const title = argv.find((_a, i) => argv[i - 1] === "--title") || "Untitled";
|
|
203
|
+
const category = argv.find((_a, i) => argv[i - 1] === "--category") || "general";
|
|
204
|
+
const type = (argv.find((_a, i) => argv[i - 1] === "--type") || "pattern");
|
|
205
|
+
const language = argv.find((_a, i) => argv[i - 1] === "--lang") || "typescript";
|
|
206
|
+
const description = argv.find((_a, i) => argv[i - 1] === "--desc") || "";
|
|
207
|
+
const example = argv.find((_a, i) => argv[i - 1] === "--example") || "";
|
|
208
|
+
const fix = argv.find((_a, i) => argv[i - 1] === "--fix");
|
|
209
|
+
const author = argv.find((_a, i) => argv[i - 1] === "--author") || "anonymous";
|
|
210
|
+
const p = addPattern({ title, category, type, language, description, example, fix, author, tags: [category] });
|
|
211
|
+
console.log(` ✅ Pattern added: ${p.id}`);
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
// List all
|
|
215
|
+
if (format === "json") {
|
|
216
|
+
console.log(JSON.stringify(patterns, null, 2));
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
console.log(`\n Pattern Registry (${patterns.length} patterns)\n ──────────────────────────`);
|
|
220
|
+
for (const p of patterns) {
|
|
221
|
+
const icon = p.type === "pattern" ? "✅" : "❌";
|
|
222
|
+
console.log(` ${icon} ${p.id.padEnd(30)} ${p.category.padEnd(15)} ${p.title}`);
|
|
223
|
+
}
|
|
224
|
+
console.log(`\n Use: judges pattern-registry --show <id>\n`);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
//# sourceMappingURL=pattern-registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pattern-registry.js","sourceRoot":"","sources":["../../src/commands/pattern-registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Performance hotspot detection — scans code for common
|
|
3
|
+
* performance anti-patterns using pattern-based analysis.
|
|
4
|
+
*
|
|
5
|
+
* All data stored locally.
|
|
6
|
+
*/
|
|
7
|
+
export declare function runPerfHotspot(argv: string[]): void;
|
|
8
|
+
//# sourceMappingURL=perf-hotspot.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"perf-hotspot.d.ts","sourceRoot":"","sources":["../../src/commands/perf-hotspot.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAgMH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CA6HnD"}
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Performance hotspot detection — scans code for common
|
|
3
|
+
* performance anti-patterns using pattern-based analysis.
|
|
4
|
+
*
|
|
5
|
+
* All data stored locally.
|
|
6
|
+
*/
|
|
7
|
+
import { readFileSync, existsSync, mkdirSync, writeFileSync, readdirSync } from "fs";
|
|
8
|
+
import { join, extname } from "path";
|
|
9
|
+
// ─── Patterns ───────────────────────────────────────────────────────────────
|
|
10
|
+
const PERF_PATTERNS = [
|
|
11
|
+
{
|
|
12
|
+
id: "n-plus-one",
|
|
13
|
+
name: "N+1 Query Pattern",
|
|
14
|
+
severity: "critical",
|
|
15
|
+
description: "Database query inside a loop",
|
|
16
|
+
regex: /for\s*\(.*\)\s*\{[^}]*(?:\.query|\.execute|\.find|\.fetch|\.select|await\s+db\.|await\s+prisma\.)/s,
|
|
17
|
+
extensions: [".ts", ".js", ".py", ".java", ".go", ".rb"],
|
|
18
|
+
recommendation: "Batch queries outside the loop or use eager loading / JOIN",
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
id: "unbounded-collection",
|
|
22
|
+
name: "Unbounded Collection Fetch",
|
|
23
|
+
severity: "high",
|
|
24
|
+
description: "Fetching all records without limit/pagination",
|
|
25
|
+
regex: /\.findAll\(\s*\)|\.find\(\s*\{\s*\}\s*\)|SELECT\s+\*\s+FROM\s+\w+\s*(?:WHERE|;|\)|$)/i,
|
|
26
|
+
extensions: [".ts", ".js", ".py", ".java", ".go", ".rb"],
|
|
27
|
+
recommendation: "Add LIMIT/pagination or use cursor-based fetching",
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
id: "sync-io-hot-path",
|
|
31
|
+
name: "Synchronous I/O in Hot Path",
|
|
32
|
+
severity: "high",
|
|
33
|
+
description: "Synchronous file/network I/O that could block event loop",
|
|
34
|
+
regex: /readFileSync|writeFileSync|execSync|spawnSync/,
|
|
35
|
+
extensions: [".ts", ".js"],
|
|
36
|
+
recommendation: "Use async variants (readFile, writeFile, exec) in request handlers",
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
id: "string-concat-loop",
|
|
40
|
+
name: "String Concatenation in Loop",
|
|
41
|
+
severity: "medium",
|
|
42
|
+
description: "String concatenation inside a loop (O(n²) in some languages)",
|
|
43
|
+
regex: /for\s*\(.*\)\s*\{[^}]*\+=\s*["'`]|for\s*\(.*\)\s*\{[^}]*\+=\s*\w+/s,
|
|
44
|
+
extensions: [".ts", ".js", ".py", ".java", ".go", ".cs"],
|
|
45
|
+
recommendation: "Use array.join(), StringBuilder, or strings.Builder",
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
id: "missing-index-hint",
|
|
49
|
+
name: "Missing Index Hint",
|
|
50
|
+
severity: "medium",
|
|
51
|
+
description: "Query with WHERE clause on non-obvious columns",
|
|
52
|
+
regex: /WHERE\s+\w+\.\w+\s*(?:=|LIKE|IN|>|<)\s*/i,
|
|
53
|
+
extensions: [".sql", ".ts", ".js", ".py", ".java"],
|
|
54
|
+
recommendation: "Ensure queried columns have appropriate database indexes",
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
id: "excessive-json-parse",
|
|
58
|
+
name: "Repeated JSON Parse/Stringify",
|
|
59
|
+
severity: "medium",
|
|
60
|
+
description: "JSON.parse/stringify inside loops or frequently called functions",
|
|
61
|
+
regex: /for\s*\(.*\)\s*\{[^}]*JSON\.(?:parse|stringify)/s,
|
|
62
|
+
extensions: [".ts", ".js"],
|
|
63
|
+
recommendation: "Parse once and reuse the object; consider streaming parsers for large data",
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
id: "regex-in-loop",
|
|
67
|
+
name: "Regex Compilation in Loop",
|
|
68
|
+
severity: "low",
|
|
69
|
+
description: "Creating new RegExp objects inside loops",
|
|
70
|
+
regex: /for\s*\(.*\)\s*\{[^}]*new\s+RegExp\(/s,
|
|
71
|
+
extensions: [".ts", ".js", ".py", ".java"],
|
|
72
|
+
recommendation: "Compile regex once outside the loop and reuse",
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
id: "large-object-spread",
|
|
76
|
+
name: "Large Object Spread in Loop",
|
|
77
|
+
severity: "medium",
|
|
78
|
+
description: "Object spread operator inside loops creates many copies",
|
|
79
|
+
regex: /for\s*\(.*\)\s*\{[^}]*\.\.\.\w+/s,
|
|
80
|
+
extensions: [".ts", ".js"],
|
|
81
|
+
recommendation: "Mutate directly or use Object.assign for better performance",
|
|
82
|
+
},
|
|
83
|
+
];
|
|
84
|
+
// ─── Scanner ────────────────────────────────────────────────────────────────
|
|
85
|
+
function scanFile(filePath, patterns) {
|
|
86
|
+
const ext = extname(filePath);
|
|
87
|
+
const applicable = patterns.filter((p) => p.extensions.includes(ext));
|
|
88
|
+
if (applicable.length === 0)
|
|
89
|
+
return [];
|
|
90
|
+
let content;
|
|
91
|
+
try {
|
|
92
|
+
content = readFileSync(filePath, "utf-8");
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
return [];
|
|
96
|
+
}
|
|
97
|
+
const lines = content.split("\n");
|
|
98
|
+
const hotspots = [];
|
|
99
|
+
for (const pattern of applicable) {
|
|
100
|
+
const match = pattern.regex.exec(content);
|
|
101
|
+
if (match) {
|
|
102
|
+
const beforeMatch = content.substring(0, match.index);
|
|
103
|
+
const lineNum = beforeMatch.split("\n").length;
|
|
104
|
+
hotspots.push({
|
|
105
|
+
file: filePath,
|
|
106
|
+
line: lineNum,
|
|
107
|
+
patternId: pattern.id,
|
|
108
|
+
patternName: pattern.name,
|
|
109
|
+
severity: pattern.severity,
|
|
110
|
+
snippet: lines[lineNum - 1]?.trim() || "",
|
|
111
|
+
recommendation: pattern.recommendation,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return hotspots;
|
|
116
|
+
}
|
|
117
|
+
function collectFiles(dir, exts, maxFiles) {
|
|
118
|
+
const result = [];
|
|
119
|
+
const skipDirs = new Set(["node_modules", ".git", "dist", "build", "coverage", ".next"]);
|
|
120
|
+
function walk(d) {
|
|
121
|
+
if (result.length >= maxFiles)
|
|
122
|
+
return;
|
|
123
|
+
let entries;
|
|
124
|
+
try {
|
|
125
|
+
entries = readdirSync(d, { withFileTypes: true }).map((e) => (typeof e === "string" ? e : e.name));
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
for (const entry of entries) {
|
|
131
|
+
if (result.length >= maxFiles)
|
|
132
|
+
return;
|
|
133
|
+
const full = join(d, entry);
|
|
134
|
+
if (skipDirs.has(entry))
|
|
135
|
+
continue;
|
|
136
|
+
try {
|
|
137
|
+
const stat = readFileSync(full, "utf-8"); // test readable
|
|
138
|
+
void stat;
|
|
139
|
+
if (exts.includes(extname(entry))) {
|
|
140
|
+
result.push(full);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
// might be a directory
|
|
145
|
+
try {
|
|
146
|
+
const sub = readdirSync(full);
|
|
147
|
+
void sub;
|
|
148
|
+
walk(full);
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
// skip
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
walk(dir);
|
|
157
|
+
return result;
|
|
158
|
+
}
|
|
159
|
+
// ─── CLI ────────────────────────────────────────────────────────────────────
|
|
160
|
+
const STORE = ".judges-perf-hotspots";
|
|
161
|
+
export function runPerfHotspot(argv) {
|
|
162
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
163
|
+
console.log(`
|
|
164
|
+
judges perf-hotspot — Performance hotspot detection
|
|
165
|
+
|
|
166
|
+
Usage:
|
|
167
|
+
judges perf-hotspot [dir]
|
|
168
|
+
judges perf-hotspot src/ --severity critical,high
|
|
169
|
+
judges perf-hotspot --patterns
|
|
170
|
+
judges perf-hotspot --history
|
|
171
|
+
|
|
172
|
+
Options:
|
|
173
|
+
--severity <levels> Filter by severity (comma-separated)
|
|
174
|
+
--patterns List all performance patterns
|
|
175
|
+
--history Show scan history
|
|
176
|
+
--format json JSON output
|
|
177
|
+
--help, -h Show this help
|
|
178
|
+
`);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
|
|
182
|
+
// List patterns
|
|
183
|
+
if (argv.includes("--patterns")) {
|
|
184
|
+
if (format === "json") {
|
|
185
|
+
console.log(JSON.stringify(PERF_PATTERNS.map(({ regex: _r, ...rest }) => rest), null, 2));
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
console.log(`\n Performance Anti-Patterns (${PERF_PATTERNS.length})\n ──────────────────────────`);
|
|
189
|
+
for (const p of PERF_PATTERNS) {
|
|
190
|
+
console.log(` [${p.severity.toUpperCase().padEnd(8)}] ${p.id.padEnd(25)} ${p.name}`);
|
|
191
|
+
}
|
|
192
|
+
console.log("");
|
|
193
|
+
}
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
// History
|
|
197
|
+
if (argv.includes("--history")) {
|
|
198
|
+
const histPath = join(STORE, "scan-history.json");
|
|
199
|
+
const history = existsSync(histPath) ? JSON.parse(readFileSync(histPath, "utf-8")) : [];
|
|
200
|
+
if (format === "json") {
|
|
201
|
+
console.log(JSON.stringify(history, null, 2));
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
if (history.length === 0) {
|
|
205
|
+
console.log(" No scan history.");
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
console.log(`\n Scan History\n ──────────────────────────`);
|
|
209
|
+
for (const h of history.slice(-10)) {
|
|
210
|
+
console.log(` ${h.timestamp} ${h.scannedFiles} files ${h.hotspots} hotspots`);
|
|
211
|
+
}
|
|
212
|
+
console.log("");
|
|
213
|
+
}
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
// Scan
|
|
217
|
+
const scanDir = argv.find((a) => !a.startsWith("--") && !argv[argv.indexOf(a) - 1]?.startsWith("--")) || ".";
|
|
218
|
+
const sevFilter = argv.find((_a, i) => argv[i - 1] === "--severity");
|
|
219
|
+
const allowedSev = sevFilter ? sevFilter.split(",") : null;
|
|
220
|
+
const allExts = [...new Set(PERF_PATTERNS.flatMap((p) => p.extensions))];
|
|
221
|
+
const files = collectFiles(scanDir, allExts, 500);
|
|
222
|
+
let hotspots = [];
|
|
223
|
+
for (const file of files) {
|
|
224
|
+
hotspots.push(...scanFile(file, PERF_PATTERNS));
|
|
225
|
+
}
|
|
226
|
+
if (allowedSev) {
|
|
227
|
+
hotspots = hotspots.filter((h) => allowedSev.includes(h.severity));
|
|
228
|
+
}
|
|
229
|
+
const report = {
|
|
230
|
+
hotspots,
|
|
231
|
+
scannedFiles: files.length,
|
|
232
|
+
patternsChecked: PERF_PATTERNS.length,
|
|
233
|
+
timestamp: new Date().toISOString(),
|
|
234
|
+
};
|
|
235
|
+
// Save history
|
|
236
|
+
if (!existsSync(STORE))
|
|
237
|
+
mkdirSync(STORE, { recursive: true });
|
|
238
|
+
const histPath = join(STORE, "scan-history.json");
|
|
239
|
+
const history = existsSync(histPath) ? JSON.parse(readFileSync(histPath, "utf-8")) : [];
|
|
240
|
+
history.push({ timestamp: report.timestamp, scannedFiles: report.scannedFiles, hotspots: hotspots.length });
|
|
241
|
+
writeFileSync(histPath, JSON.stringify(history, null, 2));
|
|
242
|
+
if (format === "json") {
|
|
243
|
+
console.log(JSON.stringify(report, null, 2));
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
console.log(`\n Performance Hotspot Scan`);
|
|
247
|
+
console.log(` Scanned: ${report.scannedFiles} files Patterns: ${report.patternsChecked}`);
|
|
248
|
+
console.log(` Found: ${hotspots.length} hotspots\n ──────────────────────────`);
|
|
249
|
+
if (hotspots.length === 0) {
|
|
250
|
+
console.log(` ✅ No performance hotspots detected\n`);
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
const grouped = new Map();
|
|
254
|
+
for (const h of hotspots) {
|
|
255
|
+
const key = h.severity;
|
|
256
|
+
if (!grouped.has(key))
|
|
257
|
+
grouped.set(key, []);
|
|
258
|
+
grouped.get(key).push(h);
|
|
259
|
+
}
|
|
260
|
+
for (const sev of ["critical", "high", "medium", "low"]) {
|
|
261
|
+
const items = grouped.get(sev);
|
|
262
|
+
if (!items)
|
|
263
|
+
continue;
|
|
264
|
+
console.log(`\n ${sev.toUpperCase()} (${items.length})`);
|
|
265
|
+
for (const h of items) {
|
|
266
|
+
console.log(` ${h.file}:${h.line} — ${h.patternName}`);
|
|
267
|
+
console.log(` ${h.snippet}`);
|
|
268
|
+
console.log(` → ${h.recommendation}`);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
console.log("");
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
//# sourceMappingURL=perf-hotspot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"perf-hotspot.js","sourceRoot":"","sources":["../../src/commands/perf-hotspot.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security maturity — assesses organization's security posture
|
|
3
|
+
* maturity level based on Judges usage and finding data.
|
|
4
|
+
*
|
|
5
|
+
* All analysis from local files — no external data.
|
|
6
|
+
*/
|
|
7
|
+
export declare function runSecurityMaturity(argv: string[]): void;
|
|
8
|
+
//# sourceMappingURL=security-maturity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"security-maturity.d.ts","sourceRoot":"","sources":["../../src/commands/security-maturity.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AA4PH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAqFxD"}
|