{ "metadata": { "reportDate": "2026-07-28", "codebaseName": "OpenFUT", "version": "0.1.0", "submodulesCovered": [ "openfut-core", "openfut-bridge", "openfut-launcher" ], "language": "Rust", "framework": "Axum + SQLite" }, "vulnerabilities": [ { "severity": "critical", "category": "authentication", "file": "openfut-core/src/services/profile.rs", "line": 8, "cwe": "CWE-287", "title": "Missing Authentication on All Endpoints", "description": "No authentication or authorization checks on any API endpoint. The system uses single-profile design with get_active_profile() returning the first row (LIMIT 1) without any token validation, session management, or per-user isolation. In a networked context, any HTTP client can access all endpoints without credentials.", "impact": "Complete compromise of data confidentiality and integrity. Any attacker can view, modify, or delete all user data without authentication.", "exploitPath": "curl http://127.0.0.1:8080/clubs - accesses club data without any auth headers or tokens", "recommendation": "Implement stateless JWT tokens or session-based authentication. Add middleware to validate tokens on all endpoints. Implement per-user authorization checks in services." }, { "severity": "critical", "category": "injection", "file": "openfut-core/src/routes/auth.rs", "line": 87, "cwe": "CWE-89", "title": "SQL Injection via String Interpolation", "description": "SQL table names are interpolated using string formatting: sqlx::query(&format!(\"DELETE FROM {table}\")). Although currently hardcoded in a loop, this violates parameterized query principles and creates a risk if the table list ever becomes user-controlled or the pattern is copied elsewhere.", "impact": "Potential remote code execution via database manipulation. If extended to user input, attackers could modify arbitrary tables or drop the database.", "exploitPath": "Currently mitigated by hardcoded table names, but the pattern is dangerous and violates secure coding practices.", "recommendation": "Use SQLx's dynamic query builders or identifier types that properly escape table/column names. Replace format! string interpolation with sqlx::query_builder for dynamic identifiers." }, { "severity": "high", "category": "configuration", "file": "openfut-bridge/src/proxy.rs", "line": 44, "cwe": "CWE-295", "title": "TLS Certificate Validation Disabled", "description": "HTTP client explicitly disables TLS certificate validation: .danger_accept_invalid_certs(true). This bypasses all certificate pinning, expiration, and hostname verification, making the bridge vulnerable to man-in-the-middle attacks.", "impact": "Attacker positioned between bridge and upstream can intercept, modify, or read all traffic. Compromises confidentiality and integrity of requests to Core and external services.", "exploitPath": "MITM attack between openfut-bridge and openfut-core or upstream services. ARP spoofing on localhost subnet would redirect traffic.", "recommendation": "Remove .danger_accept_invalid_certs(true) in production. If testing requires it, gate behind a development-only environment variable with strong warning. Use proper certificate management (CA bundles, cert pinning)." }, { "severity": "high", "category": "dos", "file": "openfut-core/src/services/season.rs", "line": 23, "cwe": "CWE-248", "title": "Unguarded expect() Causes Denial of Service", "description": "Multiple unchecked expect() calls that will panic and crash the server if database queries fail or return unexpected results: Ok(fetch(pool, profile_id).await?.expect(\"just inserted\"))", "impact": "Denial of service. A single database inconsistency or race condition crashes the entire server, making the application unavailable.", "exploitPath": "Trigger race conditions during concurrent requests (e.g., rapid profile deletion + season fetch). Database corruption or migration failure crashes the service immediately.", "recommendation": "Replace expect() with proper error handling (Result types, error logging, graceful degradation). Handle database query failures without panicking. Add integration tests for race conditions." }, { "severity": "high", "category": "dos", "file": "openfut-core/src/services/season.rs", "line": 69, "cwe": "CWE-248", "title": "Unguarded expect() in season fetch", "description": "let season = fetch(pool, profile_id).await?.expect(\"season must exist\"); Panics if season is not found.", "impact": "Server crash on missing or deleted season records.", "exploitPath": "Delete a season via concurrent requests, then call /seasons endpoint. Server panics.", "recommendation": "Return proper error (AppError::NotFound) instead of panicking." }, { "severity": "high", "category": "dos", "file": "openfut-core/src/services/season.rs", "line": 144, "cwe": "CWE-248", "title": "Unguarded expect() in season update", "description": "let updated = fetch(pool, profile_id).await?.expect(\"season must exist\");", "impact": "Server crash on concurrent season modifications.", "exploitPath": "Rapid concurrent season updates that fail race conditions.", "recommendation": "Handle missing records gracefully." }, { "severity": "high", "category": "cors", "file": "openfut-core/src/app.rs", "line": 257, "cwe": "CWE-346", "title": "Permissive CORS Configuration Allows All Origins", "description": ".layer(CorsLayer::permissive()) enables CORS for all origins (*), methods, and headers. Any website can make cross-origin requests to the API and access/modify data.", "impact": "Cross-site request forgery (CSRF) attacks. Malicious websites can issue API requests on behalf of users. Data exfiltration via JavaScript from any origin.", "exploitPath": "Attacker website:\n \n Fetch API calls to delete profiles, modify squads, etc.", "recommendation": "Restrict CORS to specific origins (e.g., localhost:3000 for web UI, or the game process if exposed). Use CorsLayer::very_restrictive() as default and explicitly allowlist origins." }, { "severity": "high", "category": "dos", "file": "openfut-bridge/src/proxy.rs", "line": 47, "cwe": "CWE-248", "title": "HTTP Client Construction Panic", "description": ".expect(\"failed to build HTTP client\") will panic if the HTTP client fails to initialize, crashing the entire proxy service on startup.", "impact": "Service unavailability. Bridge cannot start if HTTP client configuration is invalid.", "exploitPath": "Invalid system configuration or missing TLS libraries causes HTTP client build to fail, crashing bridge during startup.", "recommendation": "Return Result from new() and handle construction errors. Use anyhow::Context for better error messages." }, { "severity": "medium", "category": "information-disclosure", "file": "openfut-core/src/error.rs", "line": 54, "cwe": "CWE-209", "title": "Error Messages Leak Implementation Details", "description": "JSON parsing errors are returned directly to clients: format!(\"json parse error: {e}\"). Exposes serde_json parser internals and syntax details useful for crafting attacks.", "impact": "Information disclosure. Attackers learn the JSON parser implementation and can tailor payloads to bypass validation or find parser-specific quirks.", "exploitPath": "Send malformed JSON to any endpoint. Response includes parser error details (e.g., 'expected `,` at line 2 col 5') that aid in crafting exploits.", "recommendation": "Return generic error message to clients: 'invalid request format'. Log detailed errors internally with tracing for debugging." }, { "severity": "medium", "category": "information-disclosure", "file": "openfut-core/src/error.rs", "line": 40, "cwe": "CWE-215", "title": "Database Errors Logged with Full Details", "description": "Database errors are logged with full SQL/query details: tracing::error!(\"Database error: {e}\"). If logs are exposed or compromised, schema, query patterns, and data structure are revealed.", "impact": "Information disclosure in logs. Compromised log files expose database schema and query logic useful for SQL injection or data exfiltration planning.", "exploitPath": "Access server logs (via log aggregation service, file access, etc.) and extract database schema and query patterns.", "recommendation": "Log only error type and ID to clients. Sanitize logs before exporting. Use structured logging with field masking for queries." }, { "severity": "medium", "category": "input-validation", "file": "openfut-core/src/routes/auth.rs", "line": 17, "cwe": "CWE-1025", "title": "Hardcoded Default Credentials", "description": "Default username 'Player 1' is hardcoded with no unique identifier enforcement. Multiple profiles can be created with identical usernames, and weak defaults are used.", "impact": "Weak account creation, potential for account confusion or conflicts. No strong identity guarantees.", "exploitPath": "Multiple users create profiles with default 'Player 1' username. No way to distinguish profiles programmatically.", "recommendation": "Require explicit username on profile creation. Use UUIDs as primary identifiers. Validate username uniqueness and minimum length." }, { "severity": "medium", "category": "input-validation", "file": "openfut-core/src/services/", "line": 0, "cwe": "CWE-400", "title": "Missing Input Length Validation", "description": "No maximum length checks on string fields (usernames, club names, squad names, etc.). Large inputs can cause database bloat, memory exhaustion, or DoS.", "impact": "Denial of service via large payloads. Database bloat. Memory exhaustion. While DefaultBodyLimit::max(256KB) provides some protection, field-level validation is missing.", "exploitPath": "POST /auth/local with username = 256KB string. Database receives bloated data. Repeated calls exhaust storage.", "recommendation": "Add input validation for all user-submitted strings. Set maximum lengths (e.g., username: 50 chars, club name: 100 chars). Validate at route handler level." }, { "severity": "medium", "category": "configuration", "file": "openfut-core/src/db.rs", "line": 13, "cwe": "CWE-315", "title": "Unencrypted SQLite Database on Disk", "description": "SQLite database file (openfut.db) is stored unencrypted on disk. All user data, profiles, squads, cards, etc., are readable by anyone with filesystem access.", "impact": "Data breach if server filesystem is compromised. No protection against:local file access, stolen backups, forensic recovery.", "exploitPath": "Attacker gains filesystem access (compromised server, stolen disk). Reads openfut.db directly. All game data is readable without authentication.", "recommendation": "Use SQLite encryption (e.g., sqlcipher crate) or migrate to PostgreSQL with TLS. Implement file-level encryption. Use restrictive filesystem permissions (0600)." }, { "severity": "medium", "category": "rate-limiting", "file": "openfut-core/src/app.rs", "line": 0, "cwe": "CWE-770", "title": "No Rate Limiting on Endpoints", "description": "No per-IP or per-user rate limiting. Endpoints like POST /auth/reset can be called repeatedly without restriction, allowing attackers to repeatedly wipe all data.", "impact": "Denial of service and data destruction. Attacker can spam /auth/reset to destroy user data or exhaust server resources.", "exploitPath": "for i in 1..1000: POST /auth/reset with confirm='reset'. All data wiped repeatedly.", "recommendation": "Implement rate limiting middleware using tower_governor or similar. Add per-IP limits (e.g., 10 requests/min) and per-endpoint limits. Use exponential backoff." }, { "severity": "low", "category": "audit-logging", "file": "openfut-core/src/services/", "line": 0, "cwe": "CWE-778", "title": "Missing Audit Logging", "description": "No audit trail of user actions (profile creation, data deletion, squad modifications). Cannot detect unauthorized access, data tampering, or compliance violations.", "impact": "Incident response and forensics are impossible. Cannot determine who did what and when. Compliance risks (GDPR, etc.).", "exploitPath": "Attacker deletes all profiles, modifies squads. No audit log shows what happened or who did it.", "recommendation": "Add audit logging for all data mutations. Log: timestamp, user (profile) ID, action, resource affected, before/after state. Store in separate immutable table." }, { "severity": "low", "category": "dependencies", "file": "openfut-bridge/Cargo.toml", "line": 0, "cwe": "CWE-1035", "title": "Older Dependency Versions (reqwest, rustls)", "description": "openfut-bridge uses reqwest 0.11 (latest is 0.12) and rustls 0.21 (latest is 0.23). Intentional for version matching, but creates a larger surface area for known CVEs.", "impact": "Potential vulnerabilities in older dependencies. Delayed access to security patches.", "exploitPath": "Known CVE in reqwest 0.11 or rustls 0.21 could be exploited. Combined with danger_accept_invalid_certs, TLS bypass becomes easier.", "recommendation": "Upgrade dependencies to latest versions when possible. Monitor CVE databases (CVE, RustSec) for the versions in use. Pin versions and set up automated dependency updates." }, { "severity": "low", "category": "error-handling", "file": "openfut-core/src/app.rs", "line": 256, "cwe": "CWE-248", "title": "Body Size Limit Without Per-Field Validation", "description": "DefaultBodyLimit::max(256KB) limits the entire request body, but individual fields are not validated. A single large field can consume most of the limit.", "impact": "Mild DoS. Large field values cause database bloat. Not a critical issue due to body limit, but field-level validation would be better.", "exploitPath": "POST /auth/local with 250KB club_name field. Database receives bloated data.", "recommendation": "Add per-field validation in addition to body limits. Validate and sanitize fields before database insertion." } ], "riskScore": 82, "riskCategory": "CRITICAL", "riskSummary": "OpenFUT has critical security issues that would make it unsafe for production or networked deployment. The most severe are the complete absence of authentication/authorization and the SQL injection pattern in the auth.rs module. The system is designed as single-player (single-profile) with no multi-tenant isolation, which is dangerous if exposed to the network.", "recommendations": [ { "priority": "CRITICAL", "area": "Authentication & Authorization", "recommendation": "Implement JWT-based or session-based authentication on all endpoints. Add middleware to validate auth tokens on every request. Implement per-profile authorization checks. Currently any HTTP client can access all endpoints.", "effort": "High", "impact": "Blocks all data breaches from unauthenticated access" }, { "priority": "CRITICAL", "area": "SQL Injection Prevention", "recommendation": "Replace sqlx::query(&format!(...)) in auth.rs:87 with proper parameterized identifiers. Use sqlx::query_builder for dynamic table/column names instead of string interpolation.", "effort": "Low", "impact": "Prevents SQL injection even if pattern is copied to user input" }, { "priority": "HIGH", "area": "TLS & Transport Security", "recommendation": "Remove .danger_accept_invalid_certs(true) from proxy.rs:44. If development requires it, gate behind an environment variable (e.g., DEV_SKIP_TLS_VERIFICATION) with strong warnings in logs.", "effort": "Low", "impact": "Prevents MITM attacks on bridge-to-core communication" }, { "priority": "HIGH", "area": "Error Handling", "recommendation": "Replace all expect() calls with proper Result handling. Use anyhow::Context or custom error types. Add logging for debugging but return generic errors to clients.", "effort": "Medium", "impact": "Prevents DoS via server panics" }, { "priority": "HIGH", "area": "CORS", "recommendation": "Replace CorsLayer::permissive() with CorsLayer::very_restrictive() or explicit allowlist. For single-player use, restrict to localhost and the game process only.", "effort": "Low", "impact": "Prevents CSRF and cross-origin attacks" }, { "priority": "HIGH", "area": "Rate Limiting", "recommendation": "Add per-IP rate limiting using tower_governor or similar. Implement limits on destructive endpoints (e.g., POST /auth/reset: 1 request per hour per IP).", "effort": "Medium", "impact": "Prevents DoS and repeated data destruction" }, { "priority": "MEDIUM", "area": "Input Validation", "recommendation": "Add maximum length validation for all string fields (username, club_name, squad_name, etc.). Enforce at route handler level. Example: username max 50 chars, club_name max 100 chars.", "effort": "Medium", "impact": "Prevents database bloat and data validation failures" }, { "priority": "MEDIUM", "area": "Data Encryption", "recommendation": "Use SQLite encryption (sqlcipher) or migrate to PostgreSQL with TLS. Set restrictive filesystem permissions (0600) on openfut.db.", "effort": "High", "impact": "Protects data at rest from filesystem access" }, { "priority": "MEDIUM", "area": "Error Message Handling", "recommendation": "Return generic error messages to clients. Log detailed errors internally. Example: client sees 'invalid request', server logs 'JSON parse error: expected `,` at line 2'.", "effort": "Low", "impact": "Reduces information disclosure" }, { "priority": "MEDIUM", "area": "Audit Logging", "recommendation": "Add audit trail for all data mutations (create, update, delete). Log timestamp, profile ID, action, resource, and before/after state. Store in immutable audit_log table.", "effort": "Medium", "impact": "Enables incident response and forensics" }, { "priority": "LOW", "area": "Dependency Management", "recommendation": "Upgrade reqwest to 0.12 and rustls to 0.23 when possible. Set up Dependabot or RustSec monitoring for CVEs. Regularly audit dependencies.", "effort": "Low", "impact": "Reduces attack surface from known CVEs" }, { "priority": "LOW", "area": "Default Values", "recommendation": "Remove hardcoded default username 'Player 1'. Require explicit username on profile creation. Use UUIDs for profile identification.", "effort": "Low", "impact": "Improves account identity and prevents confusion" } ], "securityDesignNotes": { "intendedUse": "OpenFUT is designed for single-player offline use. Single-profile design is intentional for local FIFA 23 emulation.", "deploymentContext": "Localhost only (127.0.0.1:8080). Not intended for networked or multi-user deployment.", "implicationForSecurity": "Many security issues (no auth, permissive CORS) are acceptable for localhost-only use. However, the code structure lacks security boundaries, so if ever exposed to the network, it would be completely unsecured. Recommend adding security gates now rather than retrofitting later.", "suggestedDefensiveApproach": "Even for single-player use, add security layers (basic auth, CORS restrictions, rate limiting) to prevent accidental misuse if deployed in an unsafe context." }, "positiveFindingsAndStrengths": [ "✓ SQLx is used throughout with parameterized queries (except auth.rs:87)", "✓ Foreign key constraints are enforced in SQLite", "✓ UUIDs are used for entity IDs instead of sequential IDs (reduces enumeration attacks)", "✓ Request body size is limited to 256KB (prevents large payload DoS)", "✓ Concurrency is limited to 256 concurrent requests", "✓ Sensitive tokens (X-UT-SID, X-UT-PHISHING-TOKEN) are stripped from captures", "✓ Logging is structured using tracing crate (good for audit trails)", "✓ Services layer properly encapsulates database access" ], "testingRecommendations": [ "Add integration tests for authentication bypass (attempt to access endpoints without tokens)", "Test SQL injection payloads in auth.rs:87 pattern (if table names become dynamic)", "Test CORS with cross-origin requests from external origins", "Test rate limiting with rapid concurrent requests to /auth/reset", "Test input validation with oversized strings (100MB+ usernames)", "Test panic handling with corrupted database state", "Test TLS MITM scenarios (certificate pinning validation)", "Add fuzz testing for JSON parsing to find edge cases" ], "complianceNotes": { "gdpr": "No explicit data handling policy. If user data is processed, GDPR requires consent, data retention limits, and audit trails. Not currently implemented.", "dataProtection": "Unencrypted database at rest violates most data protection frameworks.", "logging": "Audit logging is missing, violating compliance requirements." } }