@kevinrabun/judges 3.56.0 → 3.58.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 +24 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +112 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/assertion-density.d.ts +5 -0
- package/dist/commands/assertion-density.d.ts.map +1 -0
- package/dist/commands/assertion-density.js +264 -0
- package/dist/commands/assertion-density.js.map +1 -0
- package/dist/commands/async-safety.d.ts +5 -0
- package/dist/commands/async-safety.d.ts.map +1 -0
- package/dist/commands/async-safety.js +267 -0
- package/dist/commands/async-safety.js.map +1 -0
- package/dist/commands/cache-audit.d.ts +5 -0
- package/dist/commands/cache-audit.d.ts.map +1 -0
- package/dist/commands/cache-audit.js +220 -0
- package/dist/commands/cache-audit.js.map +1 -0
- package/dist/commands/clone-detect.d.ts +5 -0
- package/dist/commands/clone-detect.d.ts.map +1 -0
- package/dist/commands/clone-detect.js +233 -0
- package/dist/commands/clone-detect.js.map +1 -0
- package/dist/commands/comment-drift.d.ts +5 -0
- package/dist/commands/comment-drift.d.ts.map +1 -0
- package/dist/commands/comment-drift.js +229 -0
- package/dist/commands/comment-drift.js.map +1 -0
- package/dist/commands/contract-verify.d.ts +5 -0
- package/dist/commands/contract-verify.d.ts.map +1 -0
- package/dist/commands/contract-verify.js +317 -0
- package/dist/commands/contract-verify.js.map +1 -0
- package/dist/commands/dead-code-detect.d.ts +5 -0
- package/dist/commands/dead-code-detect.d.ts.map +1 -0
- package/dist/commands/dead-code-detect.js +256 -0
- package/dist/commands/dead-code-detect.js.map +1 -0
- package/dist/commands/encoding-safety.d.ts +5 -0
- package/dist/commands/encoding-safety.d.ts.map +1 -0
- package/dist/commands/encoding-safety.js +276 -0
- package/dist/commands/encoding-safety.js.map +1 -0
- package/dist/commands/error-ux.d.ts +5 -0
- package/dist/commands/error-ux.d.ts.map +1 -0
- package/dist/commands/error-ux.js +253 -0
- package/dist/commands/error-ux.js.map +1 -0
- package/dist/commands/event-leak.d.ts +5 -0
- package/dist/commands/event-leak.d.ts.map +1 -0
- package/dist/commands/event-leak.js +263 -0
- package/dist/commands/event-leak.js.map +1 -0
- package/dist/commands/idempotency-audit.d.ts +5 -0
- package/dist/commands/idempotency-audit.d.ts.map +1 -0
- package/dist/commands/idempotency-audit.js +223 -0
- package/dist/commands/idempotency-audit.js.map +1 -0
- package/dist/commands/input-guard.d.ts +5 -0
- package/dist/commands/input-guard.d.ts.map +1 -0
- package/dist/commands/input-guard.js +256 -0
- package/dist/commands/input-guard.js.map +1 -0
- package/dist/commands/privilege-path.d.ts +5 -0
- package/dist/commands/privilege-path.d.ts.map +1 -0
- package/dist/commands/privilege-path.js +234 -0
- package/dist/commands/privilege-path.js.map +1 -0
- package/dist/commands/state-integrity.d.ts +5 -0
- package/dist/commands/state-integrity.d.ts.map +1 -0
- package/dist/commands/state-integrity.js +284 -0
- package/dist/commands/state-integrity.js.map +1 -0
- package/dist/commands/timeout-audit.d.ts +5 -0
- package/dist/commands/timeout-audit.d.ts.map +1 -0
- package/dist/commands/timeout-audit.js +211 -0
- package/dist/commands/timeout-audit.js.map +1 -0
- package/dist/commands/type-boundary.d.ts +5 -0
- package/dist/commands/type-boundary.d.ts.map +1 -0
- package/dist/commands/type-boundary.js +236 -0
- package/dist/commands/type-boundary.js.map +1 -0
- package/package.json +1 -1
- package/server.json +2 -2
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error UX — audit user-facing error messages for actionability, consistency, and info leakage.
|
|
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", ".rs"]);
|
|
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
|
+
// Skip test and fixture files
|
|
52
|
+
if (/\.test\.|\.spec\.|__test__|fixture|mock/i.test(filepath))
|
|
53
|
+
return issues;
|
|
54
|
+
for (let i = 0; i < lines.length; i++) {
|
|
55
|
+
const line = lines[i];
|
|
56
|
+
// Generic unhelpful error messages
|
|
57
|
+
if (/(?:throw\s+new\s+Error|res\.status.*\.(?:json|send))\s*\(\s*['"]([^'"]+)['"]\s*\)/.test(line)) {
|
|
58
|
+
const msgMatch = line.match(/['"]([^'"]+)['"]/);
|
|
59
|
+
if (msgMatch) {
|
|
60
|
+
const msg = msgMatch[1];
|
|
61
|
+
if (/^(?:error|something went wrong|an error occurred|internal error|unknown error|bad request|failed|invalid)$/i.test(msg.trim())) {
|
|
62
|
+
issues.push({
|
|
63
|
+
file: filepath,
|
|
64
|
+
line: i + 1,
|
|
65
|
+
issue: "Generic error message",
|
|
66
|
+
severity: "medium",
|
|
67
|
+
detail: `"${msg}" — provide actionable guidance: what went wrong and how to fix it`,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
// Stack trace leaked to client
|
|
73
|
+
if (/(?:res\.(?:json|send)|response\.(?:json|send))\s*\(/.test(line)) {
|
|
74
|
+
const block = lines.slice(i, Math.min(i + 5, lines.length)).join("\n");
|
|
75
|
+
if (/\.stack|stackTrace|stack_trace|err\.message|error\.message/i.test(block)) {
|
|
76
|
+
if (!/production|NODE_ENV|process\.env/i.test(block)) {
|
|
77
|
+
issues.push({
|
|
78
|
+
file: filepath,
|
|
79
|
+
line: i + 1,
|
|
80
|
+
issue: "Stack trace may leak to client",
|
|
81
|
+
severity: "high",
|
|
82
|
+
detail: "Error details sent in response without environment check — may expose internals in production",
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// Internal paths leaked in error messages
|
|
88
|
+
if (/(?:throw|console\.error|res\..*(?:json|send))\s*\(/.test(line)) {
|
|
89
|
+
if (/(?:\/home\/|\/var\/|\/usr\/|C:\\|D:\\|\/opt\/|__dirname|__filename)/i.test(line)) {
|
|
90
|
+
issues.push({
|
|
91
|
+
file: filepath,
|
|
92
|
+
line: i + 1,
|
|
93
|
+
issue: "Internal file path in error output",
|
|
94
|
+
severity: "medium",
|
|
95
|
+
detail: "Server filesystem path in error — reveals deployment structure to attacker",
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// SQL/DB error details sent to client
|
|
100
|
+
if (/catch\s*\(/.test(line)) {
|
|
101
|
+
const catchBlock = lines.slice(i, Math.min(i + 10, lines.length)).join("\n");
|
|
102
|
+
if (/(?:sql|query|database|ECONNREFUSED|ETIMEDOUT)/i.test(catchBlock)) {
|
|
103
|
+
if (/res\.(?:json|send|status)|response\.(?:json|send)/i.test(catchBlock)) {
|
|
104
|
+
if (/err\.message|error\.message|e\.message/i.test(catchBlock)) {
|
|
105
|
+
issues.push({
|
|
106
|
+
file: filepath,
|
|
107
|
+
line: i + 1,
|
|
108
|
+
issue: "Database error details sent to client",
|
|
109
|
+
severity: "high",
|
|
110
|
+
detail: "DB error message forwarded to response — may expose table names, SQL syntax, or connection info",
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// Error message with jargon (internal codes exposed)
|
|
117
|
+
if (/(?:throw\s+new\s+Error|\.send|\.json)\s*\(/.test(line)) {
|
|
118
|
+
const msgMatch = line.match(/['"]([^'"]{10,})['"]/);
|
|
119
|
+
if (msgMatch) {
|
|
120
|
+
const msg = msgMatch[1];
|
|
121
|
+
if (/(?:ENOENT|EACCES|EPERM|SIGTERM|ENOMEM|OOM|segfault|null pointer|NullReferenceException)/i.test(msg)) {
|
|
122
|
+
issues.push({
|
|
123
|
+
file: filepath,
|
|
124
|
+
line: i + 1,
|
|
125
|
+
issue: "Technical jargon in user-facing error",
|
|
126
|
+
severity: "medium",
|
|
127
|
+
detail: `"${msg.slice(0, 50)}" — replace system-level jargon with plain language for end users`,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
// Empty catch blocks (silent failures)
|
|
133
|
+
if (/catch\s*\(\s*\w*\s*\)\s*\{\s*\}/.test(line) || /catch\s*\(\s*\w*\s*\)\s*\{\s*\/\//i.test(line)) {
|
|
134
|
+
issues.push({
|
|
135
|
+
file: filepath,
|
|
136
|
+
line: i + 1,
|
|
137
|
+
issue: "Silent error swallowing",
|
|
138
|
+
severity: "medium",
|
|
139
|
+
detail: "Empty catch block hides failures — log the error or surface a user-friendly message",
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
// HTTP status code mismatch
|
|
143
|
+
if (/res\.status\s*\(\s*(\d+)\s*\)/.test(line)) {
|
|
144
|
+
const statusMatch = line.match(/res\.status\s*\(\s*(\d+)\s*\)/);
|
|
145
|
+
const msgMatch2 = line.match(/['"]([^'"]+)['"]/);
|
|
146
|
+
if (statusMatch && msgMatch2) {
|
|
147
|
+
const status = parseInt(statusMatch[1], 10);
|
|
148
|
+
const msg = msgMatch2[1].toLowerCase();
|
|
149
|
+
if (status === 200 && /error|fail|invalid|denied/i.test(msg)) {
|
|
150
|
+
issues.push({
|
|
151
|
+
file: filepath,
|
|
152
|
+
line: i + 1,
|
|
153
|
+
issue: "Success status with error message",
|
|
154
|
+
severity: "medium",
|
|
155
|
+
detail: `HTTP 200 with error message "${msg.slice(0, 40)}" — use appropriate 4xx/5xx status code`,
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
if (status === 500 && /not found|unauthorized|forbidden/i.test(msg)) {
|
|
159
|
+
issues.push({
|
|
160
|
+
file: filepath,
|
|
161
|
+
line: i + 1,
|
|
162
|
+
issue: "Incorrect HTTP status code",
|
|
163
|
+
severity: "low",
|
|
164
|
+
detail: `HTTP 500 for "${msg.slice(0, 30)}" — use 404/401/403 for client errors`,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
// Error without remediation hint
|
|
170
|
+
if (/throw\s+new\s+Error\s*\(\s*['"]([^'"]{15,})['"]\s*\)/.test(line)) {
|
|
171
|
+
const errMsg = line.match(/throw\s+new\s+Error\s*\(\s*['"]([^'"]+)['"]\s*\)/)?.[1] || "";
|
|
172
|
+
if (!/please|try|check|ensure|make sure|verify|see|refer|visit|use|run|set|configure|install/i.test(errMsg)) {
|
|
173
|
+
issues.push({
|
|
174
|
+
file: filepath,
|
|
175
|
+
line: i + 1,
|
|
176
|
+
issue: "Error without remediation hint",
|
|
177
|
+
severity: "low",
|
|
178
|
+
detail: `"${errMsg.slice(0, 50)}" — add guidance on how the user can resolve the issue`,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
// Inconsistent error format (mix of throw/console.error/process.exit)
|
|
183
|
+
if (/process\.exit\s*\(\s*1\s*\)/.test(line)) {
|
|
184
|
+
const block = lines.slice(Math.max(0, i - 3), i + 1).join("\n");
|
|
185
|
+
if (!/console\.error|console\.log|logger|log\./i.test(block)) {
|
|
186
|
+
issues.push({
|
|
187
|
+
file: filepath,
|
|
188
|
+
line: i + 1,
|
|
189
|
+
issue: "process.exit without error message",
|
|
190
|
+
severity: "medium",
|
|
191
|
+
detail: "Process exits with code 1 but no error message — user sees nothing about what went wrong",
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return issues;
|
|
197
|
+
}
|
|
198
|
+
// ─── CLI ────────────────────────────────────────────────────────────────────
|
|
199
|
+
export function runErrorUx(argv) {
|
|
200
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
201
|
+
console.log(`
|
|
202
|
+
judges error-ux — Audit user-facing error messages for quality and safety
|
|
203
|
+
|
|
204
|
+
Usage:
|
|
205
|
+
judges error-ux [dir]
|
|
206
|
+
judges error-ux src/ --format json
|
|
207
|
+
|
|
208
|
+
Options:
|
|
209
|
+
[dir] Directory to scan (default: .)
|
|
210
|
+
--format json JSON output
|
|
211
|
+
--help, -h Show this help
|
|
212
|
+
|
|
213
|
+
Checks: generic messages, stack trace leaks, internal path exposure, DB error forwarding,
|
|
214
|
+
jargon in user errors, silent catch blocks, HTTP status mismatches, missing remediation hints.
|
|
215
|
+
`);
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
|
|
219
|
+
const dir = argv.find((a) => !a.startsWith("-") && argv.indexOf(a) > 0) || ".";
|
|
220
|
+
const files = collectFiles(dir);
|
|
221
|
+
const allIssues = [];
|
|
222
|
+
for (const f of files)
|
|
223
|
+
allIssues.push(...analyzeFile(f));
|
|
224
|
+
const highCount = allIssues.filter((i) => i.severity === "high").length;
|
|
225
|
+
const medCount = allIssues.filter((i) => i.severity === "medium").length;
|
|
226
|
+
const score = Math.max(0, 100 - highCount * 8 - medCount * 3);
|
|
227
|
+
if (format === "json") {
|
|
228
|
+
console.log(JSON.stringify({
|
|
229
|
+
issues: allIssues,
|
|
230
|
+
score,
|
|
231
|
+
summary: { high: highCount, medium: medCount, total: allIssues.length },
|
|
232
|
+
timestamp: new Date().toISOString(),
|
|
233
|
+
}, null, 2));
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
const badge = score >= 80 ? "✅ CLEAR" : score >= 50 ? "⚠️ MIXED" : "❌ POOR";
|
|
237
|
+
console.log(`\n Error UX Quality: ${badge} (${score}/100)\n ─────────────────────────────`);
|
|
238
|
+
if (allIssues.length === 0) {
|
|
239
|
+
console.log(" No error UX issues detected.\n");
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
for (const issue of allIssues.slice(0, 25)) {
|
|
243
|
+
const icon = issue.severity === "high" ? "🔴" : issue.severity === "medium" ? "🟡" : "🔵";
|
|
244
|
+
console.log(` ${icon} ${issue.issue}`);
|
|
245
|
+
console.log(` ${issue.file}:${issue.line}`);
|
|
246
|
+
console.log(` ${issue.detail}`);
|
|
247
|
+
}
|
|
248
|
+
if (allIssues.length > 25)
|
|
249
|
+
console.log(` ... and ${allIssues.length - 25} more`);
|
|
250
|
+
console.log(`\n Total: ${allIssues.length} | High: ${highCount} | Medium: ${medCount} | Score: ${score}/100\n`);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
//# sourceMappingURL=error-ux.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-ux.js","sourceRoot":"","sources":["../../src/commands/error-ux.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-leak.d.ts","sourceRoot":"","sources":["../../src/commands/event-leak.ts"],"names":[],"mappings":"AAAA;;GAEG;AAwOH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CA+DjD"}
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Event leak — detect orphaned event listeners, unsubscribed observables, dangling async handles.
|
|
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
|
+
// ─── 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
|
+
const fullText = content;
|
|
52
|
+
for (let i = 0; i < lines.length; i++) {
|
|
53
|
+
const line = lines[i];
|
|
54
|
+
// addEventListener without removeEventListener
|
|
55
|
+
if (/\.addEventListener\s*\(/.test(line)) {
|
|
56
|
+
const eventMatch = line.match(/addEventListener\s*\(\s*['"](\w+)['"]/);
|
|
57
|
+
if (eventMatch) {
|
|
58
|
+
const event = eventMatch[1];
|
|
59
|
+
if (!fullText.includes(`removeEventListener`) || !fullText.includes(`'${event}'`)) {
|
|
60
|
+
// Check for AbortController signal
|
|
61
|
+
const block = lines.slice(i, Math.min(i + 3, lines.length)).join("\n");
|
|
62
|
+
if (!/signal|AbortController|once:\s*true/i.test(block)) {
|
|
63
|
+
issues.push({
|
|
64
|
+
file: filepath,
|
|
65
|
+
line: i + 1,
|
|
66
|
+
issue: "addEventListener without cleanup",
|
|
67
|
+
severity: "high",
|
|
68
|
+
detail: `'${event}' listener added but no removeEventListener found — memory leak risk`,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// .on() without .off() / .removeListener()
|
|
75
|
+
if (/\.on\s*\(\s*['"](\w+)['"]/.test(line) && !/\.once\s*\(/.test(line)) {
|
|
76
|
+
const eventMatch = line.match(/\.on\s*\(\s*['"](\w+)['"]/);
|
|
77
|
+
if (eventMatch) {
|
|
78
|
+
const event = eventMatch[1];
|
|
79
|
+
if (!/\.off\(|\.removeListener\(|\.removeAllListeners\(/i.test(fullText) ||
|
|
80
|
+
!new RegExp(`(?:\\.off|\\.removeListener)\\s*\\(\\s*['"]${event}['"]`).test(fullText)) {
|
|
81
|
+
issues.push({
|
|
82
|
+
file: filepath,
|
|
83
|
+
line: i + 1,
|
|
84
|
+
issue: "Event emitter .on() without .off()",
|
|
85
|
+
severity: "medium",
|
|
86
|
+
detail: `'${event}' listener registered but no corresponding .off() or .removeListener()`,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
// Observable subscribe without unsubscribe
|
|
92
|
+
if (/\.subscribe\s*\(/.test(line)) {
|
|
93
|
+
const block = lines.slice(i, Math.min(i + 10, lines.length)).join("\n");
|
|
94
|
+
if (!/unsubscribe|takeUntil|take\(|first\(\)|pipe.*take|subscription.*=|\.add\(/i.test(block) &&
|
|
95
|
+
!/unsubscribe|takeUntil|ngOnDestroy|componentWillUnmount|useEffect.*return/i.test(fullText)) {
|
|
96
|
+
issues.push({
|
|
97
|
+
file: filepath,
|
|
98
|
+
line: i + 1,
|
|
99
|
+
issue: "Observable subscribe without unsubscribe",
|
|
100
|
+
severity: "high",
|
|
101
|
+
detail: "Subscription not cleaned up — will leak memory and may trigger after component unmounts",
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// setInterval without clearInterval
|
|
106
|
+
if (/setInterval\s*\(/.test(line)) {
|
|
107
|
+
if (!/clearInterval/i.test(fullText)) {
|
|
108
|
+
issues.push({
|
|
109
|
+
file: filepath,
|
|
110
|
+
line: i + 1,
|
|
111
|
+
issue: "setInterval without clearInterval",
|
|
112
|
+
severity: "high",
|
|
113
|
+
detail: "Interval runs forever — add cleanup in destructor/unmount/close handler",
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
// setTimeout stored but never cleared
|
|
118
|
+
if (/setTimeout\s*\(/.test(line)) {
|
|
119
|
+
const assignMatch = line.match(/(?:const|let|var)\s+(\w+)\s*=\s*setTimeout/);
|
|
120
|
+
if (assignMatch) {
|
|
121
|
+
const varName = assignMatch[1];
|
|
122
|
+
if (!fullText.includes(`clearTimeout(${varName})`)) {
|
|
123
|
+
issues.push({
|
|
124
|
+
file: filepath,
|
|
125
|
+
line: i + 1,
|
|
126
|
+
issue: "setTimeout assigned but never cleared",
|
|
127
|
+
severity: "low",
|
|
128
|
+
detail: `Timer '${varName}' stored but clearTimeout never called — may fire after disposal`,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
// WebSocket/SSE without close handler
|
|
134
|
+
if (/new WebSocket\s*\(|new EventSource\s*\(/.test(line)) {
|
|
135
|
+
const block = lines.slice(i, Math.min(i + 15, lines.length)).join("\n");
|
|
136
|
+
if (!/\.close\s*\(|onclose|\.addEventListener.*close/i.test(block)) {
|
|
137
|
+
issues.push({
|
|
138
|
+
file: filepath,
|
|
139
|
+
line: i + 1,
|
|
140
|
+
issue: "WebSocket/SSE without close handler",
|
|
141
|
+
severity: "medium",
|
|
142
|
+
detail: "Connection opened but no close handler — resource leak on navigation or unmount",
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
// React useEffect without cleanup return
|
|
147
|
+
if (/useEffect\s*\(\s*\(\s*\)\s*=>\s*\{/.test(line)) {
|
|
148
|
+
// Find the matching effect body
|
|
149
|
+
let depth = 0;
|
|
150
|
+
let effectEnd = i;
|
|
151
|
+
for (let j = i; j < Math.min(i + 30, lines.length); j++) {
|
|
152
|
+
for (const ch of lines[j]) {
|
|
153
|
+
if (ch === "{")
|
|
154
|
+
depth++;
|
|
155
|
+
if (ch === "}")
|
|
156
|
+
depth--;
|
|
157
|
+
}
|
|
158
|
+
if (depth <= 0) {
|
|
159
|
+
effectEnd = j;
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
const effectBody = lines.slice(i, effectEnd + 1).join("\n");
|
|
164
|
+
const hasSideEffect = /addEventListener|\.on\(|subscribe|setInterval|setTimeout|fetch|WebSocket/i.test(effectBody);
|
|
165
|
+
const hasCleanup = /return\s*\(\s*\)\s*=>|return\s*\(\)\s*\{|return\s*function/i.test(effectBody);
|
|
166
|
+
if (hasSideEffect && !hasCleanup) {
|
|
167
|
+
issues.push({
|
|
168
|
+
file: filepath,
|
|
169
|
+
line: i + 1,
|
|
170
|
+
issue: "useEffect with side effects but no cleanup",
|
|
171
|
+
severity: "high",
|
|
172
|
+
detail: "Effect creates subscriptions/listeners but returns no cleanup function — leak on re-render",
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
// MutationObserver / IntersectionObserver / ResizeObserver without disconnect
|
|
177
|
+
if (/new (?:Mutation|Intersection|Resize)Observer\s*\(/.test(line)) {
|
|
178
|
+
if (!/\.disconnect\s*\(/i.test(fullText)) {
|
|
179
|
+
const observerType = line.match(/(Mutation|Intersection|Resize)Observer/)?.[1] || "Observer";
|
|
180
|
+
issues.push({
|
|
181
|
+
file: filepath,
|
|
182
|
+
line: i + 1,
|
|
183
|
+
issue: `${observerType}Observer without disconnect`,
|
|
184
|
+
severity: "medium",
|
|
185
|
+
detail: "Observer created but never disconnected — continues observing after disposal",
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
// AbortController created but never aborted
|
|
190
|
+
if (/new AbortController\s*\(/.test(line)) {
|
|
191
|
+
const varMatch = line.match(/(?:const|let|var)\s+(\w+)\s*=\s*new AbortController/);
|
|
192
|
+
if (varMatch) {
|
|
193
|
+
const varName = varMatch[1];
|
|
194
|
+
if (!fullText.includes(`${varName}.abort()`)) {
|
|
195
|
+
issues.push({
|
|
196
|
+
file: filepath,
|
|
197
|
+
line: i + 1,
|
|
198
|
+
issue: "AbortController never aborted",
|
|
199
|
+
severity: "low",
|
|
200
|
+
detail: `AbortController '${varName}' created but .abort() never called — no cleanup on cancel`,
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return issues;
|
|
207
|
+
}
|
|
208
|
+
// ─── CLI ────────────────────────────────────────────────────────────────────
|
|
209
|
+
export function runEventLeak(argv) {
|
|
210
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
211
|
+
console.log(`
|
|
212
|
+
judges event-leak — Detect orphaned listeners, unsubscribed observables, dangling handles
|
|
213
|
+
|
|
214
|
+
Usage:
|
|
215
|
+
judges event-leak [dir]
|
|
216
|
+
judges event-leak src/ --format json
|
|
217
|
+
|
|
218
|
+
Options:
|
|
219
|
+
[dir] Directory to scan (default: .)
|
|
220
|
+
--format json JSON output
|
|
221
|
+
--help, -h Show this help
|
|
222
|
+
|
|
223
|
+
Checks: addEventListener without cleanup, .on() without .off(), observable leaks,
|
|
224
|
+
setInterval without clear, useEffect without cleanup, Observer without disconnect.
|
|
225
|
+
`);
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
|
|
229
|
+
const dir = argv.find((a) => !a.startsWith("-") && argv.indexOf(a) > 0) || ".";
|
|
230
|
+
const files = collectFiles(dir);
|
|
231
|
+
const allIssues = [];
|
|
232
|
+
for (const f of files)
|
|
233
|
+
allIssues.push(...analyzeFile(f));
|
|
234
|
+
const highCount = allIssues.filter((i) => i.severity === "high").length;
|
|
235
|
+
const medCount = allIssues.filter((i) => i.severity === "medium").length;
|
|
236
|
+
const score = Math.max(0, 100 - highCount * 10 - medCount * 4);
|
|
237
|
+
if (format === "json") {
|
|
238
|
+
console.log(JSON.stringify({
|
|
239
|
+
issues: allIssues,
|
|
240
|
+
score,
|
|
241
|
+
summary: { high: highCount, medium: medCount, total: allIssues.length },
|
|
242
|
+
timestamp: new Date().toISOString(),
|
|
243
|
+
}, null, 2));
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
const badge = score >= 80 ? "✅ CLEAN" : score >= 50 ? "⚠️ LEAKY" : "❌ LEAKING";
|
|
247
|
+
console.log(`\n Event Leaks: ${badge} (${score}/100)\n ─────────────────────────────`);
|
|
248
|
+
if (allIssues.length === 0) {
|
|
249
|
+
console.log(" No event leaks detected.\n");
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
for (const issue of allIssues.slice(0, 25)) {
|
|
253
|
+
const icon = issue.severity === "high" ? "🔴" : issue.severity === "medium" ? "🟡" : "🔵";
|
|
254
|
+
console.log(` ${icon} ${issue.issue}`);
|
|
255
|
+
console.log(` ${issue.file}:${issue.line}`);
|
|
256
|
+
console.log(` ${issue.detail}`);
|
|
257
|
+
}
|
|
258
|
+
if (allIssues.length > 25)
|
|
259
|
+
console.log(` ... and ${allIssues.length - 25} more`);
|
|
260
|
+
console.log(`\n Total: ${allIssues.length} | High: ${highCount} | Medium: ${medCount} | Score: ${score}/100\n`);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
//# sourceMappingURL=event-leak.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-leak.js","sourceRoot":"","sources":["../../src/commands/event-leak.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"idempotency-audit.d.ts","sourceRoot":"","sources":["../../src/commands/idempotency-audit.ts"],"names":[],"mappings":"AAAA;;GAEG;AA2LH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CA+DxD"}
|