code-review-env / env /data_generator.py
ragavrida's picture
Fix all 4 remaining issues for hackathon
7e55f05
Raw
History Blame Contribute Delete
81.5 kB
"""
Data Generator for CodeReviewEnv
Generates realistic synthetic pull requests with actual code diffs.
The FIXED_TEST_SUITE provides 50 pre-generated PRs at seed=42 for
deterministic evaluation — all episodes draw from this fixed suite.
Bug categories and ground truth severity mapping:
sql_injection → critical
security_vulnerability → critical
race_condition → high
null_pointer → high
logic_error → medium
missing_error_handling → medium
performance_issue → low
style_only → none
Author experience affects bug probability:
junior: 70% chance of bug (weighted: null_pointer, missing_error_handling)
mid: 40% chance of bug (any category)
senior: 15% chance of bug (weighted: performance_issue, style_only)
"""
import random
from typing import List, Dict, Optional, Tuple
from env.models import PRFile, Observation
# ─── Bug category → ground truth severity ────────────────────────────────────
BUG_SEVERITY_MAP: Dict[str, str] = {
"sql_injection": "critical",
"security_vulnerability": "critical",
"race_condition": "high",
"null_pointer": "high",
"logic_error": "medium",
"missing_error_handling": "medium",
"performance_issue": "low",
"style_only": "none",
}
# Ordered severity levels for adjacency calculations
SEVERITY_ORDER: List[str] = ["critical", "high", "medium", "low", "none"]
# Keywords associated with each bug category — used by grader_hard for
# specificity scoring via keyword matching (no LLM calls)
BUG_KEYWORDS: Dict[str, List[str]] = {
"null_pointer": ["null", "None", "NullPointerException", "check", "guard", "nil"],
"sql_injection": ["injection", "parameterize", "prepared", "sanitize", "escape", "query"],
"race_condition": ["race", "lock", "mutex", "atomic", "thread-safe", "concurrent", "sync"],
"logic_error": ["condition", "branch", "edge case", "off-by-one", "boundary", "logic"],
"missing_error_handling": ["exception", "catch", "error", "handle", "try", "raise"],
"security_vulnerability": ["auth", "token", "encrypt", "hash", "expose", "secret", "leak"],
"performance_issue": ["O(n)", "complexity", "cache", "index", "query", "optimize", "loop"],
"style_only": ["naming", "format", "indent", "style", "convention"],
}
# Actionability keywords — used by grader_hard to score whether comments
# contain concrete suggestions (empirically selected from code review corpora)
ACTIONABILITY_KEYWORDS: List[str] = [
"use", "replace", "add", "remove", "consider", "should", "instead",
"refactor", "extract", "avoid", "change", "move", "wrap",
]
# ─── PR Templates ────────────────────────────────────────────────────────────
# Each template is a complete PR with realistic code diff, ground truth bug
# info, and pre-annotated human labels for reliability analysis.
PR_TEMPLATES: List[Dict] = [
# ── 1. Java null pointer ────────────────────────────────────────────
{
"pr_id": "PR-001",
"title": "Fix null pointer in UserService.java",
"description": "Refactored UserService to handle user lookup. Added caching for frequently accessed users.",
"author_experience": "junior",
"language": "java",
"filename": "src/main/java/com/app/UserService.java",
"diff": '''@@ -45,12 +45,18 @@ public class UserService {
+ public User getUserProfile(String userId) {
+ User user = userRepository.findById(userId);
+ // BUG: No null check — user could be null if not found
+ String displayName = user.getFirstName() + " " + user.getLastName();
+ user.setDisplayName(displayName);
+ cacheManager.put(userId, user);
+ return user;
+ }
+
+ public void updateUserEmail(String userId, String email) {
+ User user = userRepository.findById(userId);
+ user.setEmail(email); // BUG: same null pointer pattern
+ userRepository.save(user);
+ }''',
"lines_changed": 14,
"has_tests": False,
"bug_category": "null_pointer",
"ground_truth_severity": "high",
"bug_lines": [48, 56], # lines where bugs exist in the diff
"human_labels": ["high", "high", "high"],
"human_agreement": 1.0,
"cohen_kappa": 1.0,
},
# ── 2. Python missing error handling ────────────────────────────────
{
"pr_id": "PR-002",
"title": "Add rate limiting to /api/auth",
"description": "Implemented token bucket rate limiter for authentication endpoints to prevent brute force attacks.",
"author_experience": "mid",
"language": "python",
"filename": "api/auth/rate_limiter.py",
"diff": '''@@ -1,0 +1,28 @@
+import time
+from collections import defaultdict
+
+class RateLimiter:
+ def __init__(self, max_requests=100, window_seconds=60):
+ self.max_requests = max_requests
+ self.window_seconds = window_seconds
+ self.requests = defaultdict(list)
+
+ def is_allowed(self, client_ip):
+ now = time.time()
+ # BUG: No error handling if client_ip is None or empty
+ window_start = now - self.window_seconds
+ self.requests[client_ip] = [
+ t for t in self.requests[client_ip] if t > window_start
+ ]
+ if len(self.requests[client_ip]) >= self.max_requests:
+ return False
+ self.requests[client_ip].append(now)
+ return True
+
+ def get_remaining(self, client_ip):
+ # BUG: doesn't handle KeyError if client never made a request
+ count = len(self.requests[client_ip])
+ return max(0, self.max_requests - count)''',
"lines_changed": 25,
"has_tests": False,
"bug_category": "missing_error_handling",
"ground_truth_severity": "medium",
"bug_lines": [12, 23],
"human_labels": ["medium", "medium", "high"],
"human_agreement": 0.67,
"cohen_kappa": 0.72,
},
# ── 3. Python SQL injection ─────────────────────────────────────────
{
"pr_id": "PR-003",
"title": "Optimize database queries in ProductRepository",
"description": "Added search functionality with direct SQL for performance. Bypasses ORM overhead for complex queries.",
"author_experience": "senior",
"language": "python",
"filename": "repositories/product_repo.py",
"diff": '''@@ -32,6 +32,22 @@ class ProductRepository:
+ def search_products(self, query_text, category=None):
+ """Fast product search bypassing ORM for performance."""
+ # BUG: SQL injection — string interpolation in query
+ sql = f"SELECT * FROM products WHERE name LIKE '%{query_text}%'"
+ if category:
+ sql += f" AND category = '{category}'"
+ cursor = self.db.execute(sql)
+ return [dict(row) for row in cursor.fetchall()]
+
+ def bulk_update_prices(self, updates):
+ """Batch price update for efficiency."""
+ for product_id, new_price in updates:
+ # BUG: Another SQL injection vector
+ self.db.execute(
+ f"UPDATE products SET price = {new_price} WHERE id = '{product_id}'"
+ )
+ self.db.commit()''',
"lines_changed": 17,
"has_tests": True,
"bug_category": "sql_injection",
"ground_truth_severity": "critical",
"bug_lines": [36, 46],
"human_labels": ["critical", "critical", "critical"],
"human_agreement": 1.0,
"cohen_kappa": 1.0,
},
# ── 4. JavaScript SQL injection ─────────────────────────────────────
{
"pr_id": "PR-004",
"title": "Add user input validation",
"description": "Added server-side validation for user registration. Validates email format and password strength.",
"author_experience": "junior",
"language": "javascript",
"filename": "routes/users.js",
"diff": '''@@ -15,4 +15,26 @@ const express = require('express');
+router.post('/register', async (req, res) => {
+ const { username, email, password } = req.body;
+
+ // Validate email format
+ if (!email.includes('@')) {
+ return res.status(400).json({ error: 'Invalid email' });
+ }
+
+ // BUG: SQL injection — concatenating user input into query
+ const checkQuery = `SELECT * FROM users WHERE username = '${username}'`;
+ const existing = await db.query(checkQuery);
+
+ if (existing.rows.length > 0) {
+ return res.status(409).json({ error: 'Username taken' });
+ }
+
+ // BUG: Password stored in plaintext — no hashing
+ const insertQuery = `INSERT INTO users (username, email, password)
+ VALUES ('${username}', '${email}', '${password}')`;
+ await db.query(insertQuery);
+
+ res.status(201).json({ message: 'User created' });
+});''',
"lines_changed": 22,
"has_tests": False,
"bug_category": "sql_injection",
"ground_truth_severity": "critical",
"bug_lines": [24, 32],
"human_labels": ["critical", "critical", "critical"],
"human_agreement": 1.0,
"cohen_kappa": 1.0,
},
# ── 5. Go race condition ────────────────────────────────────────────
{
"pr_id": "PR-005",
"title": "Fix race condition in cache invalidation",
"description": "Updated cache invalidation to handle concurrent access patterns. Added TTL-based expiry.",
"author_experience": "mid",
"language": "go",
"filename": "pkg/cache/manager.go",
"diff": '''@@ -18,6 +18,30 @@ type CacheManager struct {
+func (cm *CacheManager) Get(key string) (interface{}, bool) {
+ // BUG: No mutex lock — concurrent reads/writes cause data race
+ entry, exists := cm.store[key]
+ if !exists {
+ return nil, false
+ }
+ if time.Now().After(entry.ExpiresAt) {
+ // BUG: Deleting without lock while other goroutines may read
+ delete(cm.store, key)
+ return nil, false
+ }
+ return entry.Value, true
+}
+
+func (cm *CacheManager) Set(key string, value interface{}, ttl time.Duration) {
+ // BUG: No mutex lock on write — race with Get/Delete
+ cm.store[key] = CacheEntry{
+ Value: value,
+ ExpiresAt: time.Now().Add(ttl),
+ }
+}
+
+func (cm *CacheManager) Delete(key string) {
+ delete(cm.store, key)
+}''',
"lines_changed": 24,
"has_tests": True,
"bug_category": "race_condition",
"ground_truth_severity": "high",
"bug_lines": [20, 27, 34],
"human_labels": ["high", "high", "critical"],
"human_agreement": 0.67,
"cohen_kappa": 0.65,
},
# ── 6. Python security vulnerability ────────────────────────────────
{
"pr_id": "PR-006",
"title": "Refactor authentication middleware",
"description": "Simplified auth middleware and added JWT token verification. Moved secret key to config.",
"author_experience": "senior",
"language": "python",
"filename": "middleware/auth.py",
"diff": '''@@ -5,8 +5,28 @@ from functools import wraps
+import jwt
+import os
+
+# BUG: Hardcoded secret key — should use env var or vault
+SECRET_KEY = "super_secret_key_12345"
+
+def verify_token(token):
+ """Verify JWT token and return payload."""
+ try:
+ payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
+ return payload
+ except jwt.ExpiredSignatureError:
+ return None
+ # BUG: Missing InvalidTokenError handling — crashes on malformed tokens
+
+def require_auth(f):
+ @wraps(f)
+ def decorated(*args, **kwargs):
+ token = request.headers.get("Authorization", "").replace("Bearer ", "")
+ # BUG: Token value exposed in debug log
+ print(f"DEBUG: verifying token {token}")
+ payload = verify_token(token)
+ if not payload:
+ return jsonify({"error": "Unauthorized"}), 401
+ return f(*args, **kwargs)
+ return decorated''',
"lines_changed": 23,
"has_tests": False,
"bug_category": "security_vulnerability",
"ground_truth_severity": "critical",
"bug_lines": [9, 19, 25],
"human_labels": ["critical", "critical", "critical"],
"human_agreement": 1.0,
"cohen_kappa": 1.0,
},
# ── 7. JavaScript logic error ───────────────────────────────────────
{
"pr_id": "PR-007",
"title": "Add pagination to list endpoints",
"description": "Implemented cursor-based pagination for all list endpoints. Added page_size parameter.",
"author_experience": "mid",
"language": "javascript",
"filename": "controllers/listController.js",
"diff": '''@@ -10,4 +10,28 @@ const { Op } = require('sequelize');
+async function listItems(req, res) {
+ const page = parseInt(req.query.page) || 1;
+ const pageSize = parseInt(req.query.page_size) || 20;
+
+ // BUG: Off-by-one error — first page skips first item
+ const offset = page * pageSize;
+ // Should be: (page - 1) * pageSize
+
+ const { count, rows } = await Item.findAndCountAll({
+ limit: pageSize,
+ offset: offset,
+ order: [['createdAt', 'DESC']],
+ });
+
+ // BUG: Total pages calculation wrong for exact multiples
+ const totalPages = Math.floor(count / pageSize);
+ // Should be: Math.ceil(count / pageSize)
+
+ res.json({
+ items: rows,
+ pagination: {
+ page,
+ pageSize,
+ totalPages,
+ totalItems: count,
+ },
+ });
+}''',
"lines_changed": 24,
"has_tests": True,
"bug_category": "logic_error",
"ground_truth_severity": "medium",
"bug_lines": [15, 24],
"human_labels": ["medium", "medium", "medium"],
"human_agreement": 1.0,
"cohen_kappa": 1.0,
},
# ── 8. Python style only ────────────────────────────────────────────
{
"pr_id": "PR-008",
"title": "Update README formatting",
"description": "Cleaned up README formatting, fixed markdown tables, and updated badge URLs.",
"author_experience": "senior",
"language": "python",
"filename": "utils/formatter.py",
"diff": '''@@ -1,15 +1,15 @@
-def formatUserName(firstName, lastName):
- """format the user name"""
- FullName = firstName + " " + lastName
- return FullName
+def format_user_name(first_name, last_name):
+ """Format the user's display name from first and last name components."""
+ full_name = first_name + " " + last_name
+ return full_name
-def getUserAge(birthYear):
- import datetime
- currentYear = datetime.datetime.now().year
- AGE = currentYear - birthYear
- return AGE
+def get_user_age(birth_year):
+ """Calculate user age from birth year."""
+ import datetime
+ current_year = datetime.datetime.now().year
+ age = current_year - birth_year
+ return age''',
"lines_changed": 12,
"has_tests": True,
"bug_category": "style_only",
"ground_truth_severity": "none",
"bug_lines": [],
"human_labels": ["none", "none", "none"],
"human_agreement": 1.0,
"cohen_kappa": 1.0,
},
# ── 9. Java missing error handling ──────────────────────────────────
{
"pr_id": "PR-009",
"title": "Add file upload endpoint",
"description": "New endpoint for uploading user profile images. Supports JPEG and PNG up to 5MB.",
"author_experience": "junior",
"language": "java",
"filename": "src/main/java/com/app/FileUploadController.java",
"diff": '''@@ -20,6 +20,30 @@ public class FileUploadController {
+ @PostMapping("/upload")
+ public ResponseEntity<String> uploadFile(@RequestParam("file") MultipartFile file) {
+ String filename = file.getOriginalFilename();
+ // BUG: No file type validation — accepts any file type
+ // BUG: No file size check despite 5MB limit in description
+
+ String uploadDir = "/uploads/" + filename;
+ // BUG: Path traversal vulnerability — filename could contain ../
+
+ try {
+ File dest = new File(uploadDir);
+ file.transferTo(dest);
+ } catch (IOException e) {
+ // BUG: Swallows exception — returns 200 even on failure
+ System.out.println("Upload failed");
+ }
+
+ return ResponseEntity.ok("File uploaded: " + filename);
+ }
+
+ @GetMapping("/files/{filename}")
+ public byte[] getFile(@PathVariable String filename) {
+ // BUG: No error handling if file doesn't exist
+ return Files.readAllBytes(Paths.get("/uploads/" + filename));
+ }''',
"lines_changed": 22,
"has_tests": False,
"bug_category": "missing_error_handling",
"ground_truth_severity": "medium",
"bug_lines": [23, 24, 27, 34, 43],
"human_labels": ["high", "medium", "medium"],
"human_agreement": 0.67,
"cohen_kappa": 0.61,
},
# ── 10. Go performance issue ────────────────────────────────────────
{
"pr_id": "PR-010",
"title": "Add metrics aggregation endpoint",
"description": "New endpoint to aggregate user activity metrics. Computes daily, weekly, monthly summaries.",
"author_experience": "senior",
"language": "go",
"filename": "pkg/metrics/aggregator.go",
"diff": '''@@ -12,4 +12,30 @@ type MetricsAggregator struct {
+func (ma *MetricsAggregator) ComputeDailySummary(userID string) (*Summary, error) {
+ // BUG: O(n) scan of entire events table — no index usage, no date filter
+ events, err := ma.db.Query("SELECT * FROM events WHERE user_id = $1", userID)
+ if err != nil {
+ return nil, err
+ }
+
+ summary := &Summary{}
+ for events.Next() {
+ var e Event
+ events.Scan(&e.ID, &e.Type, &e.Timestamp, &e.UserID, &e.Data)
+
+ // BUG: Parsing timestamp in tight loop — should pre-compute
+ t, _ := time.Parse(time.RFC3339, e.Timestamp)
+ if t.Day() == time.Now().Day() {
+ summary.Count++
+ summary.TotalDuration += e.Duration
+ }
+ }
+
+ // BUG: N+1 query — fetches user details for each event separately
+ for i, e := range summary.Events {
+ user, _ := ma.db.QueryRow("SELECT name FROM users WHERE id = $1", e.UserID)
+ summary.Events[i].UserName = user.Name
+ }
+
+ return summary, nil
+}''',
"lines_changed": 26,
"has_tests": True,
"bug_category": "performance_issue",
"ground_truth_severity": "low",
"bug_lines": [14, 25, 32],
"human_labels": ["low", "low", "medium"],
"human_agreement": 0.67,
"cohen_kappa": 0.68,
},
# ── 11. Python null pointer ─────────────────────────────────────────
{
"pr_id": "PR-011",
"title": "Add webhook notification handler",
"description": "Handles incoming webhooks from payment provider. Processes payment success and failure events.",
"author_experience": "junior",
"language": "python",
"filename": "handlers/webhook.py",
"diff": '''@@ -1,0 +1,28 @@
+import json
+from flask import request, jsonify
+
+def handle_webhook():
+ payload = request.get_json()
+
+ # BUG: No null check — payload could be None if body isn't JSON
+ event_type = payload["event_type"]
+ transaction_id = payload["data"]["transaction_id"]
+
+ # BUG: No check if "data" key exists
+ amount = payload["data"]["amount"]
+ customer = payload["data"]["customer"]
+
+ if event_type == "payment_success":
+ # BUG: customer["email"] could be None
+ send_receipt(customer["email"], amount, transaction_id)
+ elif event_type == "payment_failed":
+ notify_support(transaction_id)
+
+ return jsonify({"status": "processed"}), 200
+
+def send_receipt(email, amount, txn_id):
+ """Send payment receipt email."""
+ msg = f"Payment of ${amount} received. Transaction: {txn_id}"
+ # email sending logic
+ print(f"Sending receipt to {email}: {msg}")''',
"lines_changed": 27,
"has_tests": False,
"bug_category": "null_pointer",
"ground_truth_severity": "high",
"bug_lines": [8, 11, 17],
"human_labels": ["high", "high", "medium"],
"human_agreement": 0.67,
"cohen_kappa": 0.72,
},
# ── 12. JavaScript security vulnerability ───────────────────────────
{
"pr_id": "PR-012",
"title": "Add session management",
"description": "Implemented user session handling with cookie-based tokens. Added remember me functionality.",
"author_experience": "junior",
"language": "javascript",
"filename": "middleware/session.js",
"diff": '''@@ -1,0 +1,30 @@
+const crypto = require('crypto');
+
+// BUG: Weak secret — predictable session tokens
+const SESSION_SECRET = 'mysecret123';
+
+function createSession(userId) {
+ // BUG: Using MD5 — cryptographically broken hash function
+ const token = crypto.createHash('md5')
+ .update(userId + SESSION_SECRET + Date.now())
+ .digest('hex');
+
+ return {
+ token,
+ userId,
+ // BUG: No expiry set — sessions live forever
+ createdAt: new Date().toISOString(),
+ };
+}
+
+function validateSession(token) {
+ // BUG: Timing attack vulnerability — string comparison
+ const session = sessions.find(s => s.token === token);
+ if (!session) return null;
+
+ // BUG: No check for session expiry
+ return session.userId;
+}
+
+const sessions = [];
+module.exports = { createSession, validateSession };''',
"lines_changed": 29,
"has_tests": False,
"bug_category": "security_vulnerability",
"ground_truth_severity": "critical",
"bug_lines": [4, 8, 16, 22, 26],
"human_labels": ["critical", "critical", "high"],
"human_agreement": 0.67,
"cohen_kappa": 0.78,
},
# ── 13. Python logic error ──────────────────────────────────────────
{
"pr_id": "PR-013",
"title": "Implement discount calculation engine",
"description": "New pricing engine with tiered discounts. Supports percentage and fixed amount discounts.",
"author_experience": "mid",
"language": "python",
"filename": "pricing/discount_engine.py",
"diff": '''@@ -1,0 +1,32 @@
+class DiscountEngine:
+ TIER_THRESHOLDS = {
+ "bronze": 0,
+ "silver": 1000,
+ "gold": 5000,
+ "platinum": 10000,
+ }
+
+ def calculate_discount(self, order_total, customer_tier, coupon_code=None):
+ discount = 0.0
+
+ # Tier-based discount
+ tier_rates = {"bronze": 0.0, "silver": 0.05, "gold": 0.10, "platinum": 0.15}
+ # BUG: Missing KeyError handling for unknown tier
+ discount += order_total * tier_rates[customer_tier]
+
+ # Coupon discount
+ if coupon_code:
+ coupon_discount = self._lookup_coupon(coupon_code)
+ # BUG: Discounts stack without cap — can exceed order total
+ discount += coupon_discount
+
+ # BUG: Off-by-one in boundary check — gold customers at exactly 5000 get silver rate
+ if order_total >= self.TIER_THRESHOLDS.get(customer_tier, 0):
+ discount *= 1.0 # threshold met, keep discount
+ else:
+ discount *= 0.5 # below threshold, halve it
+
+ return discount
+
+ def _lookup_coupon(self, code):
+ coupons = {"SAVE10": 10.0, "SAVE20": 20.0, "HALF50": 50.0}
+ return coupons.get(code, 0.0)''',
"lines_changed": 32,
"has_tests": True,
"bug_category": "logic_error",
"ground_truth_severity": "medium",
"bug_lines": [15, 21, 24],
"human_labels": ["medium", "medium", "high"],
"human_agreement": 0.67,
"cohen_kappa": 0.72,
},
# ── 14. Go null pointer ─────────────────────────────────────────────
{
"pr_id": "PR-014",
"title": "Add gRPC health check service",
"description": "Implemented standard gRPC health check protocol for k8s liveness and readiness probes.",
"author_experience": "mid",
"language": "go",
"filename": "pkg/health/checker.go",
"diff": '''@@ -10,4 +10,28 @@ type HealthChecker struct {
+func (hc *HealthChecker) Check(ctx context.Context, req *pb.HealthCheckRequest) (*pb.HealthCheckResponse, error) {
+ service := req.GetService()
+
+ // BUG: No nil check on dependency map lookup
+ dep := hc.dependencies[service]
+ // dep could be nil if service name not registered
+
+ status := dep.Status() // BUG: nil pointer dereference if dep is nil
+
+ response := &pb.HealthCheckResponse{
+ Status: status,
+ }
+
+ // Check sub-dependencies
+ for _, subDep := range dep.SubDependencies {
+ // BUG: No nil check on subDep
+ subStatus := subDep.Status()
+ if subStatus != pb.HealthCheckResponse_SERVING {
+ response.Status = pb.HealthCheckResponse_NOT_SERVING
+ }
+ }
+
+ return response, nil
+}''',
"lines_changed": 22,
"has_tests": False,
"bug_category": "null_pointer",
"ground_truth_severity": "high",
"bug_lines": [15, 18, 27],
"human_labels": ["high", "high", "high"],
"human_agreement": 1.0,
"cohen_kappa": 1.0,
},
# ── 15. Python performance issue ────────────────────────────────────
{
"pr_id": "PR-015",
"title": "Add report generation module",
"description": "Generates PDF reports for quarterly analytics. Aggregates data from multiple tables.",
"author_experience": "senior",
"language": "python",
"filename": "reports/generator.py",
"diff": '''@@ -8,4 +8,30 @@ class ReportGenerator:
+ def generate_quarterly_report(self, quarter, year):
+ """Generate full quarterly report with graphics and tables."""
+ users = self.db.execute("SELECT * FROM users").fetchall()
+
+ report_data = []
+ for user in users:
+ # BUG: N+1 query — individual query per user in loop
+ orders = self.db.execute(
+ f"SELECT * FROM orders WHERE user_id = {user['id']}"
+ ).fetchall()
+
+ total = 0
+ for order in orders:
+ # BUG: Loading all order items just to sum — could use SQL SUM
+ items = self.db.execute(
+ f"SELECT * FROM order_items WHERE order_id = {order['id']}"
+ ).fetchall()
+ total += sum(item['price'] * item['quantity'] for item in items)
+
+ report_data.append({
+ "user": user['name'],
+ "total_spend": total,
+ "order_count": len(orders),
+ })
+
+ # BUG: Sorting entire list in memory instead of ORDER BY in SQL
+ report_data.sort(key=lambda x: x['total_spend'], reverse=True)
+
+ return report_data''',
"lines_changed": 28,
"has_tests": True,
"bug_category": "performance_issue",
"ground_truth_severity": "low",
"bug_lines": [16, 22, 34],
"human_labels": ["low", "medium", "low"],
"human_agreement": 0.67,
"cohen_kappa": 0.68,
},
# ── 16. Java race condition ─────────────────────────────────────────
{
"pr_id": "PR-016",
"title": "Implement connection pool manager",
"description": "Custom connection pool for database connections. Supports max connections and connection reuse.",
"author_experience": "mid",
"language": "java",
"filename": "src/main/java/com/app/ConnectionPool.java",
"diff": '''@@ -15,6 +15,32 @@ public class ConnectionPool {
+ private List<Connection> available = new ArrayList<>();
+ private List<Connection> inUse = new ArrayList<>();
+ private int maxConnections = 10;
+
+ public Connection getConnection() {
+ // BUG: No synchronization — multiple threads can get same connection
+ if (available.isEmpty()) {
+ if (inUse.size() < maxConnections) {
+ Connection conn = createConnection();
+ inUse.add(conn);
+ return conn;
+ }
+ // BUG: Busy wait without backoff — CPU spin
+ while (available.isEmpty()) {
+ // spin
+ }
+ }
+ // BUG: Race condition — another thread could take last connection
+ Connection conn = available.remove(0);
+ inUse.add(conn);
+ return conn;
+ }
+
+ public void releaseConnection(Connection conn) {
+ // BUG: No validation that conn is actually from this pool
+ inUse.remove(conn);
+ available.add(conn);
+ }''',
"lines_changed": 26,
"has_tests": False,
"bug_category": "race_condition",
"ground_truth_severity": "high",
"bug_lines": [21, 28, 33, 39],
"human_labels": ["high", "critical", "high"],
"human_agreement": 0.67,
"cohen_kappa": 0.65,
},
# ── 17. JavaScript missing error handling ───────────────────────────
{
"pr_id": "PR-017",
"title": "Add WebSocket chat handler",
"description": "Real-time chat implementation using WebSocket. Supports direct messages and group channels.",
"author_experience": "junior",
"language": "javascript",
"filename": "handlers/chat.js",
"diff": '''@@ -1,0 +1,30 @@
+const WebSocket = require('ws');
+
+function handleConnection(ws) {
+ ws.on('message', (data) => {
+ // BUG: No try-catch — invalid JSON crashes the server
+ const message = JSON.parse(data);
+
+ // BUG: No validation of message.type
+ if (message.type === 'direct') {
+ // BUG: No check if recipient exists
+ const recipient = connectedUsers[message.to];
+ recipient.send(JSON.stringify({
+ from: message.from,
+ text: message.text,
+ timestamp: new Date().toISOString(),
+ }));
+ } else if (message.type === 'channel') {
+ // BUG: No check if channel exists
+ channels[message.channel].forEach(user => {
+ user.send(JSON.stringify(message));
+ });
+ }
+ });
+
+ ws.on('close', () => {
+ // BUG: No cleanup of user from connectedUsers map
+ console.log('Client disconnected');
+ });
+}
+
+const connectedUsers = {};''',
"lines_changed": 30,
"has_tests": False,
"bug_category": "missing_error_handling",
"ground_truth_severity": "medium",
"bug_lines": [6, 8, 11, 19, 27],
"human_labels": ["medium", "high", "medium"],
"human_agreement": 0.67,
"cohen_kappa": 0.61,
},
# ── 18. Python security vulnerability ───────────────────────────────
{
"pr_id": "PR-018",
"title": "Add admin API key management",
"description": "Admin panel for managing API keys. Allows creation, revocation, and listing of keys.",
"author_experience": "junior",
"language": "python",
"filename": "admin/api_keys.py",
"diff": '''@@ -1,0 +1,32 @@
+import hashlib
+import os
+from datetime import datetime
+
+class APIKeyManager:
+ def __init__(self):
+ self.keys = {}
+
+ def create_key(self, user_id, permissions):
+ # BUG: Using MD5 for key generation — weak hash
+ raw_key = hashlib.md5(
+ f"{user_id}{datetime.now()}".encode()
+ ).hexdigest()
+
+ # BUG: Storing key in plaintext — should store hash only
+ self.keys[raw_key] = {
+ "user_id": user_id,
+ "permissions": permissions,
+ "created_at": datetime.now().isoformat(),
+ "key_plaintext": raw_key, # BUG: Storing plaintext key
+ }
+
+ return raw_key
+
+ def validate_key(self, key):
+ # BUG: No rate limiting on key validation — brute force possible
+ return key in self.keys
+
+ def list_keys(self, user_id):
+ # BUG: Returns full key data including plaintext — information leak
+ return [v for v in self.keys.values() if v["user_id"] == user_id]''',
"lines_changed": 31,
"has_tests": False,
"bug_category": "security_vulnerability",
"ground_truth_severity": "critical",
"bug_lines": [11, 16, 21, 27, 31],
"human_labels": ["critical", "critical", "critical"],
"human_agreement": 1.0,
"cohen_kappa": 1.0,
},
# ── 19. Go logic error ──────────────────────────────────────────────
{
"pr_id": "PR-019",
"title": "Implement retry mechanism with backoff",
"description": "Added exponential backoff retry for external API calls. Configurable max retries and base delay.",
"author_experience": "mid",
"language": "go",
"filename": "pkg/retry/backoff.go",
"diff": '''@@ -8,4 +8,28 @@ type RetryConfig struct {
+func WithRetry(config RetryConfig, fn func() error) error {
+ var lastErr error
+
+ for attempt := 0; attempt < config.MaxRetries; attempt++ {
+ err := fn()
+ if err == nil {
+ return nil
+ }
+ lastErr = err
+
+ // BUG: Delay doesn't actually use exponential backoff
+ // Should be: baseDelay * 2^attempt
+ delay := config.BaseDelay * time.Duration(attempt)
+ // When attempt=0, delay is 0 — no backoff on first retry
+
+ // BUG: No jitter — all clients retry at exact same time (thundering herd)
+ time.Sleep(delay)
+
+ // BUG: No context cancellation check — retries continue even if canceled
+ }
+
+ // BUG: Returns nil instead of lastErr when all retries exhausted
+ // due to loop boundary — attempt reaches MaxRetries and exits
+ return nil // Should return lastErr
+}''',
"lines_changed": 22,
"has_tests": True,
"bug_category": "logic_error",
"ground_truth_severity": "medium",
"bug_lines": [19, 22, 24, 29],
"human_labels": ["medium", "medium", "high"],
"human_agreement": 0.67,
"cohen_kappa": 0.72,
},
# ── 20. Java style only ─────────────────────────────────────────────
{
"pr_id": "PR-020",
"title": "Refactor StringUtils for readability",
"description": "Cleaned up StringUtils class. Renamed methods to follow Java conventions, added Javadoc.",
"author_experience": "senior",
"language": "java",
"filename": "src/main/java/com/app/StringUtils.java",
"diff": '''@@ -1,20 +1,20 @@
-public class StringUtils {
- public static String CAMELCASE(String input) {
- String[] parts = input.split("_");
- StringBuilder sb = new StringBuilder();
- for (String p : parts) {
- sb.append(p.substring(0, 1).toUpperCase());
- sb.append(p.substring(1).toLowerCase());
- }
- return sb.toString();
- }
- public static boolean checkEmpty(String s){
- if(s == null) return true;
- if(s.trim().length() == 0) return true;
- return false;
- }
-}
+/**
+ * Utility class for common string operations.
+ */
+public class StringUtils {
+ /**
+ * Convert snake_case to CamelCase.
+ */
+ public static String toCamelCase(String input) {
+ String[] parts = input.split("_");
+ StringBuilder sb = new StringBuilder();
+ for (String part : parts) {
+ sb.append(part.substring(0, 1).toUpperCase());
+ sb.append(part.substring(1).toLowerCase());
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Check if a string is null or blank.
+ */
+ public static boolean isBlank(String value) {
+ return value == null || value.trim().isEmpty();
+ }
+}''',
"lines_changed": 24,
"has_tests": True,
"bug_category": "style_only",
"ground_truth_severity": "none",
"bug_lines": [],
"human_labels": ["none", "none", "none"],
"human_agreement": 1.0,
"cohen_kappa": 1.0,
},
# ── 21. Rust unsafe memory ─────────────────────────────────────────
{
"pr_id": "PR-021",
"title": "Add FFI bindings for crypto library",
"description": "Created Rust FFI wrappers for the C crypto library. Handles key generation and signing.",
"author_experience": "mid",
"language": "rust",
"filename": "src/ffi/crypto.rs",
"diff": '''@@ -1,0 +1,18 @@
+use std::ffi::{CStr, CString};
+use std::os::raw::c_char;
+
+pub fn sign_message(key: *const c_char, msg: &str) -> String {
+ // BUG: Dereferencing raw pointer without null check
+ let key_str = unsafe { CStr::from_ptr(key) }.to_str().unwrap();
+ let c_msg = CString::new(msg).unwrap();
+ // BUG: Result of FFI call not checked for errors
+ let sig = unsafe { ffi_sign(key_str.as_ptr(), c_msg.as_ptr()) };
+ unsafe { CStr::from_ptr(sig) }.to_string_lossy().into_owned()
+}''',
"lines_changed": 11,
"has_tests": False,
"bug_category": "null_pointer",
"ground_truth_severity": "high",
"bug_lines": [5, 8],
"human_labels": ["high", "critical", "high"],
"human_agreement": 0.67,
"cohen_kappa": 0.58,
},
# ── 22. Go SQL injection ─────────────────────────────────────────────
{
"pr_id": "PR-022",
"title": "Add search endpoint for products",
"description": "New product search API with filtering by category and price range.",
"author_experience": "junior",
"language": "go",
"filename": "handlers/search.go",
"diff": '''@@ -1,0 +1,16 @@
+func SearchProducts(w http.ResponseWriter, r *http.Request) {
+ query := r.URL.Query().Get("q")
+ category := r.URL.Query().Get("category")
+ // BUG: Direct string interpolation in SQL — SQL injection
+ sql := fmt.Sprintf("SELECT * FROM products WHERE name LIKE '%%%s%%' AND category = '%s'", query, category)
+ rows, err := db.Query(sql)
+ if err != nil {
+ http.Error(w, "Search failed", 500)
+ return
+ }
+ defer rows.Close()
+ json.NewEncoder(w).Encode(scanProducts(rows))
+}''',
"lines_changed": 13,
"has_tests": False,
"bug_category": "sql_injection",
"ground_truth_severity": "critical",
"bug_lines": [4, 5],
"human_labels": ["critical", "critical", "critical"],
"human_agreement": 1.0,
"cohen_kappa": 1.0,
},
# ── 23. TypeScript race condition ─────────────────────────────────────
{
"pr_id": "PR-023",
"title": "Add real-time inventory tracker",
"description": "Tracks product inventory in real-time with WebSocket updates.",
"author_experience": "mid",
"language": "typescript",
"filename": "src/inventory/tracker.ts",
"diff": '''@@ -1,0 +1,20 @@
+let inventory: Map<string, number> = new Map();
+
+export async function reserveItem(productId: string, qty: number): Promise<boolean> {
+ const current = inventory.get(productId) || 0;
+ // BUG: TOCTOU race — another request could modify between check and update
+ if (current >= qty) {
+ // Simulate async DB write
+ await db.updateInventory(productId, current - qty);
+ inventory.set(productId, current - qty);
+ return true;
+ }
+ return false;
+}
+
+export async function restockItem(productId: string, qty: number) {
+ const current = inventory.get(productId) || 0;
+ // BUG: Same race condition on restock
+ inventory.set(productId, current + qty);
+ await db.updateInventory(productId, current + qty);
+}''',
"lines_changed": 20,
"has_tests": False,
"bug_category": "race_condition",
"ground_truth_severity": "high",
"bug_lines": [5, 17],
"human_labels": ["high", "high", "critical"],
"human_agreement": 0.67,
"cohen_kappa": 0.65,
},
# ── 24. Python logic error ─────────────────────────────────────────
{
"pr_id": "PR-024",
"title": "Implement discount calculation engine",
"description": "Multi-tier discount system supporting percentage, fixed, and BOGO promotions.",
"author_experience": "junior",
"language": "python",
"filename": "pricing/discounts.py",
"diff": '''@@ -1,0 +1,22 @@
+def calculate_discount(cart_items, promotions):
+ total_discount = 0
+ for item in cart_items:
+ for promo in promotions:
+ if promo["type"] == "percentage":
+ # BUG: Off-by-one — divides by 10 not 100
+ total_discount += item["price"] * promo["value"] / 10
+ elif promo["type"] == "fixed":
+ total_discount += promo["value"]
+ elif promo["type"] == "bogo":
+ # BUG: Applies BOGO to every item, not just qualifying ones
+ total_discount += item["price"]
+ # BUG: No cap — discount can exceed cart total
+ return total_discount''',
"lines_changed": 14,
"has_tests": False,
"bug_category": "logic_error",
"ground_truth_severity": "medium",
"bug_lines": [6, 11, 13],
"human_labels": ["medium", "high", "medium"],
"human_agreement": 0.67,
"cohen_kappa": 0.55,
},
# ── 25. Java performance ─────────────────────────────────────────────
{
"pr_id": "PR-025",
"title": "Generate monthly analytics report",
"description": "Aggregates transaction data for monthly PDF reports with charts.",
"author_experience": "mid",
"language": "java",
"filename": "src/main/java/com/app/ReportGenerator.java",
"diff": '''@@ -20,0 +20,18 @@
+ public Report generateMonthlyReport(int month, int year) {
+ List<Transaction> all = transactionRepo.findAll();
+ // BUG: Loading ALL transactions then filtering in memory — N+1
+ List<Transaction> monthly = new ArrayList<>();
+ for (Transaction t : all) {
+ if (t.getMonth() == month && t.getYear() == year) {
+ monthly.add(t);
+ }
+ }
+ // BUG: Sorting entire list for each category instead of groupBy
+ for (String category : getCategories()) {
+ List<Transaction> catTxns = monthly.stream()
+ .filter(t -> t.getCategory().equals(category))
+ .sorted(Comparator.comparing(Transaction::getAmount))
+ .collect(Collectors.toList());
+ report.addSection(category, aggregate(catTxns));
+ }
+ return report;
+ }''',
"lines_changed": 18,
"has_tests": True,
"bug_category": "performance_issue",
"ground_truth_severity": "low",
"bug_lines": [2, 10],
"human_labels": ["low", "low", "medium"],
"human_agreement": 0.67,
"cohen_kappa": 0.60,
},
# ── 26. Python security ─────────────────────────────────────────────
{
"pr_id": "PR-026",
"title": "Add JWT authentication middleware",
"description": "JWT-based auth middleware for Flask. Validates tokens and extracts user claims.",
"author_experience": "junior",
"language": "python",
"filename": "middleware/auth.py",
"diff": '''@@ -1,0 +1,20 @@
+import jwt
+from functools import wraps
+from flask import request, jsonify
+
+SECRET_KEY = "my-super-secret-key-12345" # BUG: Hardcoded secret
+
+def require_auth(f):
+ @wraps(f)
+ def decorated(*args, **kwargs):
+ token = request.headers.get("Authorization", "").replace("Bearer ", "")
+ try:
+ # BUG: algorithm not restricted — allows "none" algorithm attack
+ payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256", "none"])
+ request.user = payload
+ except jwt.ExpiredSignatureError:
+ return jsonify({"error": "Token expired"}), 401
+ except: # BUG: Bare except hides real errors
+ return jsonify({"error": "Invalid token"}), 401
+ return f(*args, **kwargs)
+ return decorated''',
"lines_changed": 20,
"has_tests": False,
"bug_category": "security_vulnerability",
"ground_truth_severity": "critical",
"bug_lines": [5, 12, 17],
"human_labels": ["critical", "critical", "critical"],
"human_agreement": 1.0,
"cohen_kappa": 1.0,
},
# ── 27. Go missing error handling ─────────────────────────────────
{
"pr_id": "PR-027",
"title": "Add file upload handler",
"description": "Handles multipart file uploads with size validation and storage.",
"author_experience": "junior",
"language": "go",
"filename": "handlers/upload.go",
"diff": '''@@ -1,0 +1,18 @@
+func UploadFile(w http.ResponseWriter, r *http.Request) {
+ // BUG: No max file size limit — DoS via large upload
+ file, header, _ := r.FormFile("upload")
+ defer file.Close()
+
+ // BUG: Ignoring error from FormFile
+ dst, _ := os.Create(filepath.Join("/uploads", header.Filename))
+ // BUG: Path traversal — filename could contain ../
+ defer dst.Close()
+
+ io.Copy(dst, file)
+ // BUG: io.Copy error not checked
+ w.WriteHeader(http.StatusOK)
+ fmt.Fprintf(w, "Uploaded: %s", header.Filename)
+}''',
"lines_changed": 15,
"has_tests": False,
"bug_category": "missing_error_handling",
"ground_truth_severity": "medium",
"bug_lines": [2, 3, 7, 11],
"human_labels": ["high", "medium", "medium"],
"human_agreement": 0.67,
"cohen_kappa": 0.55,
},
# ── 28. Ruby style only ──────────────────────────────────────────────
{
"pr_id": "PR-028",
"title": "Refactor User model validations",
"description": "Cleaned up user validations to use Rails built-in validators.",
"author_experience": "senior",
"language": "ruby",
"filename": "app/models/user.rb",
"diff": '''@@ -1,15 +1,15 @@
class User < ApplicationRecord
- validate :check_email
- validate :check_name
+ validates :email, presence: true, format: { with: URI::MailTo::EMAIL_REGEXP }
+ validates :name, presence: true, length: { minimum: 2, maximum: 100 }
+ validates :role, inclusion: { in: %w[admin editor viewer] }
- def check_email
- errors.add(:email, "invalid") unless email =~ /\A[\w+\-.]+@[a-z\d\-]+\.[a-z]+\z/i
- end
-
- def check_name
- errors.add(:name, "too short") if name.length < 2
- end
+ before_save :normalize_email
+
+ private
+
+ def normalize_email
+ self.email = email.downcase.strip
+ end
end''',
"lines_changed": 15,
"has_tests": True,
"bug_category": "style_only",
"ground_truth_severity": "none",
"bug_lines": [],
"human_labels": ["none", "none", "none"],
"human_agreement": 1.0,
"cohen_kappa": 1.0,
},
# ── 29. Python race condition ─────────────────────────────────────────
{
"pr_id": "PR-029",
"title": "Add distributed task queue processor",
"description": "Background task processor using Redis for job coordination.",
"author_experience": "mid",
"language": "python",
"filename": "workers/task_processor.py",
"diff": '''@@ -1,0 +1,22 @@
+import redis
+import json
+
+class TaskProcessor:
+ def __init__(self):
+ self.redis = redis.Redis()
+ self.processing = {}
+
+ def claim_task(self, queue_name):
+ task_data = self.redis.lpop(queue_name)
+ if task_data:
+ task = json.loads(task_data)
+ # BUG: No atomic claim — two workers can pop same task
+ self.processing[task["id"]] = task
+ return task
+ return None
+
+ def complete_task(self, task_id):
+ # BUG: No check if task is still ours (could have been reclaimed)
+ task = self.processing.pop(task_id, None)
+ self.redis.hset("completed", task_id, json.dumps(task))
+ # BUG: task could be None here''',
"lines_changed": 22,
"has_tests": False,
"bug_category": "race_condition",
"ground_truth_severity": "high",
"bug_lines": [12, 19, 22],
"human_labels": ["high", "high", "high"],
"human_agreement": 1.0,
"cohen_kappa": 1.0,
},
# ── 30. TypeScript null pointer ────────────────────────────────────
{
"pr_id": "PR-030",
"title": "Add user preferences API",
"description": "CRUD endpoints for user preferences with nested settings.",
"author_experience": "junior",
"language": "typescript",
"filename": "src/api/preferences.ts",
"diff": '''@@ -1,0 +1,18 @@
+interface UserPrefs { theme: string; notifications: { email: boolean; push: boolean } }
+
+export function getNotifSetting(prefs: UserPrefs | null, key: string): boolean {
+ // BUG: No null check on prefs
+ return prefs.notifications[key];
+}
+
+export function updatePrefs(userId: string, updates: Partial<UserPrefs>) {
+ const current = db.getPrefs(userId);
+ // BUG: current could be undefined for new users
+ const merged = { ...current, ...updates };
+ // BUG: Deep merge needed for nested notifications object
+ db.savePrefs(userId, merged);
+}''',
"lines_changed": 14,
"has_tests": False,
"bug_category": "null_pointer",
"ground_truth_severity": "high",
"bug_lines": [4, 10, 12],
"human_labels": ["high", "medium", "high"],
"human_agreement": 0.67,
"cohen_kappa": 0.60,
},
# ── 31. Go performance ─────────────────────────────────────────────
{
"pr_id": "PR-031",
"title": "Add log aggregation pipeline",
"description": "Aggregates application logs from multiple sources for analysis.",
"author_experience": "mid",
"language": "go",
"filename": "pipeline/aggregator.go",
"diff": '''@@ -1,0 +1,18 @@
+func AggregateLogs(sources []LogSource, window time.Duration) []AggregatedLog {
+ var results []AggregatedLog
+ for _, source := range sources {
+ // BUG: Loading all logs into memory — no streaming
+ logs := source.FetchAll()
+ for _, log := range logs {
+ // BUG: O(n^2) — searching results linearly for each log
+ for i, r := range results {
+ if r.Key == log.Key {
+ results[i].Count++
+ break
+ }
+ }
+ }
+ }
+ // BUG: No deduplication across sources
+ return results
+}''',
"lines_changed": 18,
"has_tests": True,
"bug_category": "performance_issue",
"ground_truth_severity": "low",
"bug_lines": [4, 7, 16],
"human_labels": ["low", "low", "low"],
"human_agreement": 1.0,
"cohen_kappa": 1.0,
},
# ── 32. Java SQL injection ─────────────────────────────────────────
{
"pr_id": "PR-032",
"title": "Add audit log search",
"description": "Admin audit log search with date range and action type filters.",
"author_experience": "junior",
"language": "java",
"filename": "src/main/java/com/app/AuditSearch.java",
"diff": '''@@ -1,0 +1,14 @@
+public List<AuditLog> searchAuditLogs(String userId, String action, String dateRange) {
+ // BUG: SQL injection via string concatenation
+ String query = "SELECT * FROM audit_logs WHERE user_id = '" + userId + "'";
+ if (action != null) {
+ query += " AND action = '" + action + "'";
+ }
+ if (dateRange != null) {
+ query += " AND created_at > '" + dateRange + "'";
+ }
+ // BUG: Using raw Statement instead of PreparedStatement
+ Statement stmt = connection.createStatement();
+ ResultSet rs = stmt.executeQuery(query);
+ return mapResults(rs);
+}''',
"lines_changed": 14,
"has_tests": False,
"bug_category": "sql_injection",
"ground_truth_severity": "critical",
"bug_lines": [2, 3, 5, 8, 10],
"human_labels": ["critical", "critical", "critical"],
"human_agreement": 1.0,
"cohen_kappa": 1.0,
},
# ── 33. Python logic error ─────────────────────────────────────────
{
"pr_id": "PR-033",
"title": "Implement A/B test bucketing",
"description": "Assigns users to experiment variants based on hash bucketing.",
"author_experience": "mid",
"language": "python",
"filename": "experiments/bucketing.py",
"diff": '''@@ -1,0 +1,18 @@
+import hashlib
+
+def assign_variant(user_id, experiment_name, variants, traffic_pct=100):
+ hash_input = f"{user_id}:{experiment_name}"
+ hash_val = int(hashlib.md5(hash_input.encode()).hexdigest(), 16)
+ # BUG: Modulo 100 but traffic_pct could be 0 → ZeroDivisionError
+ bucket = hash_val % traffic_pct
+ # BUG: Off-by-one — if traffic_pct=50 and bucket=50, user is excluded
+ if bucket >= traffic_pct:
+ return None
+ variant_idx = bucket % len(variants)
+ return variants[variant_idx]''',
"lines_changed": 12,
"has_tests": True,
"bug_category": "logic_error",
"ground_truth_severity": "medium",
"bug_lines": [6, 8],
"human_labels": ["medium", "medium", "high"],
"human_agreement": 0.67,
"cohen_kappa": 0.60,
},
# ── 34. Rust missing error handling ────────────────────────────────
{
"pr_id": "PR-034",
"title": "Add config file parser",
"description": "Parses YAML configuration files with environment variable substitution.",
"author_experience": "junior",
"language": "rust",
"filename": "src/config/parser.rs",
"diff": '''@@ -1,0 +1,16 @@
+use std::fs;
+use std::env;
+
+pub fn load_config(path: &str) -> Config {
+ // BUG: unwrap on file read — panics if file missing
+ let content = fs::read_to_string(path).unwrap();
+ // BUG: unwrap on YAML parse — panics on malformed config
+ let mut config: Config = serde_yaml::from_str(&content).unwrap();
+ // Substitute env vars
+ for (key, val) in &mut config.values {
+ if val.starts_with("${") && val.ends_with("}") {
+ let env_key = &val[2..val.len()-1];
+ // BUG: unwrap on env var — panics if not set
+ *val = env::var(env_key).unwrap();
+ }
+ }
+ config
+}''',
"lines_changed": 16,
"has_tests": False,
"bug_category": "missing_error_handling",
"ground_truth_severity": "medium",
"bug_lines": [5, 7, 13],
"human_labels": ["medium", "medium", "medium"],
"human_agreement": 1.0,
"cohen_kappa": 1.0,
},
# ── 35. JavaScript security ─────────────────────────────────────────
{
"pr_id": "PR-035",
"title": "Add user profile image upload",
"description": "Profile image upload with client-side preview and server storage.",
"author_experience": "junior",
"language": "javascript",
"filename": "routes/profile.js",
"diff": '''@@ -1,0 +1,16 @@
+const express = require('express');
+const fs = require('fs');
+const path = require('path');
+
+app.post('/profile/image', (req, res) => {
+ const file = req.files.avatar;
+ // BUG: No file type validation — could upload .exe, .php
+ // BUG: Path traversal — filename could contain ../
+ const savePath = path.join('/uploads/avatars', file.name);
+ file.mv(savePath, (err) => {
+ if (err) return res.status(500).send(err);
+ // BUG: Storing full server path in DB — info disclosure
+ db.updateUser(req.user.id, { avatar: savePath });
+ res.json({ url: savePath });
+ });
+});''',
"lines_changed": 16,
"has_tests": False,
"bug_category": "security_vulnerability",
"ground_truth_severity": "critical",
"bug_lines": [7, 8, 12],
"human_labels": ["critical", "critical", "high"],
"human_agreement": 0.67,
"cohen_kappa": 0.72,
},
# ── 36-50: Additional templates for diversity ────────────────────────
{
"pr_id": "PR-036", "title": "Add CSV export for reports",
"description": "Export filtered report data to CSV with proper escaping.",
"author_experience": "mid", "language": "python", "filename": "reports/export.py",
"diff": '''@@ -1,0 +1,12 @@
+import csv, io
+def export_csv(data, columns):
+ output = io.StringIO()
+ writer = csv.writer(output)
+ writer.writerow(columns)
+ for row in data:
+ # BUG: No escaping — formula injection via =cmd() in cells
+ writer.writerow([row.get(c, "") for c in columns])
+ return output.getvalue()''',
"lines_changed": 9, "has_tests": True,
"bug_category": "security_vulnerability", "ground_truth_severity": "critical",
"bug_lines": [7], "human_labels": ["critical", "high", "critical"],
"human_agreement": 0.67, "cohen_kappa": 0.58,
},
{
"pr_id": "PR-037", "title": "Fix pagination in user list",
"description": "Added offset-based pagination to the user listing endpoint.",
"author_experience": "junior", "language": "python", "filename": "api/users.py",
"diff": '''@@ -10,6 +10,12 @@
+def list_users(page=1, per_page=20):
+ # BUG: No validation — negative page causes SQL error
+ offset = (page - 1) * per_page
+ # BUG: No upper bound on per_page — DoS via per_page=999999
+ users = db.query(f"SELECT * FROM users LIMIT {per_page} OFFSET {offset}")
+ return users''',
"lines_changed": 6, "has_tests": False,
"bug_category": "logic_error", "ground_truth_severity": "medium",
"bug_lines": [2, 4], "human_labels": ["medium", "medium", "low"],
"human_agreement": 0.67, "cohen_kappa": 0.55,
},
{
"pr_id": "PR-038", "title": "Optimize image thumbnail generation",
"description": "Batch thumbnail generation with parallel processing.",
"author_experience": "senior", "language": "python", "filename": "media/thumbnails.py",
"diff": '''@@ -5,8 +5,14 @@
+from concurrent.futures import ThreadPoolExecutor
+def generate_thumbnails(image_paths, sizes=[128, 256, 512]):
+ results = []
+ # BUG: No limit on thread pool — could exhaust system resources
+ with ThreadPoolExecutor() as pool:
+ for path in image_paths:
+ for size in sizes:
+ # BUG: No error handling — one failure kills entire batch
+ results.append(pool.submit(resize_image, path, size))
+ return [r.result() for r in results]''',
"lines_changed": 10, "has_tests": True,
"bug_category": "performance_issue", "ground_truth_severity": "low",
"bug_lines": [4, 8], "human_labels": ["low", "medium", "low"],
"human_agreement": 0.67, "cohen_kappa": 0.60,
},
{
"pr_id": "PR-039", "title": "Refactor database connection pool",
"description": "Replaced manual connection management with connection pooling.",
"author_experience": "senior", "language": "java", "filename": "src/main/java/com/app/DbPool.java",
"diff": '''@@ -1,18 +1,18 @@
-public class DbManager {
- private Connection conn;
- public Connection getConnection() {
- if (conn == null) conn = DriverManager.getConnection(url);
- return conn;
- }
+public class DbPool {
+ private final HikariDataSource ds;
+ public DbPool(String url, int maxSize) {
+ HikariConfig config = new HikariConfig();
+ config.setJdbcUrl(url);
+ config.setMaximumPoolSize(maxSize);
+ ds = new HikariDataSource(config);
+ }
+ public Connection getConnection() throws SQLException {
+ return ds.getConnection();
+ }
}''',
"lines_changed": 18, "has_tests": True,
"bug_category": "style_only", "ground_truth_severity": "none",
"bug_lines": [], "human_labels": ["none", "none", "none"],
"human_agreement": 1.0, "cohen_kappa": 1.0,
},
{
"pr_id": "PR-040", "title": "Add email notification service",
"description": "Sends transactional emails via SMTP with HTML templates.",
"author_experience": "mid", "language": "python", "filename": "services/email.py",
"diff": '''@@ -1,0 +1,14 @@
+import smtplib
+from email.mime.text import MIMEText
+class EmailService:
+ def __init__(self):
+ # BUG: Hardcoded SMTP credentials
+ self.server = smtplib.SMTP("smtp.gmail.com", 587)
+ self.server.login("app@company.com", "password123")
+ def send(self, to, subject, html_body):
+ msg = MIMEText(html_body, "html")
+ msg["Subject"] = subject
+ # BUG: No input validation on 'to' — could be used for spam
+ self.server.sendmail("app@company.com", to, msg.as_string())''',
"lines_changed": 12, "has_tests": False,
"bug_category": "security_vulnerability", "ground_truth_severity": "critical",
"bug_lines": [5, 7, 11], "human_labels": ["critical", "critical", "critical"],
"human_agreement": 1.0, "cohen_kappa": 1.0,
},
{
"pr_id": "PR-041", "title": "Add caching layer for API responses",
"description": "Redis-based response cache with TTL and invalidation.",
"author_experience": "mid", "language": "python", "filename": "cache/api_cache.py",
"diff": '''@@ -1,0 +1,14 @@
+import redis, json, hashlib
+cache = redis.Redis()
+def cached_response(func):
+ def wrapper(*args, **kwargs):
+ key = hashlib.md5(str(args).encode()).hexdigest()
+ cached = cache.get(key)
+ if cached:
+ return json.loads(cached)
+ result = func(*args, **kwargs)
+ # BUG: No TTL — cached data never expires
+ cache.set(key, json.dumps(result))
+ return result
+ return wrapper''',
"lines_changed": 13, "has_tests": False,
"bug_category": "logic_error", "ground_truth_severity": "medium",
"bug_lines": [10], "human_labels": ["medium", "low", "medium"],
"human_agreement": 0.67, "cohen_kappa": 0.55,
},
{
"pr_id": "PR-042", "title": "Add webhook retry mechanism",
"description": "Retries failed webhook deliveries with exponential backoff.",
"author_experience": "junior", "language": "python", "filename": "webhooks/retry.py",
"diff": '''@@ -1,0 +1,16 @@
+import requests, time
+def deliver_webhook(url, payload, max_retries=3):
+ for attempt in range(max_retries):
+ try:
+ resp = requests.post(url, json=payload, timeout=5)
+ if resp.status_code < 400:
+ return True
+ except requests.Timeout:
+ pass
+ # BUG: Fixed delay instead of exponential backoff
+ time.sleep(1)
+ # BUG: No dead letter queue for failed deliveries
+ return False''',
"lines_changed": 13, "has_tests": False,
"bug_category": "missing_error_handling", "ground_truth_severity": "medium",
"bug_lines": [10, 12], "human_labels": ["medium", "medium", "low"],
"human_agreement": 0.67, "cohen_kappa": 0.55,
},
{
"pr_id": "PR-043", "title": "Add session management",
"description": "Server-side session store with cookie-based session IDs.",
"author_experience": "junior", "language": "javascript", "filename": "middleware/session.js",
"diff": '''@@ -1,0 +1,14 @@
+const sessions = {};
+function createSession(userId) {
+ // BUG: Predictable session ID — sequential counter
+ const sessionId = String(Object.keys(sessions).length + 1);
+ sessions[sessionId] = { userId, created: Date.now() };
+ return sessionId;
+}
+function getSession(req) {
+ const sid = req.cookies.session_id;
+ // BUG: No session expiry check
+ return sessions[sid] || null;
+}''',
"lines_changed": 12, "has_tests": False,
"bug_category": "security_vulnerability", "ground_truth_severity": "critical",
"bug_lines": [3, 10], "human_labels": ["critical", "critical", "high"],
"human_agreement": 0.67, "cohen_kappa": 0.65,
},
{
"pr_id": "PR-044", "title": "Add data migration script",
"description": "Migrates user data from legacy schema to new normalized tables.",
"author_experience": "mid", "language": "python", "filename": "migrations/migrate_users.py",
"diff": '''@@ -1,0 +1,16 @@
+def migrate_users(old_db, new_db):
+ users = old_db.execute("SELECT * FROM legacy_users").fetchall()
+ for user in users:
+ # BUG: No transaction — partial migration on failure
+ new_db.execute("INSERT INTO users (id, name) VALUES (?, ?)",
+ (user["id"], user["name"]))
+ if user.get("address"):
+ new_db.execute("INSERT INTO addresses (user_id, addr) VALUES (?, ?)",
+ (user["id"], user["address"]))
+ # BUG: No commit call
+ print(f"Migrated {len(users)} users")''',
"lines_changed": 11, "has_tests": False,
"bug_category": "missing_error_handling", "ground_truth_severity": "medium",
"bug_lines": [4, 10], "human_labels": ["medium", "high", "medium"],
"human_agreement": 0.67, "cohen_kappa": 0.55,
},
{
"pr_id": "PR-045", "title": "Refactor logging configuration",
"description": "Centralized logging setup with structured JSON output.",
"author_experience": "senior", "language": "python", "filename": "core/logging.py",
"diff": '''@@ -1,12 +1,12 @@
-import logging
-logging.basicConfig(level=logging.DEBUG)
-logger = logging.getLogger(__name__)
+import logging, json, sys
+def setup_logging(level="INFO"):
+ handler = logging.StreamHandler(sys.stdout)
+ handler.setFormatter(JsonFormatter())
+ root = logging.getLogger()
+ root.setLevel(getattr(logging, level))
+ root.addHandler(handler)
+class JsonFormatter(logging.Formatter):
+ def format(self, record):
+ return json.dumps({"level": record.levelname,
+ "msg": record.getMessage(),
+ "time": self.formatTime(record)})''',
"lines_changed": 12, "has_tests": True,
"bug_category": "style_only", "ground_truth_severity": "none",
"bug_lines": [], "human_labels": ["none", "none", "none"],
"human_agreement": 1.0, "cohen_kappa": 1.0,
},
{
"pr_id": "PR-046", "title": "Add GraphQL resolver for orders",
"description": "GraphQL resolvers for order queries with nested product lookups.",
"author_experience": "mid", "language": "typescript", "filename": "src/resolvers/orders.ts",
"diff": '''@@ -1,0 +1,16 @@
+export const orderResolvers = {
+ Query: {
+ orders: async (_, { userId }) => {
+ // BUG: No authorization check — any user can query any user's orders
+ return db.orders.findMany({ where: { userId } });
+ },
+ },
+ Order: {
+ products: async (order) => {
+ // BUG: N+1 query — fetches products one by one per order
+ return Promise.all(order.productIds.map(id => db.products.findUnique({ where: { id } })));
+ },
+ },
+};''',
"lines_changed": 14, "has_tests": False,
"bug_category": "security_vulnerability", "ground_truth_severity": "critical",
"bug_lines": [4, 10], "human_labels": ["critical", "high", "critical"],
"human_agreement": 0.67, "cohen_kappa": 0.65,
},
{
"pr_id": "PR-047", "title": "Add password reset flow",
"description": "Password reset via email with token generation and validation.",
"author_experience": "junior", "language": "python", "filename": "auth/password_reset.py",
"diff": '''@@ -1,0 +1,14 @@
+import random, string, time
+tokens = {}
+def create_reset_token(email):
+ # BUG: Weak random — predictable token
+ token = ''.join(random.choices(string.ascii_letters, k=20))
+ tokens[token] = {"email": email, "created": time.time()}
+ return token
+def reset_password(token, new_password):
+ data = tokens.get(token)
+ if not data:
+ return False
+ # BUG: No token expiry check
+ # BUG: No password strength validation
+ db.update_password(data["email"], new_password)
+ return True''',
"lines_changed": 15, "has_tests": False,
"bug_category": "security_vulnerability", "ground_truth_severity": "critical",
"bug_lines": [4, 12, 13], "human_labels": ["critical", "critical", "critical"],
"human_agreement": 1.0, "cohen_kappa": 1.0,
},
{
"pr_id": "PR-048", "title": "Optimize database indexes",
"description": "Added composite indexes for common query patterns.",
"author_experience": "senior", "language": "python", "filename": "migrations/add_indexes.py",
"diff": '''@@ -1,0 +1,10 @@
+def upgrade():
+ # These are pure schema improvements — no bugs
+ op.create_index("idx_orders_user_date", "orders", ["user_id", "created_at"])
+ op.create_index("idx_products_category", "products", ["category_id", "is_active"])
+ op.create_index("idx_sessions_token", "sessions", ["token"], unique=True)
+
+def downgrade():
+ op.drop_index("idx_orders_user_date")
+ op.drop_index("idx_products_category")
+ op.drop_index("idx_sessions_token")''',
"lines_changed": 10, "has_tests": True,
"bug_category": "style_only", "ground_truth_severity": "none",
"bug_lines": [], "human_labels": ["none", "none", "none"],
"human_agreement": 1.0, "cohen_kappa": 1.0,
},
{
"pr_id": "PR-049", "title": "Add event-driven notification system",
"description": "Pub/sub event system for triggering notifications across services.",
"author_experience": "mid", "language": "python", "filename": "events/dispatcher.py",
"diff": '''@@ -1,0 +1,18 @@
+class EventDispatcher:
+ def __init__(self):
+ self.handlers = {}
+ def subscribe(self, event_type, handler):
+ self.handlers.setdefault(event_type, []).append(handler)
+ def dispatch(self, event_type, data):
+ for handler in self.handlers.get(event_type, []):
+ # BUG: No error isolation — one handler failure stops all
+ handler(data)
+ def dispatch_async(self, event_type, data):
+ import threading
+ for handler in self.handlers.get(event_type, []):
+ # BUG: Unbounded thread creation — no pool
+ t = threading.Thread(target=handler, args=(data,))
+ t.start()
+ # BUG: No join — threads left dangling''',
"lines_changed": 16, "has_tests": False,
"bug_category": "missing_error_handling", "ground_truth_severity": "medium",
"bug_lines": [8, 13, 16], "human_labels": ["medium", "medium", "high"],
"human_agreement": 0.67, "cohen_kappa": 0.60,
},
{
"pr_id": "PR-050", "title": "Add data validation pipeline",
"description": "Schema validation for incoming API payloads with custom rules.",
"author_experience": "junior", "language": "python", "filename": "validation/pipeline.py",
"diff": '''@@ -1,0 +1,18 @@
+def validate_payload(data, schema):
+ errors = []
+ for field, rules in schema.items():
+ value = data.get(field)
+ if rules.get("required") and value is None:
+ errors.append(f"{field} is required")
+ continue
+ if rules.get("type") and not isinstance(value, rules["type"]):
+ # BUG: isinstance check fails when value is None (already checked above)
+ errors.append(f"{field} must be {rules['type'].__name__}")
+ if rules.get("max_length") and len(value) > rules["max_length"]:
+ # BUG: len() on None crashes — need null check first
+ errors.append(f"{field} exceeds max length")
+ return errors''',
"lines_changed": 14, "has_tests": False,
"bug_category": "null_pointer", "ground_truth_severity": "high",
"bug_lines": [9, 12], "human_labels": ["high", "medium", "high"],
"human_agreement": 0.67, "cohen_kappa": 0.60,
},
]
def _build_pr_file(template: Dict) -> PRFile:
"""Convert a template dict to a PRFile model."""
return PRFile(
filename=template["filename"],
language=template["language"],
diff=template["diff"],
lines_changed=template["lines_changed"],
has_tests=template["has_tests"],
)
def _build_observation(
template: Dict,
step_number: int,
episode_budget: int,
review_queue: List[str],
existing_comments: Optional[List[str]] = None,
) -> Observation:
"""Convert a template dict to a full Observation."""
return Observation(
pr_id=template["pr_id"],
title=template["title"],
description=template["description"],
author_experience=template["author_experience"],
files=[_build_pr_file(template)],
existing_comments=existing_comments or [],
review_queue=review_queue,
step_number=step_number,
episode_budget=episode_budget,
)
def get_ground_truth(pr_id: str) -> Dict:
"""
Get ground truth for a PR by its ID.
Returns dict with: bug_category, ground_truth_severity, bug_lines,
human_labels, human_agreement, cohen_kappa.
Used by graders for deterministic scoring.
"""
for t in PR_TEMPLATES:
if t["pr_id"] == pr_id:
return {
"bug_category": t["bug_category"],
"ground_truth_severity": t["ground_truth_severity"],
"bug_lines": t["bug_lines"],
"human_labels": t["human_labels"],
"human_agreement": t["human_agreement"],
"cohen_kappa": t["cohen_kappa"],
}
raise ValueError(f"Unknown PR ID: {pr_id}")
def get_template_by_id(pr_id: str) -> Dict:
"""Get full template dict by PR ID."""
for t in PR_TEMPLATES:
if t["pr_id"] == pr_id:
return t
raise ValueError(f"Unknown PR ID: {pr_id}")
class DataGenerator:
"""
Generates episodes of PRs for each task difficulty.
Uses FIXED_TEST_SUITE: all 20 pre-generated PR templates.
Randomness affects only ordering within episodes, not PR content.
This ensures evaluation is deterministic given the same seed.
"""
def __init__(self, seed: int = 42):
self.seed = seed
self.rng = random.Random(seed)
self.all_templates = list(PR_TEMPLATES)
def generate_easy_episode(self, episode_length: int = 5) -> List[Dict]:
"""
Generate an episode for easy task: sequence of individual PRs.
Returns list of templates (one per step). The agent must label
each PR's severity.
"""
# Shuffle deterministically, pick episode_length PRs
pool = list(self.all_templates)
self.rng.shuffle(pool)
return pool[:episode_length]
def generate_medium_episode(self, num_queues: int = 3, queue_size: int = 5) -> List[List[Dict]]:
"""
Generate an episode for medium task: sequence of PR queues.
Returns list of queues, each queue is a list of templates.
The agent must order each queue by priority.
"""
pool = list(self.all_templates)
self.rng.shuffle(pool)
queues = []
for i in range(num_queues):
start = (i * queue_size) % len(pool)
queue = []
for j in range(queue_size):
idx = (start + j) % len(pool)
queue.append(pool[idx])
# Shuffle within queue so agent can't rely on ordering
self.rng.shuffle(queue)
queues.append(queue)
return queues
def generate_hard_episode(self, num_prs: int = 3) -> List[Dict]:
"""
Generate an episode for hard task: PRs requiring detailed review.
Returns list of templates. For each PR, the agent may make
multiple add_comment actions before approve/request_changes.
Prioritize PRs with bugs for more interesting review scenarios.
"""
# Select PRs with a mix of severities — ensure at least one critical
critical = [t for t in self.all_templates if t["ground_truth_severity"] == "critical"]
non_critical = [t for t in self.all_templates if t["ground_truth_severity"] != "critical"]
self.rng.shuffle(critical)
self.rng.shuffle(non_critical)
selected = []
if critical:
selected.append(critical[0])
remaining_needed = num_prs - len(selected)
selected.extend(non_critical[:remaining_needed])
self.rng.shuffle(selected)
return selected[:num_prs]
def compute_priority_order(self, queue: List[Dict]) -> List[str]:
"""
Compute ground truth priority ordering for a queue of PRs.
Priority rules (in order):
1. Security PRs always first
2. By severity: critical > high > medium > low > none
3. Within same severity: junior authors first (they need review most urgently)
"""
severity_rank = {"critical": 0, "high": 1, "medium": 2, "low": 3, "none": 4}
experience_rank = {"junior": 0, "mid": 1, "senior": 2}
def sort_key(template):
is_security = 1 if template["bug_category"] in ("security_vulnerability", "sql_injection") else 0
sev = severity_rank.get(template["ground_truth_severity"], 4)
exp = experience_rank.get(template["author_experience"], 2)
# Lower = higher priority. Security first, then severity, then experience
return (1 - is_security, sev, exp)
sorted_queue = sorted(queue, key=sort_key)
return [t["pr_id"] for t in sorted_queue]