@kevinrabun/judges 3.59.0 → 3.60.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/cli.d.ts.map +1 -1
  3. package/dist/cli.js +56 -0
  4. package/dist/cli.js.map +1 -1
  5. package/dist/commands/adoption-track.d.ts +5 -0
  6. package/dist/commands/adoption-track.d.ts.map +1 -0
  7. package/dist/commands/adoption-track.js +247 -0
  8. package/dist/commands/adoption-track.js.map +1 -0
  9. package/dist/commands/context-blind.d.ts +5 -0
  10. package/dist/commands/context-blind.d.ts.map +1 -0
  11. package/dist/commands/context-blind.js +273 -0
  12. package/dist/commands/context-blind.js.map +1 -0
  13. package/dist/commands/finding-budget.d.ts +5 -0
  14. package/dist/commands/finding-budget.d.ts.map +1 -0
  15. package/dist/commands/finding-budget.js +233 -0
  16. package/dist/commands/finding-budget.js.map +1 -0
  17. package/dist/commands/hallucination-detect.d.ts +5 -0
  18. package/dist/commands/hallucination-detect.d.ts.map +1 -0
  19. package/dist/commands/hallucination-detect.js +351 -0
  20. package/dist/commands/hallucination-detect.js.map +1 -0
  21. package/dist/commands/over-abstraction.d.ts +5 -0
  22. package/dist/commands/over-abstraction.d.ts.map +1 -0
  23. package/dist/commands/over-abstraction.js +308 -0
  24. package/dist/commands/over-abstraction.js.map +1 -0
  25. package/dist/commands/review-digest.d.ts +5 -0
  26. package/dist/commands/review-digest.d.ts.map +1 -0
  27. package/dist/commands/review-digest.js +266 -0
  28. package/dist/commands/review-digest.js.map +1 -0
  29. package/dist/commands/security-theater.d.ts +5 -0
  30. package/dist/commands/security-theater.d.ts.map +1 -0
  31. package/dist/commands/security-theater.js +279 -0
  32. package/dist/commands/security-theater.js.map +1 -0
  33. package/dist/commands/stale-pattern.d.ts +5 -0
  34. package/dist/commands/stale-pattern.d.ts.map +1 -0
  35. package/dist/commands/stale-pattern.js +294 -0
  36. package/dist/commands/stale-pattern.js.map +1 -0
  37. package/package.json +1 -1
  38. package/server.json +2 -2
@@ -0,0 +1,279 @@
1
+ /**
2
+ * Security-theater — detect security-looking code that provides no actual protection.
3
+ */
4
+ import { readFileSync, readdirSync, statSync } from "fs";
5
+ import { join, extname } from "path";
6
+ // ─── File Collection ────────────────────────────────────────────────────────
7
+ const CODE_EXTS = new Set([".ts", ".tsx", ".js", ".jsx", ".py", ".java", ".go", ".cs"]);
8
+ function collectFiles(dir, max = 300) {
9
+ const files = [];
10
+ function walk(d) {
11
+ if (files.length >= max)
12
+ return;
13
+ let entries;
14
+ try {
15
+ entries = readdirSync(d);
16
+ }
17
+ catch {
18
+ return;
19
+ }
20
+ for (const e of entries) {
21
+ if (files.length >= max)
22
+ return;
23
+ if (e.startsWith(".") || e === "node_modules" || e === "dist" || e === "build")
24
+ continue;
25
+ const full = join(d, e);
26
+ try {
27
+ if (statSync(full).isDirectory())
28
+ walk(full);
29
+ else if (CODE_EXTS.has(extname(full)))
30
+ files.push(full);
31
+ }
32
+ catch {
33
+ /* skip */
34
+ }
35
+ }
36
+ }
37
+ walk(dir);
38
+ return files;
39
+ }
40
+ // ─── Analysis ───────────────────────────────────────────────────────────────
41
+ function analyzeFile(filepath) {
42
+ const issues = [];
43
+ let content;
44
+ try {
45
+ content = readFileSync(filepath, "utf-8");
46
+ }
47
+ catch {
48
+ return issues;
49
+ }
50
+ const lines = content.split("\n");
51
+ for (let i = 0; i < lines.length; i++) {
52
+ const line = lines[i];
53
+ const trimmed = line.trim();
54
+ if (trimmed.startsWith("//") || trimmed.startsWith("*") || trimmed.startsWith("/*"))
55
+ continue;
56
+ // Weak password hashing
57
+ if (/(?:createHash|hashlib\.)\s*\(\s*['"](?:md5|sha1)['"]\s*\)/.test(line) &&
58
+ /password|passwd|pwd/i.test(lines.slice(Math.max(0, i - 3), i + 3).join(" "))) {
59
+ issues.push({
60
+ file: filepath,
61
+ line: i + 1,
62
+ issue: "Weak password hashing (MD5/SHA1)",
63
+ severity: "high",
64
+ detail: "MD5/SHA1 are not appropriate for password hashing — use bcrypt, scrypt, or argon2",
65
+ });
66
+ }
67
+ // Hardcoded encryption keys/IVs
68
+ if (/(?:key|iv|secret|password)\s*[:=]\s*['"][A-Za-z0-9+/=]{8,}['"]/.test(line) &&
69
+ /(?:cipher|encrypt|decrypt|crypto|aes|des)/i.test(lines.slice(Math.max(0, i - 5), i + 5).join(" "))) {
70
+ issues.push({
71
+ file: filepath,
72
+ line: i + 1,
73
+ issue: "Hardcoded encryption key or IV",
74
+ severity: "high",
75
+ detail: "Encryption keys/IVs must not be hardcoded — use environment variables or key management services",
76
+ });
77
+ }
78
+ // Wildcard CORS with auth
79
+ if (/['"]Access-Control-Allow-Origin['"]\s*[:,]\s*['"]\s*[*]\s*['"]/.test(line)) {
80
+ const nearby = lines.slice(i, Math.min(i + 10, lines.length)).join(" ");
81
+ if (/(?:authorization|cookie|credentials|auth)/i.test(nearby)) {
82
+ issues.push({
83
+ file: filepath,
84
+ line: i + 1,
85
+ issue: "CORS wildcard (*) with authentication",
86
+ severity: "high",
87
+ detail: "Access-Control-Allow-Origin: * combined with authentication bypasses same-origin policy protections",
88
+ });
89
+ }
90
+ else {
91
+ issues.push({
92
+ file: filepath,
93
+ line: i + 1,
94
+ issue: "CORS wildcard (*)",
95
+ severity: "medium",
96
+ detail: "CORS wildcard allows any origin — restrict to trusted domains unless truly public",
97
+ });
98
+ }
99
+ }
100
+ // CSRF token generated but never verified
101
+ if (/csrf[_-]?token|csrfToken|_csrf/i.test(line) && /generate|create|set|assign/i.test(line)) {
102
+ const fileContent = content;
103
+ if (!/csrf.*verif|verify.*csrf|csrf.*valid|csrfProtection|csurf/i.test(fileContent)) {
104
+ issues.push({
105
+ file: filepath,
106
+ line: i + 1,
107
+ issue: "CSRF token generated but never verified",
108
+ severity: "high",
109
+ detail: "CSRF tokens must be verified on the server side — generation alone provides no protection",
110
+ });
111
+ }
112
+ }
113
+ // Rate limiting that is too lenient
114
+ if (/rateLimit|rate[_-]?limit/i.test(line)) {
115
+ const context = lines.slice(i, Math.min(i + 5, lines.length)).join(" ");
116
+ const limitMatch = context.match(/(?:max|limit|windowMs|window)\s*[:=]\s*(\d+)/);
117
+ if (limitMatch) {
118
+ const val = parseInt(limitMatch[1], 10);
119
+ if (/max|limit/i.test(limitMatch[0]) && val >= 10000) {
120
+ issues.push({
121
+ file: filepath,
122
+ line: i + 1,
123
+ issue: "Ineffective rate limiting (limit too high)",
124
+ severity: "medium",
125
+ detail: `Rate limit of ${val} requests is too permissive to be protective`,
126
+ });
127
+ }
128
+ }
129
+ }
130
+ // Validation without authorization
131
+ if (/(?:validate|sanitize|check)\s*\(\s*(?:req|request)/.test(line) || /(?:joi|yup|zod)\s*\./.test(line)) {
132
+ if (/(?:body|params|query)/.test(line)) {
133
+ const nearby = lines.slice(Math.max(0, i - 10), Math.min(i + 10, lines.length)).join(" ");
134
+ if (!/(?:auth|authorize|isAuthenticated|requireAuth|passport|jwt|token|session)/.test(nearby)) {
135
+ issues.push({
136
+ file: filepath,
137
+ line: i + 1,
138
+ issue: "Input validation without authorization check",
139
+ severity: "medium",
140
+ detail: "Validating input without checking authorization — an unauthenticated user could send valid-looking data",
141
+ });
142
+ }
143
+ }
144
+ }
145
+ // Client-side only validation
146
+ if (/(?:disabled|readonly|hidden)\s*[=:]\s*(?:true|['"]true['"])/.test(line) &&
147
+ /(?:submit|form|action)/i.test(lines.slice(Math.max(0, i - 5), i + 5).join(" "))) {
148
+ issues.push({
149
+ file: filepath,
150
+ line: i + 1,
151
+ issue: "Client-side-only form restriction",
152
+ severity: "medium",
153
+ detail: "disabled/readonly/hidden form attributes can be bypassed — enforce restrictions server-side",
154
+ });
155
+ }
156
+ // Base64 used as encryption
157
+ if (/(?:btoa|atob|Buffer\.from\([^)]*,\s*['"]base64['"]|base64[_-]?encode|base64[_-]?decode)/i.test(line)) {
158
+ const nearby = lines.slice(Math.max(0, i - 3), i + 3).join(" ");
159
+ if (/(?:encrypt|secret|password|secure|protect|hide)/i.test(nearby)) {
160
+ issues.push({
161
+ file: filepath,
162
+ line: i + 1,
163
+ issue: "Base64 used as encryption",
164
+ severity: "high",
165
+ detail: "Base64 is encoding, not encryption — it provides zero confidentiality",
166
+ });
167
+ }
168
+ }
169
+ // SQL partial parameterization
170
+ if (/(?:query|execute|exec|raw)\s*\(/.test(line)) {
171
+ const context = lines.slice(i, Math.min(i + 3, lines.length)).join(" ");
172
+ if (/\$\{|\+\s*['"]/.test(context) && /[?$]/.test(context)) {
173
+ issues.push({
174
+ file: filepath,
175
+ line: i + 1,
176
+ issue: "Partially parameterized SQL query",
177
+ severity: "high",
178
+ detail: "Query mixes parameterized values with string concatenation — fully parameterize all inputs",
179
+ });
180
+ }
181
+ }
182
+ // Commented-out security controls
183
+ if (/^\s*\/\/\s*(?:app\.use\s*\(\s*(?:helmet|csrf|cors|rateLimit)|requireAuth|isAuthenticated|authorize)/.test(line)) {
184
+ issues.push({
185
+ file: filepath,
186
+ line: i + 1,
187
+ issue: "Commented-out security middleware",
188
+ severity: "high",
189
+ detail: "Security middleware is commented out — likely left disabled from debugging",
190
+ });
191
+ }
192
+ // JWT with 'none' algorithm
193
+ if (/algorithm[s]?\s*[:=]\s*\[?\s*['"]none['"]/i.test(line)) {
194
+ issues.push({
195
+ file: filepath,
196
+ line: i + 1,
197
+ issue: "JWT 'none' algorithm allowed",
198
+ severity: "high",
199
+ detail: "Allowing 'none' algorithm in JWT verification completely disables signature checking",
200
+ });
201
+ }
202
+ // Overly long JWT expiration
203
+ if (/expiresIn\s*[:=]\s*['"](\d+)([dh])['"]/.test(line)) {
204
+ const match = line.match(/expiresIn\s*[:=]\s*['"](\d+)([dh])['"]/);
205
+ if (match) {
206
+ const val = parseInt(match[1], 10);
207
+ const unit = match[2];
208
+ if ((unit === "d" && val > 30) || (unit === "h" && val > 720)) {
209
+ issues.push({
210
+ file: filepath,
211
+ line: i + 1,
212
+ issue: "JWT expiration too long",
213
+ severity: "medium",
214
+ detail: `Token expiration of ${val}${unit} is excessively long — shorter-lived tokens reduce blast radius of theft`,
215
+ });
216
+ }
217
+ }
218
+ }
219
+ }
220
+ return issues;
221
+ }
222
+ // ─── CLI ────────────────────────────────────────────────────────────────────
223
+ export function runSecurityTheater(argv) {
224
+ if (argv.includes("--help") || argv.includes("-h")) {
225
+ console.log(`
226
+ judges security-theater — Detect security-looking code that provides no protection
227
+
228
+ Usage:
229
+ judges security-theater [dir]
230
+ judges security-theater src/ --format json
231
+
232
+ Options:
233
+ [dir] Directory to scan (default: .)
234
+ --format json JSON output
235
+ --help, -h Show this help
236
+
237
+ Checks: weak password hashing, hardcoded keys/IVs, wildcard CORS with auth,
238
+ unverified CSRF tokens, ineffective rate limiting, validation without auth,
239
+ base64-as-encryption, partial SQL parameterization, commented-out security
240
+ middleware, JWT 'none' algorithm, overly long JWT expiration.
241
+ `);
242
+ return;
243
+ }
244
+ const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
245
+ const dir = argv.find((a) => !a.startsWith("-") && argv.indexOf(a) > 0) || ".";
246
+ const files = collectFiles(dir);
247
+ const allIssues = [];
248
+ for (const f of files)
249
+ allIssues.push(...analyzeFile(f));
250
+ const highCount = allIssues.filter((i) => i.severity === "high").length;
251
+ const medCount = allIssues.filter((i) => i.severity === "medium").length;
252
+ const score = Math.max(0, 100 - highCount * 15 - medCount * 7 - allIssues.filter((i) => i.severity === "low").length * 3);
253
+ if (format === "json") {
254
+ console.log(JSON.stringify({
255
+ issues: allIssues,
256
+ score,
257
+ summary: { high: highCount, medium: medCount, total: allIssues.length },
258
+ timestamp: new Date().toISOString(),
259
+ }, null, 2));
260
+ }
261
+ else {
262
+ const badge = score >= 80 ? "✅ GENUINE SECURITY" : score >= 50 ? "⚠️ THEATER DETECTED" : "❌ SECURITY THEATER";
263
+ console.log(`\n Security-Theater: ${badge} (${score}/100)\n ─────────────────────────────`);
264
+ if (allIssues.length === 0) {
265
+ console.log(" No security theater detected.\n");
266
+ return;
267
+ }
268
+ for (const issue of allIssues.slice(0, 25)) {
269
+ const icon = issue.severity === "high" ? "🔴" : issue.severity === "medium" ? "🟡" : "🔵";
270
+ console.log(` ${icon} ${issue.issue}`);
271
+ console.log(` ${issue.file}:${issue.line}`);
272
+ console.log(` ${issue.detail}`);
273
+ }
274
+ if (allIssues.length > 25)
275
+ console.log(` ... and ${allIssues.length - 25} more`);
276
+ console.log(`\n Total: ${allIssues.length} | High: ${highCount} | Medium: ${medCount} | Score: ${score}/100\n`);
277
+ }
278
+ }
279
+ //# sourceMappingURL=security-theater.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"security-theater.js","sourceRoot":"","sources":["../../src/commands/security-theater.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Stale-pattern — identify outdated idioms when modern alternatives exist.
3
+ */
4
+ export declare function runStalePattern(argv: string[]): void;
5
+ //# sourceMappingURL=stale-pattern.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stale-pattern.d.ts","sourceRoot":"","sources":["../../src/commands/stale-pattern.ts"],"names":[],"mappings":"AAAA;;GAEG;AA6QH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAiEpD"}
@@ -0,0 +1,294 @@
1
+ /**
2
+ * Stale-pattern — identify outdated idioms when modern alternatives exist.
3
+ */
4
+ import { readFileSync, readdirSync, statSync } from "fs";
5
+ import { join, extname } from "path";
6
+ // ─── File Collection ────────────────────────────────────────────────────────
7
+ const CODE_EXTS = new Set([".ts", ".tsx", ".js", ".jsx"]);
8
+ function collectFiles(dir, max = 300) {
9
+ const files = [];
10
+ function walk(d) {
11
+ if (files.length >= max)
12
+ return;
13
+ let entries;
14
+ try {
15
+ entries = readdirSync(d);
16
+ }
17
+ catch {
18
+ return;
19
+ }
20
+ for (const e of entries) {
21
+ if (files.length >= max)
22
+ return;
23
+ if (e.startsWith(".") || e === "node_modules" || e === "dist" || e === "build")
24
+ continue;
25
+ const full = join(d, e);
26
+ try {
27
+ if (statSync(full).isDirectory())
28
+ walk(full);
29
+ else if (CODE_EXTS.has(extname(full)))
30
+ files.push(full);
31
+ }
32
+ catch {
33
+ /* skip */
34
+ }
35
+ }
36
+ }
37
+ walk(dir);
38
+ return files;
39
+ }
40
+ const STALE_PATTERNS = [
41
+ // Node.js deprecated APIs
42
+ {
43
+ regex: /new\s+Buffer\s*\(/,
44
+ name: "new Buffer()",
45
+ severity: "high",
46
+ modern: "Buffer.from() / Buffer.alloc()",
47
+ detail: "new Buffer() is deprecated due to security issues (uninitialized memory)",
48
+ },
49
+ {
50
+ regex: /url\.parse\s*\(/,
51
+ name: "url.parse()",
52
+ severity: "medium",
53
+ modern: "new URL()",
54
+ detail: "url.parse() is deprecated — use the WHATWG URL API",
55
+ },
56
+ {
57
+ regex: /require\s*\(\s*['"]domain['"]/,
58
+ name: "domain module",
59
+ severity: "medium",
60
+ modern: "AsyncLocalStorage or structured error handling",
61
+ detail: "The domain module is deprecated and should not be used for error handling",
62
+ },
63
+ {
64
+ regex: /require\s*\(\s*['"]punycode['"]/,
65
+ name: "punycode module",
66
+ severity: "low",
67
+ modern: "url.domainToASCII() / url.domainToUnicode()",
68
+ detail: "Built-in punycode module is deprecated",
69
+ },
70
+ {
71
+ regex: /fs\.exists\s*\(/,
72
+ name: "fs.exists()",
73
+ severity: "medium",
74
+ modern: "fs.access() or fs.stat()",
75
+ detail: "fs.exists() is deprecated — use fs.access() for existence checks",
76
+ },
77
+ {
78
+ regex: /path\._makeLong\s*\(/,
79
+ name: "path._makeLong()",
80
+ severity: "low",
81
+ modern: "path.toNamespacedPath()",
82
+ detail: "path._makeLong() is an internal API — use toNamespacedPath()",
83
+ },
84
+ // Callback patterns vs async/await
85
+ {
86
+ regex: /(\w+)\s*\(\s*(?:function\s*\(|[(]\s*)\s*(?:err|error)\s*,\s*(?:data|result|res|body|response)/,
87
+ name: "Error-first callback",
88
+ severity: "medium",
89
+ modern: "async/await with promises",
90
+ detail: "Error-first callbacks should be replaced with async/await for readability and error handling",
91
+ },
92
+ {
93
+ regex: /\.then\s*\(\s*(?:function|[(])\s*\w*\s*[)]*\s*\{[^}]*\.then\s*\(/,
94
+ name: "Nested .then() chains",
95
+ severity: "medium",
96
+ modern: "async/await",
97
+ detail: "Nested promise chains are hard to read — use async/await",
98
+ },
99
+ // Old JavaScript patterns
100
+ {
101
+ regex: /var\s+\w+\s*=/,
102
+ name: "var declaration",
103
+ severity: "low",
104
+ modern: "const / let",
105
+ detail: "var has function scoping issues — use const or let for block scoping",
106
+ },
107
+ {
108
+ regex: /typeof\s+\w+\s*[!=]==?\s*['"]undefined['"]/,
109
+ name: "typeof undefined check",
110
+ severity: "low",
111
+ modern: "Optional chaining (?.) or nullish coalescing (??)",
112
+ detail: "typeof undefined checks can often be replaced with optional chaining",
113
+ },
114
+ {
115
+ regex: /\.apply\s*\(\s*(?:null|this)\s*,\s*arguments\s*\)/,
116
+ name: ".apply(null, arguments)",
117
+ severity: "low",
118
+ modern: "...rest parameters and spread",
119
+ detail: "Use rest parameters and spread syntax instead of arguments object",
120
+ },
121
+ {
122
+ regex: /arguments\s*\[\s*\d+\s*\]/,
123
+ name: "arguments[] indexing",
124
+ severity: "low",
125
+ modern: "Named parameters or rest (...args)",
126
+ detail: "The arguments object is a legacy feature — use named or rest parameters",
127
+ },
128
+ // Old testing patterns
129
+ {
130
+ regex: /(?:it|test)\s*\(\s*['"][^'"]+['"]\s*,\s*function\s*\(\s*done\s*\)/,
131
+ name: "done() callback test",
132
+ severity: "medium",
133
+ modern: "async test functions",
134
+ detail: "Test done() callbacks are error-prone — use async/await in tests",
135
+ },
136
+ {
137
+ regex: /\.should\.\w+/,
138
+ name: "should-style assertions",
139
+ severity: "low",
140
+ modern: "expect() or assert()",
141
+ detail: "Should-style assertions modify Object.prototype — use expect() or assert()",
142
+ },
143
+ // React class components
144
+ {
145
+ regex: /class\s+\w+\s+extends\s+(?:React\.)?(?:Component|PureComponent)\s*[<{]/,
146
+ name: "React class component",
147
+ severity: "medium",
148
+ modern: "Function components with hooks",
149
+ detail: "React class components are legacy — prefer function components with hooks",
150
+ },
151
+ {
152
+ regex: /componentWillMount\s*\(/,
153
+ name: "componentWillMount",
154
+ severity: "high",
155
+ modern: "useEffect hook",
156
+ detail: "componentWillMount is removed in React 18 — use useEffect",
157
+ },
158
+ {
159
+ regex: /componentWillReceiveProps\s*\(/,
160
+ name: "componentWillReceiveProps",
161
+ severity: "high",
162
+ modern: "getDerivedStateFromProps or useEffect",
163
+ detail: "componentWillReceiveProps is removed in React 18",
164
+ },
165
+ // Promise constructor anti-pattern
166
+ {
167
+ regex: /new\s+Promise\s*\(\s*(?:async|[(]\s*(?:resolve|reject))/,
168
+ name: "Async executor in Promise",
169
+ severity: "medium",
170
+ modern: "Direct async function",
171
+ detail: "async function inside Promise executor is an anti-pattern — errors won't reject the promise",
172
+ },
173
+ // Old module patterns
174
+ {
175
+ regex: /module\.exports\s*=/,
176
+ name: "CommonJS module.exports",
177
+ severity: "low",
178
+ modern: "ES module export",
179
+ detail: "CommonJS is legacy in TypeScript — use ES module exports",
180
+ },
181
+ {
182
+ regex: /exports\.\w+\s*=/,
183
+ name: "CommonJS exports.x",
184
+ severity: "low",
185
+ modern: "ES module named export",
186
+ detail: "CommonJS exports are legacy in TypeScript — use ES module named exports",
187
+ },
188
+ // Deprecated string methods
189
+ {
190
+ regex: /\.substr\s*\(/,
191
+ name: ".substr()",
192
+ severity: "low",
193
+ modern: ".substring() or .slice()",
194
+ detail: ".substr() is deprecated in favor of .substring() or .slice()",
195
+ },
196
+ // Old error handling
197
+ {
198
+ regex: /process\.on\s*\(\s*['"]uncaughtException['"]/,
199
+ name: "uncaughtException handler",
200
+ severity: "medium",
201
+ modern: "Structured error handling or process.on('unhandledRejection')",
202
+ detail: "Catching uncaughtException and continuing is dangerous — fix the root cause or exit",
203
+ },
204
+ ];
205
+ // ─── Analysis ───────────────────────────────────────────────────────────────
206
+ function analyzeFile(filepath) {
207
+ const issues = [];
208
+ let content;
209
+ try {
210
+ content = readFileSync(filepath, "utf-8");
211
+ }
212
+ catch {
213
+ return issues;
214
+ }
215
+ const lines = content.split("\n");
216
+ for (let i = 0; i < lines.length; i++) {
217
+ const line = lines[i];
218
+ // Skip comments
219
+ const trimmed = line.trim();
220
+ if (trimmed.startsWith("//") || trimmed.startsWith("*") || trimmed.startsWith("/*"))
221
+ continue;
222
+ for (const pattern of STALE_PATTERNS) {
223
+ if (pattern.regex.test(line)) {
224
+ issues.push({
225
+ file: filepath,
226
+ line: i + 1,
227
+ pattern: pattern.name,
228
+ severity: pattern.severity,
229
+ detail: pattern.detail,
230
+ modernAlternative: pattern.modern,
231
+ });
232
+ }
233
+ }
234
+ }
235
+ return issues;
236
+ }
237
+ // ─── CLI ────────────────────────────────────────────────────────────────────
238
+ export function runStalePattern(argv) {
239
+ if (argv.includes("--help") || argv.includes("-h")) {
240
+ console.log(`
241
+ judges stale-pattern — Identify outdated idioms when modern alternatives exist
242
+
243
+ Usage:
244
+ judges stale-pattern [dir]
245
+ judges stale-pattern src/ --format json
246
+
247
+ Options:
248
+ [dir] Directory to scan (default: .)
249
+ --format json JSON output
250
+ --help, -h Show this help
251
+
252
+ Checks: deprecated Node APIs (new Buffer, url.parse, fs.exists), callback patterns,
253
+ var declarations, old testing patterns, React class components, Promise anti-patterns,
254
+ CommonJS modules, deprecated string methods, old error handling.
255
+ `);
256
+ return;
257
+ }
258
+ const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
259
+ const dir = argv.find((a) => !a.startsWith("-") && argv.indexOf(a) > 0) || ".";
260
+ const files = collectFiles(dir);
261
+ const allIssues = [];
262
+ for (const f of files)
263
+ allIssues.push(...analyzeFile(f));
264
+ const highCount = allIssues.filter((i) => i.severity === "high").length;
265
+ const medCount = allIssues.filter((i) => i.severity === "medium").length;
266
+ const score = Math.max(0, 100 - highCount * 10 - medCount * 5 - allIssues.filter((i) => i.severity === "low").length * 2);
267
+ if (format === "json") {
268
+ console.log(JSON.stringify({
269
+ issues: allIssues,
270
+ score,
271
+ summary: { high: highCount, medium: medCount, total: allIssues.length },
272
+ timestamp: new Date().toISOString(),
273
+ }, null, 2));
274
+ }
275
+ else {
276
+ const badge = score >= 80 ? "✅ MODERN" : score >= 50 ? "⚠️ DATED" : "❌ STALE";
277
+ console.log(`\n Stale-Pattern: ${badge} (${score}/100)\n ─────────────────────────────`);
278
+ if (allIssues.length === 0) {
279
+ console.log(" No stale patterns detected.\n");
280
+ return;
281
+ }
282
+ for (const issue of allIssues.slice(0, 25)) {
283
+ const icon = issue.severity === "high" ? "🔴" : issue.severity === "medium" ? "🟡" : "🔵";
284
+ console.log(` ${icon} ${issue.pattern}`);
285
+ console.log(` ${issue.file}:${issue.line}`);
286
+ console.log(` ${issue.detail}`);
287
+ console.log(` ↳ Use: ${issue.modernAlternative}`);
288
+ }
289
+ if (allIssues.length > 25)
290
+ console.log(` ... and ${allIssues.length - 25} more`);
291
+ console.log(`\n Total: ${allIssues.length} | High: ${highCount} | Medium: ${medCount} | Score: ${score}/100\n`);
292
+ }
293
+ }
294
+ //# sourceMappingURL=stale-pattern.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stale-pattern.js","sourceRoot":"","sources":["../../src/commands/stale-pattern.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kevinrabun/judges",
3
- "version": "3.59.0",
3
+ "version": "3.60.0",
4
4
  "description": "45 specialized judges that evaluate AI-generated code for security, cost, and quality.",
5
5
  "mcpName": "io.github.KevinRabun/judges",
6
6
  "type": "module",
package/server.json CHANGED
@@ -7,12 +7,12 @@
7
7
  "url": "https://github.com/kevinrabun/judges",
8
8
  "source": "github"
9
9
  },
10
- "version": "3.59.0",
10
+ "version": "3.60.0",
11
11
  "packages": [
12
12
  {
13
13
  "registryType": "npm",
14
14
  "identifier": "@kevinrabun/judges",
15
- "version": "3.59.0",
15
+ "version": "3.60.0",
16
16
  "transport": {
17
17
  "type": "stdio"
18
18
  }