@kevinrabun/judges 3.23.6 → 3.23.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +37 -2
- package/dist/cli.js.map +1 -1
- package/dist/commands/benchmark.d.ts.map +1 -1
- package/dist/commands/benchmark.js +556 -0
- package/dist/commands/benchmark.js.map +1 -1
- package/dist/commands/diff.d.ts.map +1 -1
- package/dist/commands/diff.js +17 -1
- package/dist/commands/diff.js.map +1 -1
- package/dist/commands/review.d.ts +37 -0
- package/dist/commands/review.d.ts.map +1 -0
- package/dist/commands/review.js +539 -0
- package/dist/commands/review.js.map +1 -0
- package/dist/commands/tune.d.ts +25 -0
- package/dist/commands/tune.d.ts.map +1 -0
- package/dist/commands/tune.js +408 -0
- package/dist/commands/tune.js.map +1 -0
- package/dist/finding-lifecycle.d.ts +93 -0
- package/dist/finding-lifecycle.d.ts.map +1 -0
- package/dist/finding-lifecycle.js +214 -0
- package/dist/finding-lifecycle.js.map +1 -0
- package/dist/patches/index.d.ts.map +1 -1
- package/dist/patches/index.js +127 -0
- package/dist/patches/index.js.map +1 -1
- package/dist/presets.d.ts.map +1 -1
- package/dist/presets.js +103 -0
- package/dist/presets.js.map +1 -1
- package/package.json +1 -1
- package/server.json +2 -2
|
@@ -1586,6 +1586,562 @@ app.post("/api/v1/login", async (req, res) => {
|
|
|
1586
1586
|
category: "clean",
|
|
1587
1587
|
difficulty: "hard",
|
|
1588
1588
|
},
|
|
1589
|
+
// ── FP Benchmark Corpus — Multi-Language Clean Code ──────────────────────
|
|
1590
|
+
// These cases are well-written code that should NOT trigger findings.
|
|
1591
|
+
// They measure the false positive rate across languages.
|
|
1592
|
+
// ────────────────────────────────────────────────────────────────────────────
|
|
1593
|
+
// ── Clean Python: FastAPI with Pydantic validation ──
|
|
1594
|
+
{
|
|
1595
|
+
id: "clean-python-fastapi",
|
|
1596
|
+
description: "Well-structured FastAPI endpoint with Pydantic validation, auth, and error handling",
|
|
1597
|
+
language: "python",
|
|
1598
|
+
code: `from fastapi import FastAPI, Depends, HTTPException, status
|
|
1599
|
+
from fastapi.security import OAuth2PasswordBearer
|
|
1600
|
+
from pydantic import BaseModel, EmailStr, Field
|
|
1601
|
+
from slowapi import Limiter
|
|
1602
|
+
from slowapi.util import get_remote_address
|
|
1603
|
+
import logging
|
|
1604
|
+
import secrets
|
|
1605
|
+
|
|
1606
|
+
app = FastAPI()
|
|
1607
|
+
limiter = Limiter(key_func=get_remote_address)
|
|
1608
|
+
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
|
|
1609
|
+
logger = logging.getLogger(__name__)
|
|
1610
|
+
|
|
1611
|
+
class UserCreate(BaseModel):
|
|
1612
|
+
email: EmailStr
|
|
1613
|
+
password: str = Field(min_length=12, max_length=128)
|
|
1614
|
+
name: str = Field(min_length=1, max_length=200)
|
|
1615
|
+
|
|
1616
|
+
@app.post("/api/v1/users", status_code=status.HTTP_201_CREATED)
|
|
1617
|
+
@limiter.limit("10/minute")
|
|
1618
|
+
async def create_user(user: UserCreate, token: str = Depends(oauth2_scheme)):
|
|
1619
|
+
current_user = await verify_token(token)
|
|
1620
|
+
if not current_user.is_admin:
|
|
1621
|
+
raise HTTPException(status_code=403, detail="Admin access required")
|
|
1622
|
+
hashed = bcrypt.hashpw(user.password.encode(), bcrypt.gensalt())
|
|
1623
|
+
new_user = await db.users.create(email=user.email, password_hash=hashed, name=user.name)
|
|
1624
|
+
logger.info("User created: %s by admin %s", new_user.id, current_user.id)
|
|
1625
|
+
return {"id": new_user.id, "email": new_user.email}`,
|
|
1626
|
+
expectedRuleIds: [],
|
|
1627
|
+
unexpectedRuleIds: ["CYBER-001", "CYBER-002", "AUTH-001", "SEC-001", "RATE-001", "DATA-001"],
|
|
1628
|
+
category: "clean",
|
|
1629
|
+
difficulty: "hard",
|
|
1630
|
+
},
|
|
1631
|
+
// ── Clean Go: HTTP handler with proper error handling ──
|
|
1632
|
+
{
|
|
1633
|
+
id: "clean-go-handler",
|
|
1634
|
+
description: "Well-structured Go HTTP handler with parameterized queries, auth, and logging",
|
|
1635
|
+
language: "go",
|
|
1636
|
+
code: `package handlers
|
|
1637
|
+
|
|
1638
|
+
import (
|
|
1639
|
+
"encoding/json"
|
|
1640
|
+
"log/slog"
|
|
1641
|
+
"net/http"
|
|
1642
|
+
"github.com/go-chi/chi/v5"
|
|
1643
|
+
"github.com/jmoiron/sqlx"
|
|
1644
|
+
)
|
|
1645
|
+
|
|
1646
|
+
type UserHandler struct {
|
|
1647
|
+
db *sqlx.DB
|
|
1648
|
+
logger *slog.Logger
|
|
1649
|
+
}
|
|
1650
|
+
|
|
1651
|
+
type CreateUserRequest struct {
|
|
1652
|
+
Email string \`json:"email" validate:"required,email"\`
|
|
1653
|
+
Name string \`json:"name" validate:"required,min=1,max=200"\`
|
|
1654
|
+
}
|
|
1655
|
+
|
|
1656
|
+
func (h *UserHandler) GetUser(w http.ResponseWriter, r *http.Request) {
|
|
1657
|
+
userID := chi.URLParam(r, "id")
|
|
1658
|
+
if userID == "" {
|
|
1659
|
+
http.Error(w, "missing user id", http.StatusBadRequest)
|
|
1660
|
+
return
|
|
1661
|
+
}
|
|
1662
|
+
var user User
|
|
1663
|
+
err := h.db.QueryRowContext(r.Context(), "SELECT id, email, name FROM users WHERE id = $1", userID).Scan(&user.ID, &user.Email, &user.Name)
|
|
1664
|
+
if err != nil {
|
|
1665
|
+
h.logger.Error("failed to fetch user", "error", err, "user_id", userID)
|
|
1666
|
+
http.Error(w, "user not found", http.StatusNotFound)
|
|
1667
|
+
return
|
|
1668
|
+
}
|
|
1669
|
+
w.Header().Set("Content-Type", "application/json")
|
|
1670
|
+
json.NewEncoder(w).Encode(user)
|
|
1671
|
+
}`,
|
|
1672
|
+
expectedRuleIds: [],
|
|
1673
|
+
unexpectedRuleIds: ["CYBER-001", "CYBER-002", "SEC-001", "ERR-001"],
|
|
1674
|
+
category: "clean",
|
|
1675
|
+
difficulty: "hard",
|
|
1676
|
+
},
|
|
1677
|
+
// ── Clean Rust: Safe web handler ──
|
|
1678
|
+
{
|
|
1679
|
+
id: "clean-rust-handler",
|
|
1680
|
+
description: "Well-structured Rust Actix-web handler with validation and error types",
|
|
1681
|
+
language: "rust",
|
|
1682
|
+
code: `use actix_web::{web, HttpResponse, Result};
|
|
1683
|
+
use serde::{Deserialize, Serialize};
|
|
1684
|
+
use sqlx::PgPool;
|
|
1685
|
+
use validator::Validate;
|
|
1686
|
+
use tracing::{info, error};
|
|
1687
|
+
|
|
1688
|
+
#[derive(Deserialize, Validate)]
|
|
1689
|
+
pub struct CreateItemRequest {
|
|
1690
|
+
#[validate(length(min = 1, max = 200))]
|
|
1691
|
+
pub name: String,
|
|
1692
|
+
#[validate(range(min = 0.01, max = 999999.99))]
|
|
1693
|
+
pub price: f64,
|
|
1694
|
+
}
|
|
1695
|
+
|
|
1696
|
+
#[derive(Serialize)]
|
|
1697
|
+
pub struct ItemResponse {
|
|
1698
|
+
pub id: i64,
|
|
1699
|
+
pub name: String,
|
|
1700
|
+
pub price: f64,
|
|
1701
|
+
}
|
|
1702
|
+
|
|
1703
|
+
pub async fn create_item(
|
|
1704
|
+
pool: web::Data<PgPool>,
|
|
1705
|
+
body: web::Json<CreateItemRequest>,
|
|
1706
|
+
) -> Result<HttpResponse> {
|
|
1707
|
+
body.validate().map_err(|e| {
|
|
1708
|
+
actix_web::error::ErrorBadRequest(format!("Validation error: {}", e))
|
|
1709
|
+
})?;
|
|
1710
|
+
let row = sqlx::query_as!(
|
|
1711
|
+
ItemResponse,
|
|
1712
|
+
"INSERT INTO items (name, price) VALUES ($1, $2) RETURNING id, name, price",
|
|
1713
|
+
body.name,
|
|
1714
|
+
body.price,
|
|
1715
|
+
)
|
|
1716
|
+
.fetch_one(pool.get_ref())
|
|
1717
|
+
.await
|
|
1718
|
+
.map_err(|e| {
|
|
1719
|
+
error!("DB insert failed: {}", e);
|
|
1720
|
+
actix_web::error::ErrorInternalServerError("Failed to create item")
|
|
1721
|
+
})?;
|
|
1722
|
+
info!(item_id = row.id, "Item created");
|
|
1723
|
+
Ok(HttpResponse::Created().json(row))
|
|
1724
|
+
}`,
|
|
1725
|
+
expectedRuleIds: [],
|
|
1726
|
+
unexpectedRuleIds: ["CYBER-001", "CYBER-002", "SEC-001", "ERR-001"],
|
|
1727
|
+
category: "clean",
|
|
1728
|
+
difficulty: "hard",
|
|
1729
|
+
},
|
|
1730
|
+
// ── Clean Java: Spring Boot controller with validation ──
|
|
1731
|
+
{
|
|
1732
|
+
id: "clean-java-spring",
|
|
1733
|
+
description: "Well-structured Spring Boot REST controller with validation and auth",
|
|
1734
|
+
language: "java",
|
|
1735
|
+
code: `import org.springframework.web.bind.annotation.*;
|
|
1736
|
+
import org.springframework.http.ResponseEntity;
|
|
1737
|
+
import org.springframework.security.access.prepost.PreAuthorize;
|
|
1738
|
+
import javax.validation.Valid;
|
|
1739
|
+
import org.slf4j.Logger;
|
|
1740
|
+
import org.slf4j.LoggerFactory;
|
|
1741
|
+
|
|
1742
|
+
@RestController
|
|
1743
|
+
@RequestMapping("/api/v1/products")
|
|
1744
|
+
public class ProductController {
|
|
1745
|
+
private static final Logger log = LoggerFactory.getLogger(ProductController.class);
|
|
1746
|
+
private final ProductService productService;
|
|
1747
|
+
|
|
1748
|
+
public ProductController(ProductService productService) {
|
|
1749
|
+
this.productService = productService;
|
|
1750
|
+
}
|
|
1751
|
+
|
|
1752
|
+
@GetMapping("/{id}")
|
|
1753
|
+
public ResponseEntity<ProductDTO> getProduct(@PathVariable Long id) {
|
|
1754
|
+
return productService.findById(id)
|
|
1755
|
+
.map(ResponseEntity::ok)
|
|
1756
|
+
.orElse(ResponseEntity.notFound().build());
|
|
1757
|
+
}
|
|
1758
|
+
|
|
1759
|
+
@PostMapping
|
|
1760
|
+
@PreAuthorize("hasRole('ADMIN')")
|
|
1761
|
+
public ResponseEntity<ProductDTO> createProduct(@Valid @RequestBody CreateProductRequest request) {
|
|
1762
|
+
log.info("Creating product: {}", request.getName());
|
|
1763
|
+
ProductDTO created = productService.create(request);
|
|
1764
|
+
return ResponseEntity.status(201).body(created);
|
|
1765
|
+
}
|
|
1766
|
+
|
|
1767
|
+
@DeleteMapping("/{id}")
|
|
1768
|
+
@PreAuthorize("hasRole('ADMIN')")
|
|
1769
|
+
public ResponseEntity<Void> deleteProduct(@PathVariable Long id) {
|
|
1770
|
+
log.info("Deleting product: {}", id);
|
|
1771
|
+
productService.delete(id);
|
|
1772
|
+
return ResponseEntity.noContent().build();
|
|
1773
|
+
}
|
|
1774
|
+
}`,
|
|
1775
|
+
expectedRuleIds: [],
|
|
1776
|
+
unexpectedRuleIds: ["CYBER-001", "CYBER-002", "AUTH-001", "SEC-001"],
|
|
1777
|
+
category: "clean",
|
|
1778
|
+
difficulty: "hard",
|
|
1779
|
+
},
|
|
1780
|
+
// ── Clean C#: ASP.NET Core controller ──
|
|
1781
|
+
{
|
|
1782
|
+
id: "clean-csharp-aspnet",
|
|
1783
|
+
description: "Well-structured ASP.NET Core controller with EF Core parameterized queries",
|
|
1784
|
+
language: "csharp",
|
|
1785
|
+
code: `using Microsoft.AspNetCore.Mvc;
|
|
1786
|
+
using Microsoft.AspNetCore.Authorization;
|
|
1787
|
+
using Microsoft.EntityFrameworkCore;
|
|
1788
|
+
using Microsoft.Extensions.Logging;
|
|
1789
|
+
using FluentValidation;
|
|
1790
|
+
|
|
1791
|
+
[ApiController]
|
|
1792
|
+
[Route("api/v1/[controller]")]
|
|
1793
|
+
[Authorize]
|
|
1794
|
+
public class OrdersController : ControllerBase
|
|
1795
|
+
{
|
|
1796
|
+
private readonly AppDbContext _db;
|
|
1797
|
+
private readonly ILogger<OrdersController> _logger;
|
|
1798
|
+
private readonly IValidator<CreateOrderRequest> _validator;
|
|
1799
|
+
|
|
1800
|
+
public OrdersController(AppDbContext db, ILogger<OrdersController> logger, IValidator<CreateOrderRequest> validator)
|
|
1801
|
+
{
|
|
1802
|
+
_db = db;
|
|
1803
|
+
_logger = logger;
|
|
1804
|
+
_validator = validator;
|
|
1805
|
+
}
|
|
1806
|
+
|
|
1807
|
+
[HttpGet("{id}")]
|
|
1808
|
+
public async Task<IActionResult> GetOrder(int id)
|
|
1809
|
+
{
|
|
1810
|
+
var order = await _db.Orders
|
|
1811
|
+
.Where(o => o.Id == id && o.UserId == User.GetUserId())
|
|
1812
|
+
.FirstOrDefaultAsync();
|
|
1813
|
+
if (order == null) return NotFound();
|
|
1814
|
+
return Ok(order);
|
|
1815
|
+
}
|
|
1816
|
+
|
|
1817
|
+
[HttpPost]
|
|
1818
|
+
public async Task<IActionResult> CreateOrder([FromBody] CreateOrderRequest request)
|
|
1819
|
+
{
|
|
1820
|
+
var validation = await _validator.ValidateAsync(request);
|
|
1821
|
+
if (!validation.IsValid) return BadRequest(validation.Errors);
|
|
1822
|
+
var order = new Order { UserId = User.GetUserId(), Total = request.Total, Items = request.Items };
|
|
1823
|
+
_db.Orders.Add(order);
|
|
1824
|
+
await _db.SaveChangesAsync();
|
|
1825
|
+
_logger.LogInformation("Order {OrderId} created by user {UserId}", order.Id, User.GetUserId());
|
|
1826
|
+
return CreatedAtAction(nameof(GetOrder), new { id = order.Id }, order);
|
|
1827
|
+
}
|
|
1828
|
+
}`,
|
|
1829
|
+
expectedRuleIds: [],
|
|
1830
|
+
unexpectedRuleIds: ["CYBER-001", "CYBER-002", "AUTH-001", "SEC-001", "DATA-001"],
|
|
1831
|
+
category: "clean",
|
|
1832
|
+
difficulty: "hard",
|
|
1833
|
+
},
|
|
1834
|
+
// ── Clean TypeScript: Pure utility library (no server) ──
|
|
1835
|
+
{
|
|
1836
|
+
id: "clean-ts-utility-lib",
|
|
1837
|
+
description: "Pure TypeScript utility library — no server code, should have zero security findings",
|
|
1838
|
+
language: "typescript",
|
|
1839
|
+
code: `/**
|
|
1840
|
+
* A type-safe result type for error handling without exceptions.
|
|
1841
|
+
*/
|
|
1842
|
+
export type Result<T, E = Error> = { ok: true; value: T } | { ok: false; error: E };
|
|
1843
|
+
|
|
1844
|
+
export function ok<T>(value: T): Result<T, never> {
|
|
1845
|
+
return { ok: true, value };
|
|
1846
|
+
}
|
|
1847
|
+
|
|
1848
|
+
export function err<E>(error: E): Result<never, E> {
|
|
1849
|
+
return { ok: false, error };
|
|
1850
|
+
}
|
|
1851
|
+
|
|
1852
|
+
export function map<T, U, E>(result: Result<T, E>, fn: (value: T) => U): Result<U, E> {
|
|
1853
|
+
return result.ok ? ok(fn(result.value)) : result;
|
|
1854
|
+
}
|
|
1855
|
+
|
|
1856
|
+
export function flatMap<T, U, E>(result: Result<T, E>, fn: (value: T) => Result<U, E>): Result<U, E> {
|
|
1857
|
+
return result.ok ? fn(result.value) : result;
|
|
1858
|
+
}
|
|
1859
|
+
|
|
1860
|
+
export function unwrapOr<T, E>(result: Result<T, E>, defaultValue: T): T {
|
|
1861
|
+
return result.ok ? result.value : defaultValue;
|
|
1862
|
+
}
|
|
1863
|
+
|
|
1864
|
+
/** Retry an async operation with exponential backoff. */
|
|
1865
|
+
export async function retry<T>(fn: () => Promise<T>, maxRetries = 3, baseDelay = 100): Promise<T> {
|
|
1866
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
1867
|
+
try {
|
|
1868
|
+
return await fn();
|
|
1869
|
+
} catch (error) {
|
|
1870
|
+
if (attempt === maxRetries) throw error;
|
|
1871
|
+
await new Promise((r) => setTimeout(r, baseDelay * Math.pow(2, attempt)));
|
|
1872
|
+
}
|
|
1873
|
+
}
|
|
1874
|
+
throw new Error("Unreachable");
|
|
1875
|
+
}`,
|
|
1876
|
+
expectedRuleIds: [],
|
|
1877
|
+
unexpectedRuleIds: ["CYBER-001", "CYBER-002", "SEC-001", "AUTH-001", "RATE-001", "ERR-001"],
|
|
1878
|
+
category: "clean",
|
|
1879
|
+
difficulty: "hard",
|
|
1880
|
+
},
|
|
1881
|
+
// ── Clean Terraform: Hardened AWS infrastructure ──
|
|
1882
|
+
{
|
|
1883
|
+
id: "clean-terraform-hardened",
|
|
1884
|
+
description: "Terraform with encryption, private access, and proper security groups",
|
|
1885
|
+
language: "hcl",
|
|
1886
|
+
code: `resource "aws_s3_bucket" "data" {
|
|
1887
|
+
bucket = "myapp-data-prod"
|
|
1888
|
+
}
|
|
1889
|
+
|
|
1890
|
+
resource "aws_s3_bucket_server_side_encryption_configuration" "data" {
|
|
1891
|
+
bucket = aws_s3_bucket.data.id
|
|
1892
|
+
rule {
|
|
1893
|
+
apply_server_side_encryption_by_default {
|
|
1894
|
+
sse_algorithm = "aws:kms"
|
|
1895
|
+
}
|
|
1896
|
+
}
|
|
1897
|
+
}
|
|
1898
|
+
|
|
1899
|
+
resource "aws_s3_bucket_public_access_block" "data" {
|
|
1900
|
+
bucket = aws_s3_bucket.data.id
|
|
1901
|
+
block_public_acls = true
|
|
1902
|
+
block_public_policy = true
|
|
1903
|
+
ignore_public_acls = true
|
|
1904
|
+
restrict_public_buckets = true
|
|
1905
|
+
}
|
|
1906
|
+
|
|
1907
|
+
resource "aws_db_instance" "main" {
|
|
1908
|
+
engine = "postgres"
|
|
1909
|
+
instance_class = "db.r6g.large"
|
|
1910
|
+
publicly_accessible = false
|
|
1911
|
+
storage_encrypted = true
|
|
1912
|
+
multi_az = true
|
|
1913
|
+
deletion_protection = true
|
|
1914
|
+
backup_retention_period = 7
|
|
1915
|
+
}
|
|
1916
|
+
|
|
1917
|
+
resource "aws_security_group" "web" {
|
|
1918
|
+
name = "web-sg"
|
|
1919
|
+
vpc_id = var.vpc_id
|
|
1920
|
+
|
|
1921
|
+
ingress {
|
|
1922
|
+
from_port = 443
|
|
1923
|
+
to_port = 443
|
|
1924
|
+
protocol = "tcp"
|
|
1925
|
+
cidr_blocks = [var.allowed_cidr]
|
|
1926
|
+
}
|
|
1927
|
+
|
|
1928
|
+
egress {
|
|
1929
|
+
from_port = 0
|
|
1930
|
+
to_port = 0
|
|
1931
|
+
protocol = "-1"
|
|
1932
|
+
cidr_blocks = ["0.0.0.0/0"]
|
|
1933
|
+
}
|
|
1934
|
+
}`,
|
|
1935
|
+
expectedRuleIds: [],
|
|
1936
|
+
unexpectedRuleIds: ["IAC-001", "SEC-001", "CYBER-001", "DATA-001"],
|
|
1937
|
+
category: "clean",
|
|
1938
|
+
difficulty: "hard",
|
|
1939
|
+
},
|
|
1940
|
+
// ── Clean Python: Data processing script (not a server) ──
|
|
1941
|
+
{
|
|
1942
|
+
id: "clean-python-data-script",
|
|
1943
|
+
description: "Python data processing script — no web endpoints, should not flag server concerns",
|
|
1944
|
+
language: "python",
|
|
1945
|
+
code: `"""Data pipeline for aggregating daily sales metrics."""
|
|
1946
|
+
import csv
|
|
1947
|
+
import logging
|
|
1948
|
+
from pathlib import Path
|
|
1949
|
+
from dataclasses import dataclass
|
|
1950
|
+
from typing import Iterator
|
|
1951
|
+
|
|
1952
|
+
logger = logging.getLogger(__name__)
|
|
1953
|
+
|
|
1954
|
+
@dataclass
|
|
1955
|
+
class SalesRecord:
|
|
1956
|
+
date: str
|
|
1957
|
+
product_id: str
|
|
1958
|
+
quantity: int
|
|
1959
|
+
unit_price: float
|
|
1960
|
+
|
|
1961
|
+
@property
|
|
1962
|
+
def total(self) -> float:
|
|
1963
|
+
return self.quantity * self.unit_price
|
|
1964
|
+
|
|
1965
|
+
def read_records(path: Path) -> Iterator[SalesRecord]:
|
|
1966
|
+
with path.open("r", encoding="utf-8") as f:
|
|
1967
|
+
reader = csv.DictReader(f)
|
|
1968
|
+
for row in reader:
|
|
1969
|
+
try:
|
|
1970
|
+
yield SalesRecord(
|
|
1971
|
+
date=row["date"],
|
|
1972
|
+
product_id=row["product_id"],
|
|
1973
|
+
quantity=int(row["quantity"]),
|
|
1974
|
+
unit_price=float(row["unit_price"]),
|
|
1975
|
+
)
|
|
1976
|
+
except (KeyError, ValueError) as e:
|
|
1977
|
+
logger.warning("Skipping invalid row: %s (%s)", row, e)
|
|
1978
|
+
|
|
1979
|
+
def aggregate_by_date(records: Iterator[SalesRecord]) -> dict[str, float]:
|
|
1980
|
+
totals: dict[str, float] = {}
|
|
1981
|
+
for record in records:
|
|
1982
|
+
totals[record.date] = totals.get(record.date, 0.0) + record.total
|
|
1983
|
+
return totals
|
|
1984
|
+
|
|
1985
|
+
if __name__ == "__main__":
|
|
1986
|
+
logging.basicConfig(level=logging.INFO)
|
|
1987
|
+
path = Path("data/sales.csv")
|
|
1988
|
+
if not path.exists():
|
|
1989
|
+
logger.error("File not found: %s", path)
|
|
1990
|
+
raise SystemExit(1)
|
|
1991
|
+
results = aggregate_by_date(read_records(path))
|
|
1992
|
+
for date, total in sorted(results.items()):
|
|
1993
|
+
logger.info("Date: %s, Total: $%.2f", date, total)`,
|
|
1994
|
+
expectedRuleIds: [],
|
|
1995
|
+
unexpectedRuleIds: ["CYBER-001", "CYBER-002", "SEC-001", "AUTH-001", "RATE-001"],
|
|
1996
|
+
category: "clean",
|
|
1997
|
+
difficulty: "hard",
|
|
1998
|
+
},
|
|
1999
|
+
// ── Clean Go: CLI tool (not a server) ──
|
|
2000
|
+
{
|
|
2001
|
+
id: "clean-go-cli-tool",
|
|
2002
|
+
description: "Go CLI tool — should not flag server-side security concerns",
|
|
2003
|
+
language: "go",
|
|
2004
|
+
code: `package main
|
|
2005
|
+
|
|
2006
|
+
import (
|
|
2007
|
+
"encoding/json"
|
|
2008
|
+
"flag"
|
|
2009
|
+
"fmt"
|
|
2010
|
+
"log"
|
|
2011
|
+
"os"
|
|
2012
|
+
"path/filepath"
|
|
2013
|
+
"sort"
|
|
2014
|
+
)
|
|
2015
|
+
|
|
2016
|
+
type Config struct {
|
|
2017
|
+
InputDir string \`json:"input_dir"\`
|
|
2018
|
+
OutputDir string \`json:"output_dir"\`
|
|
2019
|
+
Verbose bool \`json:"verbose"\`
|
|
2020
|
+
}
|
|
2021
|
+
|
|
2022
|
+
func loadConfig(path string) (*Config, error) {
|
|
2023
|
+
data, err := os.ReadFile(path)
|
|
2024
|
+
if err != nil {
|
|
2025
|
+
return nil, fmt.Errorf("read config: %w", err)
|
|
2026
|
+
}
|
|
2027
|
+
var cfg Config
|
|
2028
|
+
if err := json.Unmarshal(data, &cfg); err != nil {
|
|
2029
|
+
return nil, fmt.Errorf("parse config: %w", err)
|
|
2030
|
+
}
|
|
2031
|
+
return &cfg, nil
|
|
2032
|
+
}
|
|
2033
|
+
|
|
2034
|
+
func processFiles(cfg *Config) error {
|
|
2035
|
+
entries, err := os.ReadDir(cfg.InputDir)
|
|
2036
|
+
if err != nil {
|
|
2037
|
+
return fmt.Errorf("read dir: %w", err)
|
|
2038
|
+
}
|
|
2039
|
+
sort.Slice(entries, func(i, j int) bool { return entries[i].Name() < entries[j].Name() })
|
|
2040
|
+
for _, entry := range entries {
|
|
2041
|
+
if entry.IsDir() || filepath.Ext(entry.Name()) != ".json" {
|
|
2042
|
+
continue
|
|
2043
|
+
}
|
|
2044
|
+
src := filepath.Join(cfg.InputDir, entry.Name())
|
|
2045
|
+
dst := filepath.Join(cfg.OutputDir, entry.Name())
|
|
2046
|
+
if cfg.Verbose {
|
|
2047
|
+
log.Printf("Processing: %s -> %s", src, dst)
|
|
2048
|
+
}
|
|
2049
|
+
data, err := os.ReadFile(src)
|
|
2050
|
+
if err != nil {
|
|
2051
|
+
log.Printf("Warning: skip %s: %v", src, err)
|
|
2052
|
+
continue
|
|
2053
|
+
}
|
|
2054
|
+
if err := os.WriteFile(dst, data, 0644); err != nil {
|
|
2055
|
+
return fmt.Errorf("write %s: %w", dst, err)
|
|
2056
|
+
}
|
|
2057
|
+
}
|
|
2058
|
+
return nil
|
|
2059
|
+
}
|
|
2060
|
+
|
|
2061
|
+
func main() {
|
|
2062
|
+
configPath := flag.String("config", "config.json", "path to config file")
|
|
2063
|
+
flag.Parse()
|
|
2064
|
+
cfg, err := loadConfig(*configPath)
|
|
2065
|
+
if err != nil {
|
|
2066
|
+
log.Fatal(err)
|
|
2067
|
+
}
|
|
2068
|
+
if err := processFiles(cfg); err != nil {
|
|
2069
|
+
log.Fatal(err)
|
|
2070
|
+
}
|
|
2071
|
+
fmt.Println("Done.")
|
|
2072
|
+
}`,
|
|
2073
|
+
expectedRuleIds: [],
|
|
2074
|
+
unexpectedRuleIds: ["CYBER-001", "CYBER-002", "SEC-001", "AUTH-001", "RATE-001", "ERR-001"],
|
|
2075
|
+
category: "clean",
|
|
2076
|
+
difficulty: "hard",
|
|
2077
|
+
},
|
|
2078
|
+
// ── Clean TypeScript: React component (not a server) ──
|
|
2079
|
+
{
|
|
2080
|
+
id: "clean-ts-react-component",
|
|
2081
|
+
description: "React component with hooks — should not trigger server-side security findings",
|
|
2082
|
+
language: "typescript",
|
|
2083
|
+
code: `import React, { useState, useCallback, useMemo } from "react";
|
|
2084
|
+
|
|
2085
|
+
interface User {
|
|
2086
|
+
id: string;
|
|
2087
|
+
name: string;
|
|
2088
|
+
email: string;
|
|
2089
|
+
}
|
|
2090
|
+
|
|
2091
|
+
interface UserListProps {
|
|
2092
|
+
users: User[];
|
|
2093
|
+
onSelect: (user: User) => void;
|
|
2094
|
+
searchLabel?: string;
|
|
2095
|
+
}
|
|
2096
|
+
|
|
2097
|
+
export function UserList({ users, onSelect, searchLabel = "Search users" }: UserListProps): React.JSX.Element {
|
|
2098
|
+
const [filter, setFilter] = useState("");
|
|
2099
|
+
|
|
2100
|
+
const handleFilterChange = useCallback(
|
|
2101
|
+
(event: React.ChangeEvent<HTMLInputElement>) => {
|
|
2102
|
+
setFilter(event.target.value);
|
|
2103
|
+
},
|
|
2104
|
+
[],
|
|
2105
|
+
);
|
|
2106
|
+
|
|
2107
|
+
const filteredUsers = useMemo(() => {
|
|
2108
|
+
const lower = filter.toLowerCase();
|
|
2109
|
+
return users.filter(
|
|
2110
|
+
(u) => u.name.toLowerCase().includes(lower) || u.email.toLowerCase().includes(lower),
|
|
2111
|
+
);
|
|
2112
|
+
}, [users, filter]);
|
|
2113
|
+
|
|
2114
|
+
return (
|
|
2115
|
+
<div role="search" aria-label={searchLabel}>
|
|
2116
|
+
<label htmlFor="user-search">{searchLabel}</label>
|
|
2117
|
+
<input
|
|
2118
|
+
id="user-search"
|
|
2119
|
+
type="text"
|
|
2120
|
+
value={filter}
|
|
2121
|
+
onChange={handleFilterChange}
|
|
2122
|
+
placeholder="Type to filter..."
|
|
2123
|
+
aria-describedby="user-count"
|
|
2124
|
+
/>
|
|
2125
|
+
<p id="user-count" aria-live="polite">
|
|
2126
|
+
{filteredUsers.length} users found
|
|
2127
|
+
</p>
|
|
2128
|
+
<ul role="list">
|
|
2129
|
+
{filteredUsers.map((user) => (
|
|
2130
|
+
<li key={user.id}>
|
|
2131
|
+
<button onClick={() => onSelect(user)} aria-label={\`Select \${user.name}\`}>
|
|
2132
|
+
{user.name} ({user.email})
|
|
2133
|
+
</button>
|
|
2134
|
+
</li>
|
|
2135
|
+
))}
|
|
2136
|
+
</ul>
|
|
2137
|
+
</div>
|
|
2138
|
+
);
|
|
2139
|
+
}`,
|
|
2140
|
+
expectedRuleIds: [],
|
|
2141
|
+
unexpectedRuleIds: ["CYBER-001", "CYBER-002", "SEC-001", "AUTH-001", "A11Y-001"],
|
|
2142
|
+
category: "clean",
|
|
2143
|
+
difficulty: "hard",
|
|
2144
|
+
},
|
|
1589
2145
|
];
|
|
1590
2146
|
// ─── Benchmark Runner ───────────────────────────────────────────────────────
|
|
1591
2147
|
export function runBenchmarkSuite(cases, judgeId) {
|