@kevinrabun/judges 3.45.0 → 3.47.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 (70) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/cli.d.ts.map +1 -1
  3. package/dist/cli.js +112 -0
  4. package/dist/cli.js.map +1 -1
  5. package/dist/commands/adoption-report.d.ts +8 -0
  6. package/dist/commands/adoption-report.d.ts.map +1 -0
  7. package/dist/commands/adoption-report.js +219 -0
  8. package/dist/commands/adoption-report.js.map +1 -0
  9. package/dist/commands/ai-model-trust.d.ts +17 -0
  10. package/dist/commands/ai-model-trust.d.ts.map +1 -0
  11. package/dist/commands/ai-model-trust.js +235 -0
  12. package/dist/commands/ai-model-trust.js.map +1 -0
  13. package/dist/commands/ai-prompt-audit.d.ts +23 -0
  14. package/dist/commands/ai-prompt-audit.d.ts.map +1 -0
  15. package/dist/commands/ai-prompt-audit.js +255 -0
  16. package/dist/commands/ai-prompt-audit.js.map +1 -0
  17. package/dist/commands/audit-bundle.d.ts +29 -0
  18. package/dist/commands/audit-bundle.d.ts.map +1 -0
  19. package/dist/commands/audit-bundle.js +235 -0
  20. package/dist/commands/audit-bundle.js.map +1 -0
  21. package/dist/commands/code-owner-suggest.d.ts +17 -0
  22. package/dist/commands/code-owner-suggest.d.ts.map +1 -0
  23. package/dist/commands/code-owner-suggest.js +215 -0
  24. package/dist/commands/code-owner-suggest.js.map +1 -0
  25. package/dist/commands/config-drift.d.ts +25 -0
  26. package/dist/commands/config-drift.d.ts.map +1 -0
  27. package/dist/commands/config-drift.js +214 -0
  28. package/dist/commands/config-drift.js.map +1 -0
  29. package/dist/commands/cost-forecast.d.ts +19 -0
  30. package/dist/commands/cost-forecast.d.ts.map +1 -0
  31. package/dist/commands/cost-forecast.js +194 -0
  32. package/dist/commands/cost-forecast.js.map +1 -0
  33. package/dist/commands/dev-score.d.ts +37 -0
  34. package/dist/commands/dev-score.d.ts.map +1 -0
  35. package/dist/commands/dev-score.js +204 -0
  36. package/dist/commands/dev-score.js.map +1 -0
  37. package/dist/commands/generate.d.ts +8 -0
  38. package/dist/commands/generate.d.ts.map +1 -0
  39. package/dist/commands/generate.js +404 -0
  40. package/dist/commands/generate.js.map +1 -0
  41. package/dist/commands/learn.d.ts +27 -0
  42. package/dist/commands/learn.d.ts.map +1 -0
  43. package/dist/commands/learn.js +289 -0
  44. package/dist/commands/learn.js.map +1 -0
  45. package/dist/commands/model-risk.d.ts +28 -0
  46. package/dist/commands/model-risk.d.ts.map +1 -0
  47. package/dist/commands/model-risk.js +221 -0
  48. package/dist/commands/model-risk.js.map +1 -0
  49. package/dist/commands/pr-quality-gate.d.ts +29 -0
  50. package/dist/commands/pr-quality-gate.d.ts.map +1 -0
  51. package/dist/commands/pr-quality-gate.js +208 -0
  52. package/dist/commands/pr-quality-gate.js.map +1 -0
  53. package/dist/commands/reg-watch.d.ts +21 -0
  54. package/dist/commands/reg-watch.d.ts.map +1 -0
  55. package/dist/commands/reg-watch.js +220 -0
  56. package/dist/commands/reg-watch.js.map +1 -0
  57. package/dist/commands/retro.d.ts +23 -0
  58. package/dist/commands/retro.d.ts.map +1 -0
  59. package/dist/commands/retro.js +217 -0
  60. package/dist/commands/retro.js.map +1 -0
  61. package/dist/commands/team-leaderboard.d.ts +25 -0
  62. package/dist/commands/team-leaderboard.d.ts.map +1 -0
  63. package/dist/commands/team-leaderboard.js +228 -0
  64. package/dist/commands/team-leaderboard.js.map +1 -0
  65. package/dist/commands/team-rules-sync.d.ts +8 -0
  66. package/dist/commands/team-rules-sync.d.ts.map +1 -0
  67. package/dist/commands/team-rules-sync.js +251 -0
  68. package/dist/commands/team-rules-sync.js.map +1 -0
  69. package/package.json +1 -1
  70. package/server.json +2 -2
@@ -0,0 +1,404 @@
1
+ /**
2
+ * Secure code template generator — pre-hardened templates
3
+ * for common patterns with Judges findings pre-mitigated.
4
+ *
5
+ * All output is generated locally — no data transmitted.
6
+ */
7
+ // ─── Template library ───────────────────────────────────────────────────────
8
+ const TEMPLATE_LIBRARY = [
9
+ {
10
+ id: "express-api-route",
11
+ title: "Express API Route (Secure)",
12
+ framework: "express",
13
+ language: "typescript",
14
+ description: "Express REST endpoint with input validation, error handling, and rate limiting",
15
+ mitigations: [
16
+ "Input validation with schema check",
17
+ "Error handler hides internal details",
18
+ "Parameterized DB queries",
19
+ "Rate limiting per IP",
20
+ ],
21
+ code: `import { Router, Request, Response, NextFunction } from "express";
22
+
23
+ const router = Router();
24
+
25
+ // Simple in-memory rate limiter
26
+ const hits = new Map<string, { count: number; reset: number }>();
27
+ function rateLimit(limit: number, windowMs: number) {
28
+ return (req: Request, res: Response, next: NextFunction): void => {
29
+ const key = req.ip ?? "unknown";
30
+ const now = Date.now();
31
+ const entry = hits.get(key);
32
+ if (!entry || now > entry.reset) {
33
+ hits.set(key, { count: 1, reset: now + windowMs });
34
+ return next();
35
+ }
36
+ if (entry.count >= limit) {
37
+ res.status(429).json({ error: "Too many requests" });
38
+ return;
39
+ }
40
+ entry.count++;
41
+ next();
42
+ };
43
+ }
44
+
45
+ // Input validation
46
+ function validateBody(body: unknown): body is { name: string; email: string } {
47
+ if (typeof body !== "object" || body === null) return false;
48
+ const b = body as Record<string, unknown>;
49
+ return typeof b.name === "string" && b.name.length <= 200
50
+ && typeof b.email === "string" && /^[^@]+@[^@]+$/.test(b.email);
51
+ }
52
+
53
+ router.post("/api/users", rateLimit(100, 60_000), (req: Request, res: Response) => {
54
+ if (!validateBody(req.body)) {
55
+ res.status(400).json({ error: "Invalid input" });
56
+ return;
57
+ }
58
+ // Use parameterized queries — never interpolate user input
59
+ // db.query("INSERT INTO users (name, email) VALUES ($1, $2)", [req.body.name, req.body.email]);
60
+ res.status(201).json({ ok: true });
61
+ });
62
+
63
+ // Centralized error handler — hides internals
64
+ router.use((err: Error, _req: Request, res: Response, _next: NextFunction) => {
65
+ console.error(err); // log server-side only
66
+ res.status(500).json({ error: "Internal server error" });
67
+ });
68
+
69
+ export default router;`,
70
+ },
71
+ {
72
+ id: "react-auth-component",
73
+ title: "React Auth Component (Secure)",
74
+ framework: "react",
75
+ language: "typescript",
76
+ description: "React authentication form with CSRF protection and secure state management",
77
+ mitigations: [
78
+ "CSRF token included in form submission",
79
+ "Password field never logged or serialized",
80
+ "AuthN errors are generic — no user enumeration",
81
+ "State cleared on unmount",
82
+ ],
83
+ code: `import React, { useState, useCallback, useEffect } from "react";
84
+
85
+ interface LoginFormProps {
86
+ csrfToken: string;
87
+ onLogin: (token: string) => void;
88
+ }
89
+
90
+ export function LoginForm({ csrfToken, onLogin }: LoginFormProps) {
91
+ const [email, setEmail] = useState("");
92
+ const [password, setPassword] = useState("");
93
+ const [error, setError] = useState<string | null>(null);
94
+ const [loading, setLoading] = useState(false);
95
+
96
+ // Clear sensitive state on unmount
97
+ useEffect(() => {
98
+ return () => {
99
+ setPassword("");
100
+ };
101
+ }, []);
102
+
103
+ const handleSubmit = useCallback(
104
+ async (e: React.FormEvent) => {
105
+ e.preventDefault();
106
+ setError(null);
107
+ setLoading(true);
108
+ try {
109
+ const resp = await fetch("/api/login", {
110
+ method: "POST",
111
+ headers: {
112
+ "Content-Type": "application/json",
113
+ "X-CSRF-Token": csrfToken,
114
+ },
115
+ body: JSON.stringify({ email, password }),
116
+ credentials: "same-origin",
117
+ });
118
+ if (!resp.ok) {
119
+ // Generic error — prevents user enumeration
120
+ setError("Invalid email or password");
121
+ return;
122
+ }
123
+ const data = await resp.json();
124
+ onLogin(data.token);
125
+ } catch {
126
+ setError("Login failed. Please try again.");
127
+ } finally {
128
+ setLoading(false);
129
+ setPassword(""); // clear password from memory
130
+ }
131
+ },
132
+ [email, password, csrfToken, onLogin],
133
+ );
134
+
135
+ return (
136
+ <form onSubmit={handleSubmit}>
137
+ <label>Email
138
+ <input type="email" value={email} onChange={(e) => setEmail(e.target.value)} required />
139
+ </label>
140
+ <label>Password
141
+ <input type="password" value={password} onChange={(e) => setPassword(e.target.value)} required autoComplete="current-password" />
142
+ </label>
143
+ {error && <p role="alert">{error}</p>}
144
+ <button type="submit" disabled={loading}>{loading ? "Logging in…" : "Log In"}</button>
145
+ </form>
146
+ );
147
+ }`,
148
+ },
149
+ {
150
+ id: "node-file-upload",
151
+ title: "Node.js File Upload (Secure)",
152
+ framework: "node",
153
+ language: "typescript",
154
+ description: "File upload handler with type checking, size limits, and path traversal prevention",
155
+ mitigations: [
156
+ "File type allowlist — rejects unexpected MIME types",
157
+ "Size limit enforced before writing",
158
+ "Sanitized filename prevents path traversal",
159
+ "Upload directory is outside web root",
160
+ ],
161
+ code: `import { randomUUID } from "crypto";
162
+ import { join, extname } from "path";
163
+ import { writeFileSync, mkdirSync, existsSync } from "fs";
164
+
165
+ const UPLOAD_DIR = "/var/uploads"; // outside web root
166
+ const MAX_SIZE = 5 * 1024 * 1024; // 5 MB
167
+ const ALLOWED_TYPES = new Set(["image/png", "image/jpeg", "image/gif", "application/pdf"]);
168
+ const ALLOWED_EXTS = new Set([".png", ".jpg", ".jpeg", ".gif", ".pdf"]);
169
+
170
+ interface UploadResult {
171
+ ok: boolean;
172
+ id?: string;
173
+ error?: string;
174
+ }
175
+
176
+ export function handleUpload(
177
+ filename: string,
178
+ contentType: string,
179
+ data: Buffer,
180
+ ): UploadResult {
181
+ // 1. Size check
182
+ if (data.length > MAX_SIZE) return { ok: false, error: "File too large" };
183
+
184
+ // 2. MIME type check
185
+ if (!ALLOWED_TYPES.has(contentType)) return { ok: false, error: "File type not allowed" };
186
+
187
+ // 3. Extension check (defense in depth)
188
+ const ext = extname(filename).toLowerCase();
189
+ if (!ALLOWED_EXTS.has(ext)) return { ok: false, error: "File extension not allowed" };
190
+
191
+ // 4. Generate safe filename — never use user-supplied name directly
192
+ const id = randomUUID();
193
+ const safeName = id + ext;
194
+ const dest = join(UPLOAD_DIR, safeName);
195
+
196
+ // 5. Ensure directory exists
197
+ if (!existsSync(UPLOAD_DIR)) mkdirSync(UPLOAD_DIR, { recursive: true });
198
+
199
+ // 6. Write file
200
+ writeFileSync(dest, data);
201
+ return { ok: true, id };
202
+ }`,
203
+ },
204
+ {
205
+ id: "python-flask-api",
206
+ title: "Flask API Endpoint (Secure)",
207
+ framework: "flask",
208
+ language: "python",
209
+ description: "Flask REST endpoint with CORS, input validation, and secure error responses",
210
+ mitigations: [
211
+ "CORS restricted to allowed origins",
212
+ "Input validated before processing",
213
+ "Errors return generic messages",
214
+ "SQL parameterized via SQLAlchemy",
215
+ ],
216
+ code: `from flask import Flask, request, jsonify
217
+ from flask_cors import CORS
218
+
219
+ app = Flask(__name__)
220
+ CORS(app, origins=["https://app.example.com"]) # restrict origins
221
+
222
+ @app.route("/api/items", methods=["POST"])
223
+ def create_item():
224
+ data = request.get_json(silent=True)
225
+ if not data:
226
+ return jsonify({"error": "Invalid JSON"}), 400
227
+
228
+ name = data.get("name", "")
229
+ if not isinstance(name, str) or len(name) > 200:
230
+ return jsonify({"error": "Invalid input"}), 400
231
+
232
+ # Use parameterized queries — never f-strings with user input
233
+ # db.session.execute(text("INSERT INTO items (name) VALUES (:name)"), {"name": name})
234
+ return jsonify({"ok": True}), 201
235
+
236
+ @app.errorhandler(Exception)
237
+ def handle_error(e):
238
+ app.logger.exception("Request error") # log server-side
239
+ return jsonify({"error": "Internal server error"}), 500
240
+ `,
241
+ },
242
+ {
243
+ id: "go-http-handler",
244
+ title: "Go HTTP Handler (Secure)",
245
+ framework: "net/http",
246
+ language: "go",
247
+ description: "Go HTTP handler with timeout, input validation, and secure headers",
248
+ mitigations: [
249
+ "Read/write timeouts prevent slowloris",
250
+ "Request body size limited",
251
+ "Security headers set on every response",
252
+ "Input validation before processing",
253
+ ],
254
+ code: `package main
255
+
256
+ import (
257
+ \t"encoding/json"
258
+ \t"log"
259
+ \t"net/http"
260
+ \t"time"
261
+ )
262
+
263
+ type CreateRequest struct {
264
+ \tName string \`json:"name"\`
265
+ \tEmail string \`json:"email"\`
266
+ }
267
+
268
+ func securityHeaders(next http.Handler) http.Handler {
269
+ \treturn http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
270
+ \t\tw.Header().Set("X-Content-Type-Options", "nosniff")
271
+ \t\tw.Header().Set("X-Frame-Options", "DENY")
272
+ \t\tw.Header().Set("Content-Security-Policy", "default-src 'self'")
273
+ \t\tnext.ServeHTTP(w, r)
274
+ \t})
275
+ }
276
+
277
+ func createHandler(w http.ResponseWriter, r *http.Request) {
278
+ \tif r.Method != http.MethodPost {
279
+ \t\thttp.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
280
+ \t\treturn
281
+ \t}
282
+
283
+ \t// Limit request body size
284
+ \tr.Body = http.MaxBytesReader(w, r.Body, 1<<20) // 1 MB
285
+
286
+ \tvar req CreateRequest
287
+ \tif err := json.NewDecoder(r.Body).Decode(&req); err != nil {
288
+ \t\thttp.Error(w, "Invalid input", http.StatusBadRequest)
289
+ \t\treturn
290
+ \t}
291
+
292
+ \tif len(req.Name) == 0 || len(req.Name) > 200 {
293
+ \t\thttp.Error(w, "Invalid name", http.StatusBadRequest)
294
+ \t\treturn
295
+ \t}
296
+
297
+ \tw.Header().Set("Content-Type", "application/json")
298
+ \tjson.NewEncoder(w).Encode(map[string]bool{"ok": true})
299
+ }
300
+
301
+ func main() {
302
+ \tmux := http.NewServeMux()
303
+ \tmux.HandleFunc("/api/create", createHandler)
304
+
305
+ \tsrv := &http.Server{
306
+ \t\tAddr: ":8080",
307
+ \t\tHandler: securityHeaders(mux),
308
+ \t\tReadTimeout: 5 * time.Second,
309
+ \t\tWriteTimeout: 10 * time.Second,
310
+ \t\tIdleTimeout: 120 * time.Second,
311
+ \t}
312
+ \tlog.Fatal(srv.ListenAndServe())
313
+ }
314
+ `,
315
+ },
316
+ ];
317
+ // ─── CLI ────────────────────────────────────────────────────────────────────
318
+ export function runGenerate(argv) {
319
+ if (argv.includes("--help") || argv.includes("-h")) {
320
+ console.log(`
321
+ judges generate — Secure code template generator
322
+
323
+ Usage:
324
+ judges generate --list
325
+ judges generate --template express-api-route
326
+ judges generate --template express-api-route --out ./src/routes/users.ts
327
+ judges generate --lang typescript
328
+ judges generate --framework flask
329
+
330
+ Options:
331
+ --list List all available templates
332
+ --template <id> Generate a specific template
333
+ --lang <language> Filter templates by language
334
+ --framework <name> Filter templates by framework
335
+ --out <path> Write template to file (stdout if omitted)
336
+ --format json JSON output
337
+ --help, -h Show this help
338
+ `);
339
+ return;
340
+ }
341
+ const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
342
+ const lang = argv.find((_a, i) => argv[i - 1] === "--lang");
343
+ const framework = argv.find((_a, i) => argv[i - 1] === "--framework");
344
+ // Filter
345
+ let templates = TEMPLATE_LIBRARY;
346
+ if (lang)
347
+ templates = templates.filter((t) => t.language === lang.toLowerCase());
348
+ if (framework)
349
+ templates = templates.filter((t) => t.framework === framework.toLowerCase());
350
+ // List
351
+ if (argv.includes("--list") || !argv.find((_a, i) => argv[i - 1] === "--template")) {
352
+ if (format === "json") {
353
+ console.log(JSON.stringify(templates.map(({ code: _c, ...rest }) => rest), null, 2));
354
+ }
355
+ else {
356
+ console.log(`\n Secure Code Templates (${templates.length})\n ──────────────────────────`);
357
+ for (const t of templates) {
358
+ console.log(` ${t.id.padEnd(25)} ${t.language.padEnd(12)} ${t.framework.padEnd(10)} ${t.title}`);
359
+ }
360
+ console.log(`\n Use: judges generate --template <id>\n`);
361
+ }
362
+ return;
363
+ }
364
+ // Specific template
365
+ const templateId = argv.find((_a, i) => argv[i - 1] === "--template");
366
+ if (!templateId)
367
+ return;
368
+ const tmpl = TEMPLATE_LIBRARY.find((t) => t.id === templateId);
369
+ if (!tmpl) {
370
+ console.error(` Template not found: ${templateId}`);
371
+ console.error(` Use --list to see available templates.`);
372
+ return;
373
+ }
374
+ // Output path
375
+ const outPath = argv.find((_a, i) => argv[i - 1] === "--out");
376
+ if (format === "json") {
377
+ console.log(JSON.stringify(tmpl, null, 2));
378
+ return;
379
+ }
380
+ const header = [
381
+ `// ═══════════════════════════════════════════════════`,
382
+ `// ${tmpl.title}`,
383
+ `// Framework: ${tmpl.framework} | Language: ${tmpl.language}`,
384
+ `// ${tmpl.description}`,
385
+ `//`,
386
+ `// Security mitigations applied:`,
387
+ ...tmpl.mitigations.map((m) => `// ✓ ${m}`),
388
+ `//`,
389
+ `// Generated by: judges generate --template ${tmpl.id}`,
390
+ `// ═══════════════════════════════════════════════════`,
391
+ ``,
392
+ ].join("\n");
393
+ const output = header + tmpl.code;
394
+ if (outPath) {
395
+ const { writeFileSync: wfs } = require("fs");
396
+ wfs(outPath, output);
397
+ console.log(` ✅ Written to ${outPath}`);
398
+ console.log(` Template: ${tmpl.title}`);
399
+ }
400
+ else {
401
+ console.log(output);
402
+ }
403
+ }
404
+ //# sourceMappingURL=generate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate.js","sourceRoot":"","sources":["../../src/commands/generate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Developer learning path — personalized, structured learning
3
+ * based on a developer's finding history.
4
+ *
5
+ * Uses .judges-scores/ and .judges-learn/ for progress.
6
+ */
7
+ interface LearningModule {
8
+ id: string;
9
+ title: string;
10
+ category: string;
11
+ difficulty: "beginner" | "intermediate" | "advanced";
12
+ concepts: string[];
13
+ exercises: string[];
14
+ completed: boolean;
15
+ completedAt?: string;
16
+ }
17
+ interface LearningPath {
18
+ author: string;
19
+ modules: LearningModule[];
20
+ progress: number;
21
+ streak: number;
22
+ lastActivity: string;
23
+ }
24
+ export declare function completeModule(author: string, moduleId: string): LearningPath;
25
+ export declare function runLearn(argv: string[]): void;
26
+ export {};
27
+ //# sourceMappingURL=learn.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"learn.d.ts","sourceRoot":"","sources":["../../src/commands/learn.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAOH,UAAU,cAAc;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,UAAU,GAAG,cAAc,GAAG,UAAU,CAAC;IACrD,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,UAAU,YAAY;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;CACtB;AAgLD,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,YAAY,CAY7E;AAID,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAsG7C"}