341 lines
22 KiB
JSON
341 lines
22 KiB
JSON
{
|
|
"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 <img src=\"http://127.0.0.1:8080/clubs\" />\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<ProxyState, Error> 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."
|
|
}
|
|
}
|