@kevinrabun/judges 1.2.0 → 1.4.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 (116) hide show
  1. package/README.md +49 -13
  2. package/dist/evaluators/authentication.d.ts +3 -0
  3. package/dist/evaluators/authentication.d.ts.map +1 -0
  4. package/dist/evaluators/authentication.js +170 -0
  5. package/dist/evaluators/authentication.js.map +1 -0
  6. package/dist/evaluators/backwards-compatibility.d.ts +3 -0
  7. package/dist/evaluators/backwards-compatibility.d.ts.map +1 -0
  8. package/dist/evaluators/backwards-compatibility.js +146 -0
  9. package/dist/evaluators/backwards-compatibility.js.map +1 -0
  10. package/dist/evaluators/caching.d.ts +3 -0
  11. package/dist/evaluators/caching.d.ts.map +1 -0
  12. package/dist/evaluators/caching.js +146 -0
  13. package/dist/evaluators/caching.js.map +1 -0
  14. package/dist/evaluators/ci-cd.d.ts +3 -0
  15. package/dist/evaluators/ci-cd.d.ts.map +1 -0
  16. package/dist/evaluators/ci-cd.js +155 -0
  17. package/dist/evaluators/ci-cd.js.map +1 -0
  18. package/dist/evaluators/configuration-management.d.ts +3 -0
  19. package/dist/evaluators/configuration-management.d.ts.map +1 -0
  20. package/dist/evaluators/configuration-management.js +146 -0
  21. package/dist/evaluators/configuration-management.js.map +1 -0
  22. package/dist/evaluators/database.d.ts +3 -0
  23. package/dist/evaluators/database.d.ts.map +1 -0
  24. package/dist/evaluators/database.js +179 -0
  25. package/dist/evaluators/database.js.map +1 -0
  26. package/dist/evaluators/error-handling.d.ts +3 -0
  27. package/dist/evaluators/error-handling.d.ts.map +1 -0
  28. package/dist/evaluators/error-handling.js +180 -0
  29. package/dist/evaluators/error-handling.js.map +1 -0
  30. package/dist/evaluators/index.d.ts.map +1 -1
  31. package/dist/evaluators/index.js +48 -0
  32. package/dist/evaluators/index.js.map +1 -1
  33. package/dist/evaluators/logging-privacy.d.ts +3 -0
  34. package/dist/evaluators/logging-privacy.d.ts.map +1 -0
  35. package/dist/evaluators/logging-privacy.js +147 -0
  36. package/dist/evaluators/logging-privacy.js.map +1 -0
  37. package/dist/evaluators/maintainability.d.ts +3 -0
  38. package/dist/evaluators/maintainability.d.ts.map +1 -0
  39. package/dist/evaluators/maintainability.js +243 -0
  40. package/dist/evaluators/maintainability.js.map +1 -0
  41. package/dist/evaluators/portability.d.ts +3 -0
  42. package/dist/evaluators/portability.d.ts.map +1 -0
  43. package/dist/evaluators/portability.js +180 -0
  44. package/dist/evaluators/portability.js.map +1 -0
  45. package/dist/evaluators/rate-limiting.d.ts +3 -0
  46. package/dist/evaluators/rate-limiting.d.ts.map +1 -0
  47. package/dist/evaluators/rate-limiting.js +161 -0
  48. package/dist/evaluators/rate-limiting.js.map +1 -0
  49. package/dist/evaluators/scalability.d.ts.map +1 -1
  50. package/dist/evaluators/scalability.js +8 -3
  51. package/dist/evaluators/scalability.js.map +1 -1
  52. package/dist/evaluators/software-practices.d.ts.map +1 -1
  53. package/dist/evaluators/software-practices.js +14 -3
  54. package/dist/evaluators/software-practices.js.map +1 -1
  55. package/dist/evaluators/testing.d.ts.map +1 -1
  56. package/dist/evaluators/testing.js +6 -2
  57. package/dist/evaluators/testing.js.map +1 -1
  58. package/dist/evaluators/ux.d.ts +3 -0
  59. package/dist/evaluators/ux.d.ts.map +1 -0
  60. package/dist/evaluators/ux.js +175 -0
  61. package/dist/evaluators/ux.js.map +1 -0
  62. package/dist/index.d.ts +1 -1
  63. package/dist/index.js +3 -3
  64. package/dist/judges/authentication.d.ts +3 -0
  65. package/dist/judges/authentication.d.ts.map +1 -0
  66. package/dist/judges/authentication.js +34 -0
  67. package/dist/judges/authentication.js.map +1 -0
  68. package/dist/judges/backwards-compatibility.d.ts +3 -0
  69. package/dist/judges/backwards-compatibility.d.ts.map +1 -0
  70. package/dist/judges/backwards-compatibility.js +34 -0
  71. package/dist/judges/backwards-compatibility.js.map +1 -0
  72. package/dist/judges/caching.d.ts +3 -0
  73. package/dist/judges/caching.d.ts.map +1 -0
  74. package/dist/judges/caching.js +34 -0
  75. package/dist/judges/caching.js.map +1 -0
  76. package/dist/judges/ci-cd.d.ts +3 -0
  77. package/dist/judges/ci-cd.d.ts.map +1 -0
  78. package/dist/judges/ci-cd.js +34 -0
  79. package/dist/judges/ci-cd.js.map +1 -0
  80. package/dist/judges/configuration-management.d.ts +3 -0
  81. package/dist/judges/configuration-management.d.ts.map +1 -0
  82. package/dist/judges/configuration-management.js +34 -0
  83. package/dist/judges/configuration-management.js.map +1 -0
  84. package/dist/judges/database.d.ts +3 -0
  85. package/dist/judges/database.d.ts.map +1 -0
  86. package/dist/judges/database.js +34 -0
  87. package/dist/judges/database.js.map +1 -0
  88. package/dist/judges/error-handling.d.ts +3 -0
  89. package/dist/judges/error-handling.d.ts.map +1 -0
  90. package/dist/judges/error-handling.js +34 -0
  91. package/dist/judges/error-handling.js.map +1 -0
  92. package/dist/judges/index.d.ts.map +1 -1
  93. package/dist/judges/index.js +24 -0
  94. package/dist/judges/index.js.map +1 -1
  95. package/dist/judges/logging-privacy.d.ts +3 -0
  96. package/dist/judges/logging-privacy.d.ts.map +1 -0
  97. package/dist/judges/logging-privacy.js +34 -0
  98. package/dist/judges/logging-privacy.js.map +1 -0
  99. package/dist/judges/maintainability.d.ts +3 -0
  100. package/dist/judges/maintainability.d.ts.map +1 -0
  101. package/dist/judges/maintainability.js +34 -0
  102. package/dist/judges/maintainability.js.map +1 -0
  103. package/dist/judges/portability.d.ts +3 -0
  104. package/dist/judges/portability.d.ts.map +1 -0
  105. package/dist/judges/portability.js +34 -0
  106. package/dist/judges/portability.js.map +1 -0
  107. package/dist/judges/rate-limiting.d.ts +3 -0
  108. package/dist/judges/rate-limiting.d.ts.map +1 -0
  109. package/dist/judges/rate-limiting.js +34 -0
  110. package/dist/judges/rate-limiting.js.map +1 -0
  111. package/dist/judges/ux.d.ts +3 -0
  112. package/dist/judges/ux.d.ts.map +1 -0
  113. package/dist/judges/ux.js +34 -0
  114. package/dist/judges/ux.js.map +1 -0
  115. package/package.json +2 -2
  116. package/server.json +3 -3
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Judges Panel
2
2
 
3
- An MCP (Model Context Protocol) server that provides a panel of **18 specialized judges** to evaluate AI-generated code — acting as an independent quality gate regardless of which project is being reviewed.
3
+ An MCP (Model Context Protocol) server that provides a panel of **30 specialized judges** to evaluate AI-generated code — acting as an independent quality gate regardless of which project is being reviewed.
4
4
 
5
5
  [![CI](https://github.com/KevinRabun/judges/actions/workflows/ci.yml/badge.svg)](https://github.com/KevinRabun/judges/actions/workflows/ci.yml)
6
6
  [![npm](https://img.shields.io/npm/v/@kevinrabun/judges)](https://www.npmjs.com/package/@kevinrabun/judges)
@@ -21,7 +21,7 @@ npm run build
21
21
 
22
22
  ### 2. Try the Demo
23
23
 
24
- Run the included demo to see all 18 judges evaluate a purposely flawed API server:
24
+ Run the included demo to see all 30 judges evaluate a purposely flawed API server:
25
25
 
26
26
  ```bash
27
27
  npm run demo
@@ -41,7 +41,7 @@ This evaluates [`examples/sample-vulnerable-api.ts`](examples/sample-vulnerable-
41
41
  Critical Issues : 15
42
42
  High Issues : 17
43
43
  Total Findings : 83
44
- Judges Run : 18
44
+ Judges Run : 30
45
45
 
46
46
  Per-Judge Breakdown:
47
47
  ────────────────────────────────────────────────────────────────
@@ -63,6 +63,18 @@ This evaluates [`examples/sample-vulnerable-api.ts`](examples/sample-vulnerable-
63
63
  ⚠️ Judge Dependency Health 90/100 1 finding(s)
64
64
  ❌ Judge Concurrency 44/100 4 finding(s)
65
65
  ❌ Judge Ethics & Bias 65/100 2 finding(s)
66
+ ❌ Judge Maintainability 52/100 4 finding(s)
67
+ ❌ Judge Error Handling 27/100 3 finding(s)
68
+ ❌ Judge Authentication 0/100 4 finding(s)
69
+ ❌ Judge Database 0/100 5 finding(s)
70
+ ❌ Judge Caching 62/100 3 finding(s)
71
+ ❌ Judge Configuration Mgmt 0/100 3 finding(s)
72
+ ⚠️ Judge Backwards Compat 80/100 2 finding(s)
73
+ ⚠️ Judge Portability 72/100 2 finding(s)
74
+ ❌ Judge UX 52/100 4 finding(s)
75
+ ❌ Judge Logging Privacy 0/100 4 finding(s)
76
+ ❌ Judge Rate Limiting 27/100 4 finding(s)
77
+ ⚠️ Judge CI/CD 80/100 2 finding(s)
66
78
  ```
67
79
 
68
80
  ### 3. Run the Tests
@@ -71,7 +83,7 @@ This evaluates [`examples/sample-vulnerable-api.ts`](examples/sample-vulnerable-
71
83
  npm test
72
84
  ```
73
85
 
74
- Runs 184 automated tests covering all 18 judges, markdown formatters, and edge cases.
86
+ Runs automated tests covering all 30 judges, markdown formatters, and edge cases.
75
87
 
76
88
  ### 4. Connect to Your Editor
77
89
 
@@ -135,6 +147,18 @@ Then use `judges` as the command in your MCP config (no `args` needed).
135
147
  | **Dependency Health** | Dependency Management | `DEPS-` | Version pinning, deprecated packages, supply chain |
136
148
  | **Concurrency** | Concurrency & Async Safety | `CONC-` | Race conditions, unbounded parallelism, missing await |
137
149
  | **Ethics & Bias** | Ethics & Bias | `ETHICS-` | Demographic logic, dark patterns, inclusive language |
150
+ | **Maintainability** | Code Maintainability & Technical Debt | `MAINT-` | Any types, magic numbers, deep nesting, dead code, file length |
151
+ | **Error Handling** | Error Handling & Fault Tolerance | `ERR-` | Empty catch blocks, missing error handlers, swallowed errors |
152
+ | **Authentication** | Authentication & Authorization | `AUTH-` | Hardcoded creds, missing auth middleware, token in query params |
153
+ | **Database** | Database Design & Query Efficiency | `DB-` | SQL injection, N+1 queries, connection pooling, transactions |
154
+ | **Caching** | Caching Strategy & Data Freshness | `CACHE-` | Unbounded caches, missing TTL, no HTTP cache headers |
155
+ | **Configuration Mgmt** | Configuration & Secrets Management | `CFG-` | Hardcoded secrets, missing env vars, config validation |
156
+ | **Backwards Compat** | Backwards Compatibility & Versioning | `COMPAT-` | API versioning, breaking changes, response consistency |
157
+ | **Portability** | Platform Portability & Vendor Independence | `PORTA-` | OS-specific paths, vendor lock-in, hardcoded hosts |
158
+ | **UX** | User Experience & Interface Quality | `UX-` | Loading states, error messages, pagination, destructive actions |
159
+ | **Logging Privacy** | Logging Privacy & Data Redaction | `LOGPRIV-` | PII in logs, token logging, structured logging, redaction |
160
+ | **Rate Limiting** | Rate Limiting & Throttling | `RATE-` | Missing rate limits, unbounded queries, backoff strategy |
161
+ | **CI/CD** | CI/CD Pipeline & Deployment Safety | `CICD-` | Test infrastructure, lint config, Docker tags, build scripts |
138
162
 
139
163
  ---
140
164
 
@@ -173,7 +197,7 @@ When your AI coding assistant connects to multiple MCP servers, each one contrib
173
197
 
174
198
  | Layer | What It Does | Example Servers |
175
199
  |-------|-------------|-----------------|
176
- | **Judges Panel** | 18-judge quality gate — security patterns, cost, scalability, a11y, compliance, ethics | This server |
200
+ | **Judges Panel** | 30-judge quality gate — security patterns, cost, scalability, a11y, compliance, ethics | This server |
177
201
  | **AST Analysis** | Deep structural analysis — data flow, complexity metrics, dead code, type tracking | Tree-sitter, Semgrep, SonarQube MCP servers |
178
202
  | **CVE / SBOM** | Vulnerability scanning against live databases — known CVEs, license risks, supply chain | OSV, Snyk, Trivy, Grype MCP servers |
179
203
  | **Linting** | Language-specific style and correctness rules | ESLint, Ruff, Clippy MCP servers |
@@ -209,7 +233,7 @@ Each server returns structured findings. The AI synthesizes everything into a si
209
233
  List all available judges with their domains and descriptions.
210
234
 
211
235
  ### `evaluate_code`
212
- Submit code to the **full judges panel**. All 18 judges evaluate independently and return a combined verdict.
236
+ Submit code to the **full judges panel**. All 30 judges evaluate independently and return a combined verdict.
213
237
 
214
238
  | Parameter | Type | Required | Description |
215
239
  |-----------|------|----------|-------------|
@@ -229,7 +253,7 @@ Submit code to a **specific judge** for targeted review.
229
253
 
230
254
  #### Judge IDs
231
255
 
232
- `data-security` · `cybersecurity` · `cost-effectiveness` · `scalability` · `cloud-readiness` · `software-practices` · `accessibility` · `api-design` · `reliability` · `observability` · `performance` · `compliance` · `testing` · `documentation` · `internationalization` · `dependency-health` · `concurrency` · `ethics-bias`
256
+ `data-security` · `cybersecurity` · `cost-effectiveness` · `scalability` · `cloud-readiness` · `software-practices` · `accessibility` · `api-design` · `reliability` · `observability` · `performance` · `compliance` · `testing` · `documentation` · `internationalization` · `dependency-health` · `concurrency` · `ethics-bias` · `maintainability` · `error-handling` · `authentication` · `database` · `caching` · `configuration-management` · `backwards-compatibility` · `portability` · `ux` · `logging-privacy` · `rate-limiting` · `ci-cd`
233
257
 
234
258
  ---
235
259
 
@@ -257,7 +281,19 @@ Each judge has a corresponding prompt for LLM-powered deep analysis:
257
281
  | `judge-dependency-health` | Deep dependency health review |
258
282
  | `judge-concurrency` | Deep concurrency & async safety review |
259
283
  | `judge-ethics-bias` | Deep ethics & bias review |
260
- | `full-tribunal` | All 18 judges in a single prompt |
284
+ | `judge-maintainability` | Deep maintainability & tech debt review |
285
+ | `judge-error-handling` | Deep error handling review |
286
+ | `judge-authentication` | Deep authentication & authorization review |
287
+ | `judge-database` | Deep database design & query review |
288
+ | `judge-caching` | Deep caching strategy review |
289
+ | `judge-configuration-management` | Deep configuration & secrets review |
290
+ | `judge-backwards-compatibility` | Deep backwards compatibility review |
291
+ | `judge-portability` | Deep platform portability review |
292
+ | `judge-ux` | Deep user experience review |
293
+ | `judge-logging-privacy` | Deep logging privacy review |
294
+ | `judge-rate-limiting` | Deep rate limiting review |
295
+ | `judge-ci-cd` | Deep CI/CD pipeline review |
296
+ | `full-tribunal` | All 30 judges in a single prompt |
261
297
 
262
298
  ---
263
299
 
@@ -278,7 +314,7 @@ Each judge scores the code from **0 to 100**:
278
314
  - **WARNING** — Any high finding, any medium finding, or score < 80
279
315
  - **PASS** — Score ≥ 80 with no critical, high, or medium findings
280
316
 
281
- The **overall tribunal score** is the average of all 18 judges. The overall verdict fails if **any** judge fails.
317
+ The **overall tribunal score** is the average of all 30 judges. The overall verdict fails if **any** judge fails.
282
318
 
283
319
  ---
284
320
 
@@ -292,12 +328,12 @@ judges/
292
328
  │ ├── evaluators/ # Pattern-based analysis engine for each judge
293
329
  │ │ ├── index.ts # evaluateWithJudge(), evaluateWithTribunal()
294
330
  │ │ ├── shared.ts # Scoring, verdict logic, markdown formatters
295
- │ │ └── *.ts # One analyzer per judge (18 files)
331
+ │ │ └── *.ts # One analyzer per judge (30 files)
296
332
  │ └── judges/ # Judge definitions (id, name, domain, system prompt)
297
333
  │ ├── index.ts # JUDGES array, getJudge(), getJudgeSummaries()
298
- │ └── *.ts # One definition per judge (18 files)
334
+ │ └── *.ts # One definition per judge (30 files)
299
335
  ├── examples/
300
- │ ├── sample-vulnerable-api.ts # Intentionally flawed code (triggers all 18 judges)
336
+ │ ├── sample-vulnerable-api.ts # Intentionally flawed code (triggers all 30 judges)
301
337
  │ └── demo.ts # Run: npm run demo
302
338
  ├── tests/
303
339
  │ └── judges.test.ts # Run: npm test (184 tests)
@@ -315,7 +351,7 @@ judges/
315
351
  |---------|-------------|
316
352
  | `npm run build` | Compile TypeScript to `dist/` |
317
353
  | `npm run dev` | Watch mode — recompile on save |
318
- | `npm test` | Run the full test suite (184 tests) |
354
+ | `npm test` | Run the full test suite |
319
355
  | `npm run demo` | Run the sample tribunal demo |
320
356
  | `npm start` | Start the MCP server |
321
357
  | `npm run clean` | Remove `dist/` |
@@ -0,0 +1,3 @@
1
+ import { Finding } from "../types.js";
2
+ export declare function analyzeAuthentication(code: string, language: string): Finding[];
3
+ //# sourceMappingURL=authentication.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authentication.d.ts","sourceRoot":"","sources":["../../src/evaluators/authentication.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAGtC,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,EAAE,CAoL/E"}
@@ -0,0 +1,170 @@
1
+ import { getLineNumbers } from "./shared.js";
2
+ export function analyzeAuthentication(code, language) {
3
+ const findings = [];
4
+ let ruleNum = 1;
5
+ const prefix = "AUTH";
6
+ // Hardcoded credentials
7
+ const credentialPattern = /(?:password|passwd|pwd|secret|api_?key|apikey|token|auth_?token)\s*[:=]\s*["'`][^"'`]{3,}/gi;
8
+ const credentialLines = getLineNumbers(code, credentialPattern);
9
+ if (credentialLines.length > 0) {
10
+ findings.push({
11
+ ruleId: `${prefix}-${String(ruleNum++).padStart(3, "0")}`,
12
+ severity: "critical",
13
+ title: "Hardcoded credentials in source code",
14
+ description: `Found ${credentialLines.length} instance(s) of what appears to be hardcoded credentials. Credentials in source code are exposed in version control and cannot be rotated without redeployment.`,
15
+ lineNumbers: credentialLines,
16
+ recommendation: "Use environment variables or a secrets manager (Azure Key Vault, AWS Secrets Manager, HashiCorp Vault). Never commit credentials to version control.",
17
+ reference: "OWASP: Credential Management / CWE-798",
18
+ });
19
+ }
20
+ // No auth middleware on routes
21
+ const hasRoutes = /app\.(get|post|put|delete|patch)\s*\(\s*["'`]/gi.test(code);
22
+ const hasAuthMiddleware = /(?:authenticate|authorize|requireAuth|ensureAuth|isAuthenticated|verifyToken|passport\.authenticate|jwt\.verify|auth\(\)|protect|guard|requireLogin)/gi.test(code);
23
+ if (hasRoutes && !hasAuthMiddleware && code.split("\n").length > 20) {
24
+ findings.push({
25
+ ruleId: `${prefix}-${String(ruleNum++).padStart(3, "0")}`,
26
+ severity: "high",
27
+ title: "API routes without authentication middleware",
28
+ description: "API endpoints are defined without any visible authentication middleware. Any client can access these endpoints without proving their identity.",
29
+ recommendation: "Apply authentication middleware to routes that require it. Use app.use(authMiddleware) for global protection or per-route middleware for selective protection.",
30
+ reference: "OWASP API Security Top 10: API2 — Broken Authentication",
31
+ });
32
+ }
33
+ // Token in query parameters
34
+ const tokenQueryPattern = /req\.query\.(?:token|api_?key|auth|secret|password|access_token)/gi;
35
+ const tokenQueryLines = getLineNumbers(code, tokenQueryPattern);
36
+ if (tokenQueryLines.length > 0) {
37
+ findings.push({
38
+ ruleId: `${prefix}-${String(ruleNum++).padStart(3, "0")}`,
39
+ severity: "high",
40
+ title: "Sensitive tokens passed in query parameters",
41
+ description: "Authentication tokens or API keys are read from query parameters. Query params appear in server logs, browser history, referrer headers, and proxy logs.",
42
+ lineNumbers: tokenQueryLines,
43
+ recommendation: "Pass tokens in the Authorization header (Bearer scheme) or in httpOnly cookies. Never use query parameters for sensitive credentials.",
44
+ reference: "OWASP: Transport Layer Security / RFC 6750",
45
+ });
46
+ }
47
+ // Weak password hashing
48
+ const weakHashPattern = /createHash\s*\(\s*["'`](?:md5|sha1|sha256)["'`]\)|(?:md5|sha1)\s*\(/gi;
49
+ const weakHashLines = getLineNumbers(code, weakHashPattern);
50
+ if (weakHashLines.length > 0) {
51
+ findings.push({
52
+ ruleId: `${prefix}-${String(ruleNum++).padStart(3, "0")}`,
53
+ severity: "critical",
54
+ title: "Weak hashing algorithm for credentials",
55
+ description: "MD5, SHA1, or SHA256 are fast hash algorithms unsuitable for password storage. They can be brute-forced at billions of hashes per second.",
56
+ lineNumbers: weakHashLines,
57
+ recommendation: "Use bcrypt, scrypt, or Argon2 for password hashing. These algorithms are intentionally slow and include salt by default.",
58
+ reference: "OWASP Password Storage Cheat Sheet / NIST 800-63b",
59
+ });
60
+ }
61
+ // No RBAC / authorization checks
62
+ const hasRoleCheck = /role|permission|isAdmin|isOwner|canAccess|authorize|requiredRole|hasPermission|checkPermission/gi.test(code);
63
+ if (hasRoutes && !hasRoleCheck && code.split("\n").length > 40) {
64
+ findings.push({
65
+ ruleId: `${prefix}-${String(ruleNum++).padStart(3, "0")}`,
66
+ severity: "medium",
67
+ title: "No authorization / role-based access control detected",
68
+ description: "No role or permission checks found. Without authorization, any authenticated user could access any resource, including admin functions.",
69
+ recommendation: "Implement role-based access control (RBAC) or attribute-based access control (ABAC). Check permissions at each endpoint or resource access.",
70
+ reference: "OWASP API Security Top 10: API5 — Broken Function Level Authorization",
71
+ });
72
+ }
73
+ // JWT without verification
74
+ const hasJwt = /jwt|jsonwebtoken|jose/gi.test(code);
75
+ const hasJwtVerify = /jwt\.verify|jwtVerify|verifyToken|jose\.jwtVerify/gi.test(code);
76
+ const hasJwtSign = /jwt\.sign|jwtSign|signToken/gi.test(code);
77
+ if (hasJwt && hasJwtSign && !hasJwtVerify) {
78
+ findings.push({
79
+ ruleId: `${prefix}-${String(ruleNum++).padStart(3, "0")}`,
80
+ severity: "critical",
81
+ title: "JWT tokens signed but never verified",
82
+ description: "JWT tokens are being created but no verification logic is visible. Tokens could be tampered with or forged without the server detecting it.",
83
+ recommendation: "Always verify JWT tokens on every request: check signature, expiration (exp), issuer (iss), and audience (aud).",
84
+ reference: "RFC 7519: JWT / OWASP JWT Cheat Sheet",
85
+ });
86
+ }
87
+ // Disabled TLS / certificate validation
88
+ const tlsDisabledPattern = /NODE_TLS_REJECT_UNAUTHORIZED\s*=\s*["'`]?0|rejectUnauthorized\s*:\s*false|verify\s*=\s*False|InsecureSkipVerify\s*:\s*true/gi;
89
+ const tlsLines = getLineNumbers(code, tlsDisabledPattern);
90
+ if (tlsLines.length > 0) {
91
+ findings.push({
92
+ ruleId: `${prefix}-${String(ruleNum++).padStart(3, "0")}`,
93
+ severity: "critical",
94
+ title: "TLS certificate validation disabled",
95
+ description: "TLS certificate verification is disabled, allowing man-in-the-middle attacks. Authentication credentials sent over this connection can be intercepted.",
96
+ lineNumbers: tlsLines,
97
+ recommendation: "Never disable TLS verification in production. Fix certificate issues properly. Use CA bundles for self-signed certs in development only.",
98
+ reference: "CWE-295: Improper Certificate Validation",
99
+ });
100
+ }
101
+ // No session expiration / no token expiry
102
+ const hasSession = /session|express-session|cookie-session|SessionMiddleware/gi.test(code);
103
+ const hasExpiry = /maxAge|expires|expiresIn|exp:|ttl|timeout.*session|cookie.*max/gi.test(code);
104
+ if (hasSession && !hasExpiry) {
105
+ findings.push({
106
+ ruleId: `${prefix}-${String(ruleNum++).padStart(3, "0")}`,
107
+ severity: "high",
108
+ title: "Sessions without expiration configured",
109
+ description: "Session middleware is used without visible expiration settings. Sessions that never expire allow stolen session tokens to be used indefinitely.",
110
+ recommendation: "Set session maxAge (e.g., 30 minutes for sensitive apps). Implement idle timeout. Invalidate sessions on password change or logout.",
111
+ reference: "OWASP Session Management Cheat Sheet",
112
+ });
113
+ }
114
+ // Weak password policy — no complexity enforcement
115
+ const hasUserRegistration = /register|signup|sign.?up|createUser|create.*user|new.*user/gi.test(code);
116
+ const hasPasswordPolicy = /minLength|minimum.*length|password.*length|complexity|strongPassword|zxcvbn|password.*policy|password.*require/gi.test(code);
117
+ if (hasUserRegistration && !hasPasswordPolicy) {
118
+ findings.push({
119
+ ruleId: `${prefix}-${String(ruleNum++).padStart(3, "0")}`,
120
+ severity: "medium",
121
+ title: "No password complexity enforcement",
122
+ description: "User registration logic without visible password policy. Users can set weak passwords like '123456' or 'password', which are trivially guessable.",
123
+ recommendation: "Enforce minimum password length (12+ chars), check against known breached passwords (HaveIBeenPwned API), and use a strength estimator like zxcvbn.",
124
+ reference: "NIST 800-63b / OWASP Password Guidelines",
125
+ });
126
+ }
127
+ // No account lockout after failed attempts
128
+ const hasLogin = /login|signin|sign.?in|authenticate|verifyPassword|checkPassword/gi.test(code);
129
+ const hasLockout = /lockout|lock.*out|attempt|maxAttempt|failedAttempt|rateLimitLogin|brute.?force|account.*lock/gi.test(code);
130
+ if (hasLogin && !hasLockout) {
131
+ findings.push({
132
+ ruleId: `${prefix}-${String(ruleNum++).padStart(3, "0")}`,
133
+ severity: "medium",
134
+ title: "No account lockout after failed login attempts",
135
+ description: "Login logic without account lockout or rate limiting. Attackers can brute-force passwords by trying unlimited login attempts.",
136
+ recommendation: "Implement progressive delays or temporary lockout after 5-10 failed attempts. Use rate limiting on login endpoints. Consider CAPTCHA for repeated failures.",
137
+ reference: "OWASP Brute Force Prevention / CWE-307",
138
+ });
139
+ }
140
+ // Cookie without Secure and HttpOnly flags
141
+ const cookiePattern = /(?:cookie|Cookie|set-cookie|setCookie|res\.cookie)\s*\(/gi;
142
+ const cookieLines = getLineNumbers(code, cookiePattern);
143
+ const hasSecureFlags = /secure\s*:\s*true|httpOnly\s*:\s*true|HttpOnly|Secure/g.test(code);
144
+ if (cookieLines.length > 0 && !hasSecureFlags) {
145
+ findings.push({
146
+ ruleId: `${prefix}-${String(ruleNum++).padStart(3, "0")}`,
147
+ severity: "high",
148
+ title: "Cookies set without Secure/HttpOnly flags",
149
+ description: "Cookies are set without Secure (HTTPS-only) or HttpOnly (no JS access) flags. This exposes cookies to interception and XSS-based theft.",
150
+ lineNumbers: cookieLines,
151
+ recommendation: "Set cookies with { secure: true, httpOnly: true, sameSite: 'strict' }. Use Secure for all auth cookies. HttpOnly prevents JavaScript access.",
152
+ reference: "OWASP Secure Cookie Best Practices / CWE-614",
153
+ });
154
+ }
155
+ // No CSRF protection
156
+ const hasFormPost = /app\.post\s*\(|method\s*=\s*["']POST/gi.test(code);
157
+ const hasCsrf = /csrf|csurf|xsrf|_token|csrfToken|antiForgery|X-CSRF|X-XSRF/gi.test(code);
158
+ if (hasFormPost && !hasCsrf && hasSession) {
159
+ findings.push({
160
+ ruleId: `${prefix}-${String(ruleNum++).padStart(3, "0")}`,
161
+ severity: "high",
162
+ title: "No CSRF protection on form submissions",
163
+ description: "POST endpoints with session-based auth but no CSRF tokens. Attackers can craft pages that submit forms on behalf of authenticated users.",
164
+ recommendation: "Use CSRF tokens (csurf middleware, Django CSRF, Rails authenticity_token). Set SameSite=Strict on cookies. Use custom headers for API calls.",
165
+ reference: "OWASP CSRF Prevention Cheat Sheet / CWE-352",
166
+ });
167
+ }
168
+ return findings;
169
+ }
170
+ //# sourceMappingURL=authentication.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authentication.js","sourceRoot":"","sources":["../../src/evaluators/authentication.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { Finding } from "../types.js";
2
+ export declare function analyzeBackwardsCompatibility(code: string, language: string): Finding[];
3
+ //# sourceMappingURL=backwards-compatibility.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backwards-compatibility.d.ts","sourceRoot":"","sources":["../../src/evaluators/backwards-compatibility.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAGtC,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,EAAE,CA0JvF"}
@@ -0,0 +1,146 @@
1
+ import { getLineNumbers } from "./shared.js";
2
+ export function analyzeBackwardsCompatibility(code, language) {
3
+ const findings = [];
4
+ let ruleNum = 1;
5
+ const prefix = "COMPAT";
6
+ // No API versioning
7
+ const hasApiRoutes = /app\.(get|post|put|delete|patch)\s*\(\s*["'`]\/api\//gi.test(code);
8
+ const hasVersioning = /\/api\/v\d|\/v\d\/|api-version|x-api-version|accept-version/gi.test(code);
9
+ if (hasApiRoutes && !hasVersioning) {
10
+ findings.push({
11
+ ruleId: `${prefix}-${String(ruleNum++).padStart(3, "0")}`,
12
+ severity: "medium",
13
+ title: "API endpoints without versioning",
14
+ description: "API routes are defined under /api/ without a version prefix (e.g., /api/v1/). Without versioning, any changes to the API risk breaking existing consumers.",
15
+ recommendation: "Add version prefixes to API routes: /api/v1/users. This allows old and new versions to coexist during migration. Use URL, header, or query param versioning.",
16
+ reference: "API Versioning Best Practices / RESTful API Design",
17
+ });
18
+ }
19
+ // Deprecated API indicators without deprecation headers
20
+ const hasDeprecated = /deprecated|@deprecated|obsolete|legacy/gi.test(code);
21
+ const hasDeprecationHeader = /Deprecation|Sunset|X-Deprecated/gi.test(code);
22
+ if (hasDeprecated && !hasDeprecationHeader) {
23
+ findings.push({
24
+ ruleId: `${prefix}-${String(ruleNum++).padStart(3, "0")}`,
25
+ severity: "low",
26
+ title: "Deprecated code without API deprecation headers",
27
+ description: "Code is marked as deprecated in comments or annotations but no HTTP deprecation headers (Deprecation, Sunset) are set. API consumers won't know features are being retired.",
28
+ recommendation: "Set HTTP Deprecation and Sunset headers on deprecated endpoints. Document alternatives. Communicate timeline to consumers.",
29
+ reference: "RFC 8594: The Sunset HTTP Header / API Lifecycle Management",
30
+ });
31
+ }
32
+ // Direct field deletion in response objects
33
+ const deleteFieldPattern = /delete\s+\w+\.\w+/gi;
34
+ const deleteLines = getLineNumbers(code, deleteFieldPattern);
35
+ if (deleteLines.length > 0) {
36
+ findings.push({
37
+ ruleId: `${prefix}-${String(ruleNum++).padStart(3, "0")}`,
38
+ severity: "low",
39
+ title: "Field deletion could break consumers",
40
+ description: "Fields are deleted from objects before sending responses. If this is an API response, removing previously available fields is a breaking change.",
41
+ lineNumbers: deleteLines,
42
+ recommendation: "Instead of deleting fields, use a response DTO/mapper that explicitly selects which fields to include. Version the API when removing fields.",
43
+ reference: "Backwards-Compatible API Evolution",
44
+ });
45
+ }
46
+ // Response type changes (sending different structures)
47
+ const mixedResponsePattern = /res\.json\s*\(\s*(?:\{|\[)/g;
48
+ const responseLines = getLineNumbers(code, /res\.json\s*\(/g);
49
+ if (responseLines.length > 2) {
50
+ findings.push({
51
+ ruleId: `${prefix}-${String(ruleNum++).padStart(3, "0")}`,
52
+ severity: "info",
53
+ title: "Multiple response formats — verify contract consistency",
54
+ description: `Found ${responseLines.length} response points. Verify that all endpoints follow a consistent response envelope (e.g., { data, error, meta }). Inconsistent response shapes are a compatibility hazard.`,
55
+ lineNumbers: responseLines.slice(0, 5),
56
+ recommendation: "Use a consistent response envelope across all endpoints. Define response schemas (OpenAPI/Swagger) to enforce contracts.",
57
+ reference: "API Contract Design / JSON:API Specification",
58
+ });
59
+ }
60
+ // No semver in package version
61
+ const packageVersionPattern = /"version"\s*:\s*"(?:0\.0\.|[^"]*-alpha|[^"]*-beta)/gi;
62
+ const packageVersionLines = getLineNumbers(code, packageVersionPattern);
63
+ if (packageVersionLines.length > 0) {
64
+ findings.push({
65
+ ruleId: `${prefix}-${String(ruleNum++).padStart(3, "0")}`,
66
+ severity: "info",
67
+ title: "Pre-release version — backwards compatibility expectations unclear",
68
+ description: "Package version indicates a pre-release or 0.x version. Consumers may not know what compatibility guarantees exist.",
69
+ lineNumbers: packageVersionLines,
70
+ recommendation: "Document backwards compatibility policy. Use semver: major bumps for breaking changes, minor for features, patch for fixes.",
71
+ reference: "Semantic Versioning (semver.org)",
72
+ });
73
+ }
74
+ // Renamed or removed exports
75
+ const commentedExportPattern = /\/\/\s*export\s+(?:function|class|const|let|type|interface)\s+\w+/g;
76
+ const commentedExportLines = getLineNumbers(code, commentedExportPattern);
77
+ if (commentedExportLines.length > 0) {
78
+ findings.push({
79
+ ruleId: `${prefix}-${String(ruleNum++).padStart(3, "0")}`,
80
+ severity: "medium",
81
+ title: "Commented-out exports may indicate removed API surface",
82
+ description: `Found ${commentedExportLines.length} commented-out export(s). If these were previously published, removing them is a breaking change for consumers.`,
83
+ lineNumbers: commentedExportLines,
84
+ recommendation: "Re-export removed symbols as deprecated wrappers. Mark them @deprecated with a migration guide. Remove only in the next major version.",
85
+ reference: "Semantic Versioning / API Deprecation Lifecycle",
86
+ });
87
+ }
88
+ // Changed function signatures — optional to required parameter
89
+ const requiredAfterOptionalPattern = /\w+\?:\s*\w+[^)]*,\s*\w+\s*:\s*\w+/g;
90
+ const sigChangeLines = getLineNumbers(code, requiredAfterOptionalPattern);
91
+ if (sigChangeLines.length > 0) {
92
+ findings.push({
93
+ ruleId: `${prefix}-${String(ruleNum++).padStart(3, "0")}`,
94
+ severity: "low",
95
+ title: "Function signature with required params after optional — potential breaking change",
96
+ description: "Required parameters placed after optional parameters can break callers who relied on positional arguments.",
97
+ lineNumbers: sigChangeLines,
98
+ recommendation: "Keep required parameters before optional ones. Use options objects for functions with many parameters to allow adding fields without breaking callers.",
99
+ reference: "API Design: Function Signature Evolution",
100
+ });
101
+ }
102
+ // Enum/union type removals
103
+ const enumPattern = /enum\s+\w+\s*\{[^}]*\}/g;
104
+ const enumMatches = code.match(enumPattern) || [];
105
+ const hasDeprecatedEnumComment = /\/\/.*deprecated.*enum|\/\/.*removed.*value/gi.test(code);
106
+ if (enumMatches.length > 0 && hasDeprecatedEnumComment) {
107
+ findings.push({
108
+ ruleId: `${prefix}-${String(ruleNum++).padStart(3, "0")}`,
109
+ severity: "medium",
110
+ title: "Enum value changes may break consumers",
111
+ description: "Enums with deprecated or removed values detected. Removing enum values is a breaking change for anything serializing or deserializing these values.",
112
+ recommendation: "Never remove enum values in minor releases. Mark values as deprecated. If numeric, keep the slot allocated. Provide migration mapping for removed values.",
113
+ reference: "Breaking Changes in Enums / Protocol Buffers Reserved Fields",
114
+ });
115
+ }
116
+ // Changing HTTP methods on endpoints (POST mapping doing DELETE work, etc.)
117
+ const deleteViaPostPattern = /app\.post\s*\([^)]*(?:delete|remove|destroy)/gi;
118
+ const deleteViaPostLines = getLineNumbers(code, deleteViaPostPattern);
119
+ if (deleteViaPostLines.length > 0) {
120
+ findings.push({
121
+ ruleId: `${prefix}-${String(ruleNum++).padStart(3, "0")}`,
122
+ severity: "low",
123
+ title: "HTTP method mismatch — destructive action via POST",
124
+ description: "Destructive operations (delete, remove) are exposed via POST instead of DELETE. If these were originally DELETE endpoints, the method change breaks REST clients.",
125
+ lineNumbers: deleteViaPostLines,
126
+ recommendation: "Use appropriate HTTP methods: DELETE for removal, PUT/PATCH for updates. If migrating methods, keep the old method working during a deprecation period.",
127
+ reference: "RESTful API Design / HTTP Method Semantics",
128
+ });
129
+ }
130
+ // Breaking serialization changes (renaming JSON fields)
131
+ const fieldRenamePattern = /\/\/\s*(?:renamed|was|previously|old name|formerly)\s*[:=]?\s*\w+/gi;
132
+ const fieldRenameLines = getLineNumbers(code, fieldRenamePattern);
133
+ if (fieldRenameLines.length > 0) {
134
+ findings.push({
135
+ ruleId: `${prefix}-${String(ruleNum++).padStart(3, "0")}`,
136
+ severity: "medium",
137
+ title: "Possible field rename — breaking serialization change",
138
+ description: `Found ${fieldRenameLines.length} comment(s) suggesting renamed fields. Renaming JSON response fields breaks API clients that depend on the old names.`,
139
+ lineNumbers: fieldRenameLines,
140
+ recommendation: "Include both old and new field names during a transition period. Mark the old field as deprecated. Remove only in the next major version.",
141
+ reference: "API Versioning / Backwards-Compatible JSON Evolution",
142
+ });
143
+ }
144
+ return findings;
145
+ }
146
+ //# sourceMappingURL=backwards-compatibility.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backwards-compatibility.js","sourceRoot":"","sources":["../../src/evaluators/backwards-compatibility.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { Finding } from "../types.js";
2
+ export declare function analyzeCaching(code: string, language: string): Finding[];
3
+ //# sourceMappingURL=caching.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"caching.d.ts","sourceRoot":"","sources":["../../src/evaluators/caching.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAGtC,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,EAAE,CA0JxE"}