AIMLxDIV commited on
Commit
e5b8abf
·
unverified ·
2 Parent(s): c120715eb43917

Merge pull request #15 from DsThakurRawat/add-env-bank

Browse files
Files changed (1) hide show
  1. codereview_env/scenario_bank.py +887 -0
codereview_env/scenario_bank.py ADDED
@@ -0,0 +1,887 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import random
2
+ from codereview_env.models import (
3
+ Scenario, FileChange, GroundTruthIssue, Category, Severity, TaskId, Verdict
4
+ )
5
+
6
+ def get_scenario(task_id: TaskId, seed: int) -> Scenario:
7
+ rng = random.Random(seed)
8
+ bank = SCENARIOS.get(task_id, [])
9
+ if not bank:
10
+ raise ValueError(f"No scenarios found for task: {task_id}")
11
+
12
+ import hashlib
13
+ import json
14
+ idx = rng.randint(0, len(bank) - 1)
15
+ scenario = bank[idx]
16
+ # Dynamic hash as per roadmap
17
+ scenario_dict = scenario.model_dump()
18
+ content = json.dumps(scenario_dict, sort_keys=True).encode()
19
+ scenario.hash = hashlib.md5(content).hexdigest()
20
+ return scenario
21
+
22
+ # --- BUG DETECTION SCENARIOS (10) ---
23
+ BUG_SCENARIOS = [
24
+ Scenario(
25
+ task_id=TaskId.BUG_DETECTION,
26
+ pr_title="Fix: Off-by-one error in list processing",
27
+ pr_description="Processing elements in the list but missing the last one due to range(len(x)-1).",
28
+ files_changed=[
29
+ FileChange(
30
+ filename="utils.py",
31
+ patch="""@@ -10,1 +10,1 @@
32
+ - for i in range(len(items) - 1):
33
+ + for i in range(len(items)):
34
+ + print(items[i])""",
35
+ additions=2,
36
+ deletions=1
37
+ )
38
+ ],
39
+ ground_truth_issues=[
40
+ GroundTruthIssue(
41
+ id="bug_001",
42
+ category=Category.BUG,
43
+ severity=Severity.MEDIUM,
44
+ filename="utils.py",
45
+ line_number=10,
46
+ description="Off-by-one error in list processing loop. Should use range(len(items)).",
47
+ keywords=["off-by-one", "index", "out of range", "boundary", "loop"]
48
+ )
49
+ ],
50
+ hash="bug_001_h"
51
+ ),
52
+ Scenario(
53
+ task_id=TaskId.BUG_DETECTION,
54
+ pr_title="Add: Mutable default argument to fetch_data",
55
+ pr_description="New helper to fetch data with a default empty list for items.",
56
+ files_changed=[
57
+ FileChange(
58
+ filename="api_client.py",
59
+ patch="""@@ -5,1 +5,1 @@
60
+ -def fetch_data(url: str, headers: dict = None):
61
+ +def fetch_data(url: str, items: list = []):
62
+ + items.append(url)
63
+ + return items""",
64
+ additions=2,
65
+ deletions=1
66
+ )
67
+ ],
68
+ ground_truth_issues=[
69
+ GroundTruthIssue(
70
+ id="bug_002",
71
+ category=Category.BUG,
72
+ severity=Severity.HIGH,
73
+ filename="api_client.py",
74
+ line_number=5,
75
+ description="Mutable default argument in Python. Items list will be shared across calls.",
76
+ keywords=["mutable", "default", "argument", "persistent", "shared state"]
77
+ )
78
+ ],
79
+ hash="bug_002_h"
80
+ ),
81
+ Scenario(
82
+ task_id=TaskId.BUG_DETECTION,
83
+ pr_title="None dereference in user lookup",
84
+ pr_description="Lookup user by ID and access properties without guard.",
85
+ files_changed=[
86
+ FileChange(
87
+ filename="auth.py",
88
+ patch="""@@ -15,1 +15,2 @@
89
+ def get_user_role(uid):
90
+ - user = db.users.get(uid)
91
+ + user = db.users.get(uid)
92
+ + return user.role""",
93
+ additions=1,
94
+ deletions=1
95
+ )
96
+ ],
97
+ ground_truth_issues=[
98
+ GroundTruthIssue(
99
+ id="bug_003",
100
+ category=Category.BUG,
101
+ severity=Severity.HIGH,
102
+ filename="auth.py",
103
+ line_number=16,
104
+ description="Potential None dereference. user might be None if ID is not found.",
105
+ keywords=["None", "null check", "KeyError", "AttributeError", "guard clause"]
106
+ )
107
+ ],
108
+ hash="bug_003_h"
109
+ ),
110
+ Scenario(
111
+ task_id=TaskId.BUG_DETECTION,
112
+ pr_title="Assignment in conditional",
113
+ pr_description="Check if setting is enabled and update status.",
114
+ files_changed=[
115
+ FileChange(
116
+ filename="config_manager.py",
117
+ patch="""@@ -8,1 +8,1 @@
118
+ - if config.enabled == True:
119
+ + if config.status = "active":
120
+ + process_config(config)""",
121
+ additions=1,
122
+ deletions=1
123
+ )
124
+ ],
125
+ ground_truth_issues=[
126
+ GroundTruthIssue(
127
+ id="bug_004",
128
+ category=Category.BUG,
129
+ severity=Severity.MEDIUM,
130
+ filename="config_manager.py",
131
+ line_number=8,
132
+ description="Assignment operator used in conditional statement. Should be '=='.",
133
+ keywords=["assignment", "comparison", "conditional", "operator", "typo"]
134
+ )
135
+ ],
136
+ hash="bug_004_h"
137
+ ),
138
+ Scenario(
139
+ task_id=TaskId.BUG_DETECTION,
140
+ pr_title="Integer overflow in loop counter",
141
+ pr_description="Counter for processed records doesn't reset.",
142
+ files_changed=[
143
+ FileChange(
144
+ filename="processor.py",
145
+ patch="""@@ -25,1 +25,3 @@
146
+ - processed_count = 0
147
+ + processed_count += 1
148
+ + if processed_count > 1000000:
149
+ + log.warning("High volume")""",
150
+ additions=2,
151
+ deletions=1
152
+ )
153
+ ],
154
+ ground_truth_issues=[
155
+ GroundTruthIssue(
156
+ id="bug_005",
157
+ category=Category.BUG,
158
+ severity=Severity.MEDIUM,
159
+ filename="processor.py",
160
+ line_number=25,
161
+ description="Integer overflow or lack of reset in counter. Can lead to boundary issues.",
162
+ keywords=["overflow", "counter", "integer", "reset", "boundary", "infinite"]
163
+ )
164
+ ],
165
+ hash="bug_005_h"
166
+ ),
167
+ Scenario(
168
+ task_id=TaskId.BUG_DETECTION,
169
+ pr_title="Race condition in cache update",
170
+ pr_description="Parallel threads updating shared cache without locking.",
171
+ files_changed=[
172
+ FileChange(
173
+ filename="cache_store.py",
174
+ patch="""@@ -12,1 +12,2 @@
175
+ def update_cache(key, val):
176
+ - cache[key] = val
177
+ + old_val = cache[key]
178
+ + cache[key] = old_val + val""",
179
+ additions=1,
180
+ deletions=1
181
+ )
182
+ ],
183
+ ground_truth_issues=[
184
+ GroundTruthIssue(
185
+ id="bug_006",
186
+ category=Category.BUG,
187
+ severity=Severity.HIGH,
188
+ filename="cache_store.py",
189
+ line_number=13,
190
+ description="Race condition in cache update. Multiple threads may overwrite each other's increments.",
191
+ keywords=["race condition", "thread", "concurrent", "lock", "atomic", "synchronization"]
192
+ )
193
+ ],
194
+ hash="bug_006_h"
195
+ ),
196
+ Scenario(
197
+ task_id=TaskId.BUG_DETECTION,
198
+ pr_title="Broad exception catch",
199
+ pr_description="Swallow all errors during data import.",
200
+ files_changed=[
201
+ FileChange(
202
+ filename="importer.py",
203
+ patch="""@@ -30,1 +30,2 @@
204
+ - import_data(file)
205
+ + try: import_data(file)
206
+ + except Exception: pass""",
207
+ additions=1,
208
+ deletions=1
209
+ )
210
+ ],
211
+ ground_truth_issues=[
212
+ GroundTruthIssue(
213
+ id="bug_007",
214
+ category=Category.BUG,
215
+ severity=Severity.MEDIUM,
216
+ filename="importer.py",
217
+ line_number=31,
218
+ description="Broad exception catch-all. Swallows all errors including keyboard interrupts.",
219
+ keywords=["exception", "broad", "catch-all", "specific", "silent", "swallow"]
220
+ )
221
+ ],
222
+ hash="bug_007_h"
223
+ ),
224
+ Scenario(
225
+ task_id=TaskId.BUG_DETECTION,
226
+ pr_title="Float equality comparison",
227
+ pr_description="Check if sensor reading is exactly 0.1.",
228
+ files_changed=[
229
+ FileChange(
230
+ filename="sensors.py",
231
+ patch="""@@ -7,1 +7,1 @@
232
+ - if reading < 0.1:
233
+ + if reading == 0.1:
234
+ + trigger_alarm()""",
235
+ additions=1,
236
+ deletions=1
237
+ )
238
+ ],
239
+ ground_truth_issues=[
240
+ GroundTruthIssue(
241
+ id="bug_008",
242
+ category=Category.BUG,
243
+ severity=Severity.LOW,
244
+ filename="sensors.py",
245
+ line_number=7,
246
+ description="Floating point equality comparison is unreliable due to precision.",
247
+ keywords=["float", "equality", "precision", "epsilon", "comparison", "IEEE 754"]
248
+ )
249
+ ],
250
+ hash="bug_008_h"
251
+ ),
252
+ Scenario(
253
+ task_id=TaskId.BUG_DETECTION,
254
+ pr_title="Return inside finally block",
255
+ pr_description="Override potential errors with a success status.",
256
+ files_changed=[
257
+ FileChange(
258
+ filename="worker.py",
259
+ patch="""@@ -44,1 +44,3 @@
260
+ - process()
261
+ + try: process()
262
+ + finally:
263
+ + return "success" """,
264
+ additions=2,
265
+ deletions=1
266
+ )
267
+ ],
268
+ ground_truth_issues=[
269
+ GroundTruthIssue(
270
+ id="bug_009",
271
+ category=Category.BUG,
272
+ severity=Severity.MEDIUM,
273
+ filename="worker.py",
274
+ line_number=46,
275
+ description="Return inside finally block overrides and suppresses exceptions.",
276
+ keywords=["finally", "return", "exception", "control flow", "override", "suppress"]
277
+ )
278
+ ],
279
+ hash="bug_009_h"
280
+ ),
281
+ Scenario(
282
+ task_id=TaskId.BUG_DETECTION,
283
+ pr_title="Type coercion bug",
284
+ pr_description="Compare incoming string ID with integer constant.",
285
+ files_changed=[
286
+ FileChange(
287
+ filename="validator.py",
288
+ patch="""@@ -12,1 +12,1 @@
289
+ - if int(obj_id) == 5:
290
+ + if obj_id == 5:
291
+ + return True""",
292
+ additions=1,
293
+ deletions=1
294
+ )
295
+ ],
296
+ ground_truth_issues=[
297
+ GroundTruthIssue(
298
+ id="bug_010",
299
+ category=Category.BUG,
300
+ severity=Severity.MEDIUM,
301
+ filename="validator.py",
302
+ line_number=12,
303
+ description="Type mismatch: comparing string obj_id with integer 5 will always be False.",
304
+ keywords=["type", "coercion", "comparison", "string", "integer", "implicit"]
305
+ )
306
+ ],
307
+ hash="bug_010_h"
308
+ )
309
+ ]
310
+
311
+ # --- SECURITY AUDIT SCENARIOS (10) ---
312
+ SECURITY_SCENARIOS = [
313
+ Scenario(
314
+ task_id=TaskId.SECURITY_AUDIT,
315
+ pr_title="Security: Implement raw SQL query for performance",
316
+ pr_description="Bypassing ORM for a specific complex query to improve performance.",
317
+ files_changed=[
318
+ FileChange(
319
+ filename="db/queries.py",
320
+ patch="""@@ -42,1 +42,1 @@
321
+ - return User.objects.filter(username=name)
322
+ + return User.objects.raw(f"SELECT * FROM users WHERE username = '{name}'")""",
323
+ additions=1,
324
+ deletions=1
325
+ )
326
+ ],
327
+ ground_truth_issues=[
328
+ GroundTruthIssue(
329
+ id="sec_001",
330
+ category=Category.SECURITY,
331
+ severity=Severity.CRITICAL,
332
+ filename="db/queries.py",
333
+ line_number=42,
334
+ description="SQL injection vulnerability via f-string in raw query. Use parameterized queries.",
335
+ keywords=["SQL injection", "parameterized", "f-string", "raw query", "exploit"]
336
+ )
337
+ ],
338
+ hash="sec_001_h"
339
+ ),
340
+ Scenario(
341
+ task_id=TaskId.SECURITY_AUDIT,
342
+ pr_title="Config: Add development secret key",
343
+ pr_description="Setting a default secret key for local development convenience.",
344
+ files_changed=[
345
+ FileChange(
346
+ filename="settings.py",
347
+ patch="""@@ -20,1 +20,1 @@
348
+ -SECRET_KEY = os.environ.get('SECRET_KEY')
349
+ +SECRET_KEY = "django-insecure-dev-key-12345" """,
350
+ additions=1,
351
+ deletions=1
352
+ )
353
+ ],
354
+ ground_truth_issues=[
355
+ GroundTruthIssue(
356
+ id="sec_002",
357
+ category=Category.SECURITY,
358
+ severity=Severity.HIGH,
359
+ filename="settings.py",
360
+ line_number=20,
361
+ description="Hardcoded secret key in configuration. Should use environment variables.",
362
+ keywords=["hardcoded", "secret", "environment variable", ".env", "credential", "exposure"]
363
+ )
364
+ ],
365
+ hash="sec_002_h"
366
+ ),
367
+ Scenario(
368
+ task_id=TaskId.SECURITY_AUDIT,
369
+ pr_title="JWT: Disable verification for internal testing",
370
+ pr_description="Allow bypassing JWT checks for faster local development loop.",
371
+ files_changed=[
372
+ FileChange(
373
+ filename="tokens.py",
374
+ patch="""@@ -10,1 +10,1 @@
375
+ - payload = jwt.decode(token, secret, algorithms=["HS256"])
376
+ + payload = jwt.decode(token, verify=False, algorithms=["HS256"])""",
377
+ additions=1,
378
+ deletions=1
379
+ )
380
+ ],
381
+ ground_truth_issues=[
382
+ GroundTruthIssue(
383
+ id="sec_003",
384
+ category=Category.SECURITY,
385
+ severity=Severity.CRITICAL,
386
+ filename="tokens.py",
387
+ line_number=10,
388
+ description="JWT decoded without verification. Attackers can bypass authentication.",
389
+ keywords=["JWT", "signature", "verification", "algorithm", "none", "bypass"]
390
+ )
391
+ ],
392
+ hash="sec_003_h"
393
+ ),
394
+ Scenario(
395
+ task_id=TaskId.SECURITY_AUDIT,
396
+ pr_title="XSS: Render user bio directly",
397
+ pr_description="Enabling rich text in user bios by using mark_safe.",
398
+ files_changed=[
399
+ FileChange(
400
+ filename="templates/profile.html",
401
+ patch="""@@ -5,1 +5,1 @@
402
+ - <div class="bio">{{ user.bio }}</div>
403
+ + <div class="bio">{{ user.bio | mark_safe }}</div>""",
404
+ additions=1,
405
+ deletions=1
406
+ )
407
+ ],
408
+ ground_truth_issues=[
409
+ GroundTruthIssue(
410
+ id="sec_004",
411
+ category=Category.SECURITY,
412
+ severity=Severity.HIGH,
413
+ filename="templates/profile.html",
414
+ line_number=5,
415
+ description="Cross-site scripting (XSS) via unescaped template variable. sanitize user input.",
416
+ keywords=["XSS", "cross-site scripting", "mark_safe", "escape", "sanitize", "inject"]
417
+ )
418
+ ],
419
+ hash="sec_004_h"
420
+ ),
421
+ Scenario(
422
+ task_id=TaskId.SECURITY_AUDIT,
423
+ pr_title="File: Open local logs",
424
+ pr_description="New endpoint to read local audit logs based on path.",
425
+ files_changed=[
426
+ FileChange(
427
+ filename="logs_viewer.py",
428
+ patch="""@@ -12,1 +12,2 @@
429
+ def get_log(path):
430
+ - return open('/var/log/app.log').read()
431
+ + return open('/var/log/' + path).read()""",
432
+ additions=1,
433
+ deletions=1
434
+ )
435
+ ],
436
+ ground_truth_issues=[
437
+ GroundTruthIssue(
438
+ id="sec_005",
439
+ category=Category.SECURITY,
440
+ severity=Severity.HIGH,
441
+ filename="logs_viewer.py",
442
+ line_number=13,
443
+ description="Path traversal vulnerability. Allow reading any file using ../ notation.",
444
+ keywords=["path traversal", "directory", "normalization", "join", "sanitize", "escape"]
445
+ )
446
+ ],
447
+ hash="sec_005_h"
448
+ ),
449
+ Scenario(
450
+ task_id=TaskId.SECURITY_AUDIT,
451
+ pr_title="Serialization: Load user state from pickle",
452
+ pr_description="Faster state loading by using pickle format for internal caches.",
453
+ files_changed=[
454
+ FileChange(
455
+ filename="cache_util.py",
456
+ patch="""@@ -8,1 +8,1 @@
457
+ - return json.loads(data)
458
+ + return pickle.loads(data)""",
459
+ additions=1,
460
+ deletions=1
461
+ )
462
+ ],
463
+ ground_truth_issues=[
464
+ GroundTruthIssue(
465
+ id="sec_006",
466
+ category=Category.SECURITY,
467
+ severity=Severity.CRITICAL,
468
+ filename="cache_util.py",
469
+ line_number=8,
470
+ description="Insecure deserialization using pickle leads to Arbitrary Code Execution (RCE).",
471
+ keywords=["deserialization", "pickle", "arbitrary code", "RCE", "untrusted", "injection"]
472
+ )
473
+ ],
474
+ hash="sec_006_h"
475
+ ),
476
+ Scenario(
477
+ task_id=TaskId.SECURITY_AUDIT,
478
+ pr_title="CORS: Allow all origins for API",
479
+ pr_description="Resolving frontend browser errors by allowing all origins.",
480
+ files_changed=[
481
+ FileChange(
482
+ filename="api_gateway.py",
483
+ patch="""@@ -15,1 +15,1 @@
484
+ - allow_origins=["https://myapp.com"],
485
+ + allow_origins=["*"],""",
486
+ additions=1,
487
+ deletions=1
488
+ )
489
+ ],
490
+ ground_truth_issues=[
491
+ GroundTruthIssue(
492
+ id="sec_007",
493
+ category=Category.SECURITY,
494
+ severity=Severity.MEDIUM,
495
+ filename="api_gateway.py",
496
+ line_number=15,
497
+ description="Broad CORS policy (*) allows sensitive data exposure to arbitrary websites.",
498
+ keywords=["CORS", "wildcard", "origin", "cross-origin", "authentication", "header"]
499
+ )
500
+ ],
501
+ hash="sec_007_h"
502
+ ),
503
+ Scenario(
504
+ task_id=TaskId.SECURITY_AUDIT,
505
+ pr_title="Pass: Compare hashes directly",
506
+ pr_description="Faster password check by using native equality.",
507
+ files_changed=[
508
+ FileChange(
509
+ filename="pass_verify.py",
510
+ patch="""@@ -10,1 +10,1 @@
511
+ - return hmac.compare_digest(h1, h2)
512
+ + return h1 == h2""",
513
+ additions=1,
514
+ deletions=1
515
+ )
516
+ ],
517
+ ground_truth_issues=[
518
+ GroundTruthIssue(
519
+ id="sec_008",
520
+ category=Category.SECURITY,
521
+ severity=Severity.MEDIUM,
522
+ filename="pass_verify.py",
523
+ line_number=10,
524
+ description="Timing attack vulnerability in password comparison. Use constant-time comparison.",
525
+ keywords=["timing attack", "constant time", "hmac", "comparison", "side channel"]
526
+ )
527
+ ],
528
+ hash="sec_008_h"
529
+ ),
530
+ Scenario(
531
+ task_id=TaskId.SECURITY_AUDIT,
532
+ pr_title="Auth: Remove login limit",
533
+ pr_description="Allowing multiple login attempts for users who forgot passwords.",
534
+ files_changed=[
535
+ FileChange(
536
+ filename="login_handler.py",
537
+ patch="""@@ -12,1 +12,0 @@
538
+ - if check_rate_limit(ip): return error()""",
539
+ additions=0,
540
+ deletions=1
541
+ )
542
+ ],
543
+ ground_truth_issues=[
544
+ GroundTruthIssue(
545
+ id="sec_009",
546
+ category=Category.SECURITY,
547
+ severity=Severity.MEDIUM,
548
+ filename="login_handler.py",
549
+ line_number=12,
550
+ description="Missing rate limiting on login endpoint enables brute-force attacks.",
551
+ keywords=["rate limit", "brute force", "throttle", "attempt", "lockout", "login"]
552
+ )
553
+ ],
554
+ hash="sec_009_h"
555
+ ),
556
+ Scenario(
557
+ task_id=TaskId.SECURITY_AUDIT,
558
+ pr_title="Debug: Enable production stack traces",
559
+ pr_description="Better debugging in prod by enabling stack traces for 500 errors.",
560
+ files_changed=[
561
+ FileChange(
562
+ filename="prod_settings.py",
563
+ patch="""@@ -30,1 +30,1 @@
564
+ -DEBUG = False
565
+ +DEBUG = True""",
566
+ additions=1,
567
+ deletions=1
568
+ )
569
+ ],
570
+ ground_truth_issues=[
571
+ GroundTruthIssue(
572
+ id="sec_010",
573
+ category=Category.SECURITY,
574
+ severity=Severity.HIGH,
575
+ filename="prod_settings.py",
576
+ line_number=30,
577
+ description="DEBUG mode enabled in production. Exposes sensitive system information.",
578
+ keywords=["debug", "production", "sensitive", "stack trace", "information disclosure"]
579
+ )
580
+ ],
581
+ hash="sec_010_h"
582
+ )
583
+ ]
584
+
585
+ # --- ARCHITECTURAL REVIEW SCENARIOS (10) ---
586
+ ARCH_SCENARIOS = [
587
+ Scenario(
588
+ task_id=TaskId.ARCHITECTURAL_REVIEW,
589
+ pr_title="Refactor: Frontend direct DB access",
590
+ pr_description="Optimizing frontend by allowing direct database reads for dashboard data.",
591
+ files_changed=[
592
+ FileChange(
593
+ filename="services/dashboard.py",
594
+ patch="""@@ -5,1 +5,4 @@
595
+ - return requests.get(API_URL + '/stats').json()
596
+ + import psycopg2
597
+ + conn = psycopg2.connect(DB_URL)
598
+ + cur = conn.cursor()
599
+ + cur.execute('SELECT * FROM stats')
600
+ + return cur.fetchall()""",
601
+ additions=5,
602
+ deletions=1
603
+ )
604
+ ],
605
+ ground_truth_issues=[
606
+ GroundTruthIssue(
607
+ id="arch_001",
608
+ category=Category.ARCHITECTURE,
609
+ severity=Severity.CRITICAL,
610
+ filename="services/dashboard.py",
611
+ line_number=5,
612
+ description="Frontend service calling database directly bypassing the API layer. Violates separation of concerns.",
613
+ keywords=["direct access", "coupling", "separation of concerns", "architectural violation"],
614
+ required_verdict=Verdict.REQUEST_CHANGES
615
+ )
616
+ ],
617
+ hash="arch_001_h"
618
+ ),
619
+ Scenario(
620
+ task_id=TaskId.ARCHITECTURAL_REVIEW,
621
+ pr_title="Sync: HTTP call inside event handler",
622
+ pr_description="Ensuring user status is verified during login event processing.",
623
+ files_changed=[
624
+ FileChange(
625
+ filename="handlers/events.py",
626
+ patch="""@@ -15,1 +15,2 @@
627
+ def on_user_login(user_id):
628
+ - log.info(f"User {user_id} logged in")
629
+ + resp = requests.get(f"http://auth-service/verify/{user_id}")
630
+ + log.info(f"User {user_id} logged in: {resp.status_code}")""",
631
+ additions=2,
632
+ deletions=1
633
+ )
634
+ ],
635
+ ground_truth_issues=[
636
+ GroundTruthIssue(
637
+ id="arch_002",
638
+ category=Category.ARCHITECTURE,
639
+ severity=Severity.HIGH,
640
+ filename="handlers/events.py",
641
+ line_number=15,
642
+ description="Synchronous HTTP call inside event handler blocks the event loop.",
643
+ keywords=["synchronous", "blocking", "event loop", "async", "non-blocking", "timeout"],
644
+ required_verdict=Verdict.REQUEST_CHANGES
645
+ )
646
+ ],
647
+ hash="arch_002_h"
648
+ ),
649
+ Scenario(
650
+ task_id=TaskId.ARCHITECTURAL_REVIEW,
651
+ pr_title="Reliability: Direct API call without retries",
652
+ pr_description="Call downstream billing service directly.",
653
+ files_changed=[
654
+ FileChange(
655
+ filename="billing_proxy.py",
656
+ patch="""@@ -10,1 +10,1 @@
657
+ - return resiliency.call_with_retry(BILLING_URL)
658
+ + return requests.post(BILLING_URL, data=payload)""",
659
+ additions=1,
660
+ deletions=1
661
+ )
662
+ ],
663
+ ground_truth_issues=[
664
+ GroundTruthIssue(
665
+ id="arch_003",
666
+ category=Category.ARCHITECTURE,
667
+ severity=Severity.MEDIUM,
668
+ filename="billing_proxy.py",
669
+ line_number=10,
670
+ description="Missing retry logic and circuit breaker on external API call.",
671
+ keywords=["retry", "circuit breaker", "resilience", "idempotent", "backoff", "failure"],
672
+ required_verdict=Verdict.REQUEST_CHANGES
673
+ )
674
+ ],
675
+ hash="arch_003_h"
676
+ ),
677
+ Scenario(
678
+ task_id=TaskId.ARCHITECTURAL_REVIEW,
679
+ pr_title="Design: Implement GlobalManager",
680
+ pr_description="Consolidating all managers into one for easier access.",
681
+ files_changed=[
682
+ FileChange(
683
+ filename="app_core.py",
684
+ patch="""@@ -1,1 +1,4 @@
685
+ -class App: pass
686
+ +class GlobalManager:
687
+ + def handle_auth(self): pass
688
+ + def handle_billing(self): pass
689
+ + def handle_users(self): pass""",
690
+ additions=4,
691
+ deletions=1
692
+ )
693
+ ],
694
+ ground_truth_issues=[
695
+ GroundTruthIssue(
696
+ id="arch_004",
697
+ category=Category.ARCHITECTURE,
698
+ severity=Severity.MEDIUM,
699
+ filename="app_core.py",
700
+ line_number=2,
701
+ description="God object pattern: one class handles unrelated domains (auth, billing, users).",
702
+ keywords=["single responsibility", "god object", "cohesion", "separation", "refactor"],
703
+ required_verdict=Verdict.REQUEST_CHANGES
704
+ )
705
+ ],
706
+ hash="arch_004_h"
707
+ ),
708
+ Scenario(
709
+ task_id=TaskId.ARCHITECTURAL_REVIEW,
710
+ pr_title="Db: Fetch users in loop",
711
+ pr_description="Process audit for all users one by one.",
712
+ files_changed=[
713
+ FileChange(
714
+ filename="audit_job.py",
715
+ patch="""@@ -5,2 +5,2 @@
716
+ - users = User.objects.all().prefetch_related('logs')
717
+ - for u in users: process(u)
718
+ + for u_id in user_ids:
719
+ + user = User.objects.get(id=u_id)
720
+ + process(user)""",
721
+ additions=2,
722
+ deletions=2
723
+ )
724
+ ],
725
+ ground_truth_issues=[
726
+ GroundTruthIssue(
727
+ id="arch_005",
728
+ category=Category.ARCHITECTURE,
729
+ severity=Severity.HIGH,
730
+ filename="audit_job.py",
731
+ line_number=6,
732
+ description="N+1 query problem: fetching user objects inside a loop.",
733
+ keywords=["N+1", "query", "loop", "batch", "eager load", "select_related"],
734
+ required_verdict=Verdict.REQUEST_CHANGES
735
+ )
736
+ ],
737
+ hash="arch_005_h"
738
+ ),
739
+ Scenario(
740
+ task_id=TaskId.ARCHITECTURAL_REVIEW,
741
+ pr_title="Api: Get all logs endpoint",
742
+ pr_description="Simple endpoint to fetch current log state.",
743
+ files_changed=[
744
+ FileChange(
745
+ filename="handlers/api.py",
746
+ patch="""@@ -20,1 +20,1 @@
747
+ -def get_logs(page, limit): return db.logs.all()[page*limit:(page+1)*limit]
748
+ +def get_logs(): return db.logs.all()""",
749
+ additions=1,
750
+ deletions=1
751
+ )
752
+ ],
753
+ ground_truth_issues=[
754
+ GroundTruthIssue(
755
+ id="arch_006",
756
+ category=Category.ARCHITECTURE,
757
+ severity=Severity.MEDIUM,
758
+ filename="handlers/api.py",
759
+ line_number=20,
760
+ description="Missing pagination on endpoint. Can cause memory exhaustion on large datasets.",
761
+ keywords=["pagination", "limit", "offset", "memory", "unbounded", "cursor"],
762
+ required_verdict=Verdict.REQUEST_CHANGES
763
+ )
764
+ ],
765
+ hash="arch_006_h"
766
+ ),
767
+ Scenario(
768
+ task_id=TaskId.ARCHITECTURAL_REVIEW,
769
+ pr_title="File: Upload blocking",
770
+ pr_description="Directly saving large file uploads to disk in request thread.",
771
+ files_changed=[
772
+ FileChange(
773
+ filename="upload_service.py",
774
+ patch="""@@ -12,1 +12,1 @@
775
+ - await background_save(file)
776
+ + file.save('/tmp/large_file')""",
777
+ additions=1,
778
+ deletions=1
779
+ )
780
+ ],
781
+ ground_truth_issues=[
782
+ GroundTruthIssue(
783
+ id="arch_007",
784
+ category=Category.ARCHITECTURE,
785
+ severity=Severity.MEDIUM,
786
+ filename="upload_service.py",
787
+ line_number=13,
788
+ description="Synchronous file upload blocking the request thread. Use background tasks.",
789
+ keywords=["async", "upload", "background task", "streaming", "thread", "non-blocking"],
790
+ required_verdict=Verdict.REQUEST_CHANGES
791
+ )
792
+ ],
793
+ hash="arch_007_h"
794
+ ),
795
+ Scenario(
796
+ task_id=TaskId.ARCHITECTURAL_REVIEW,
797
+ pr_title="Payment: Direct mutation",
798
+ pr_description="Update balance directly on payment request.",
799
+ files_changed=[
800
+ FileChange(
801
+ filename="checkout.py",
802
+ patch="""@@ -8,1 +8,1 @@
803
+ - process_payment_with_idempotency(req)
804
+ + user.balance -= req.amount""",
805
+ additions=1,
806
+ deletions=1
807
+ )
808
+ ],
809
+ ground_truth_issues=[
810
+ GroundTruthIssue(
811
+ id="arch_008",
812
+ category=Category.ARCHITECTURE,
813
+ severity=Severity.HIGH,
814
+ filename="checkout.py",
815
+ line_number=8,
816
+ description="Missing idempotency key on payment mutation endpoint. Dangerous on retries.",
817
+ keywords=["idempotency", "duplicate", "payment", "retry", "key", "mutation"],
818
+ required_verdict=Verdict.REQUEST_CHANGES
819
+ )
820
+ ],
821
+ hash="arch_008_h"
822
+ ),
823
+ Scenario(
824
+ task_id=TaskId.ARCHITECTURAL_REVIEW,
825
+ pr_title="Service: Shared DB state",
826
+ pr_description="Service B updates Service A's table directly for speed.",
827
+ files_changed=[
828
+ FileChange(
829
+ filename="service_b/sync.py",
830
+ patch="""@@ -22,1 +22,1 @@
831
+ - send_event_to_service_a(data)
832
+ + db.execute('UPDATE service_a_table SET x = 1')""",
833
+ additions=1,
834
+ deletions=1
835
+ )
836
+ ],
837
+ ground_truth_issues=[
838
+ GroundTruthIssue(
839
+ id="arch_009",
840
+ category=Category.ARCHITECTURE,
841
+ severity=Severity.HIGH,
842
+ filename="service_b/sync.py",
843
+ line_number=23,
844
+ description="Shared mutable state between microservices via direct DB write. Breaks encapsulation.",
845
+ keywords=["shared state", "microservice", "event", "eventual consistency", "ownership", "coupling"],
846
+ required_verdict=Verdict.REQUEST_CHANGES
847
+ )
848
+ ],
849
+ hash="arch_009_h"
850
+ ),
851
+ Scenario(
852
+ task_id=TaskId.ARCHITECTURAL_REVIEW,
853
+ pr_title="Clean: Domain logic in handler",
854
+ pr_description="Complex interest calculation directly in the GET endpoint.",
855
+ files_changed=[
856
+ FileChange(
857
+ filename="api/finance.py",
858
+ patch="""@@ -15,1 +15,3 @@
859
+ - return finance_service.calc_interest(u)
860
+ + interest = u.balance * 0.05
861
+ + if u.type == 'GOLD': interest += 10
862
+ + return interest""",
863
+ additions=3,
864
+ deletions=1
865
+ )
866
+ ],
867
+ ground_truth_issues=[
868
+ GroundTruthIssue(
869
+ id="arch_010",
870
+ category=Category.ARCHITECTURE,
871
+ severity=Severity.MEDIUM,
872
+ filename="api/finance.py",
873
+ line_number=16,
874
+ description="Clean architecture violation: domain logic leaked into HTTP handler.",
875
+ keywords=["clean architecture", "domain", "handler", "concern", "presentation", "business logic"],
876
+ required_verdict=Verdict.REQUEST_CHANGES
877
+ )
878
+ ],
879
+ hash="arch_010_h"
880
+ )
881
+ ]
882
+
883
+ SCENARIOS = {
884
+ TaskId.BUG_DETECTION: BUG_SCENARIOS,
885
+ TaskId.SECURITY_AUDIT: SECURITY_SCENARIOS,
886
+ TaskId.ARCHITECTURAL_REVIEW: ARCH_SCENARIOS,
887
+ }