# Security audit — user_service.py (no skill)

## Findings
### F1: SQL injection in find_user via string-formatted query
- Severity: Critical
- Line: 41
- Why it is exploitable: The `username` argument is interpolated directly into the SQL statement with `%` formatting (`"... WHERE name = '%s'" % username`) and executed on line 42. An attacker controlling the login username can break out of the quoted string, e.g. with a `' UNION SELECT ... --` payload, to return an attacker-chosen row (including a password_hash value they control, defeating the comparison in `verify_login`), bypass authentication, or extract other users' names and password hashes from the `users` table.
- Fix: Use a parameterized query exactly as `create_user` does: `db.execute("SELECT id, name, password_hash FROM users WHERE name = ?", (username,))`. Never build SQL with string formatting/concatenation of user input.

### F2: Remote code execution via eval() on user input in calculate
- Severity: Critical
- Line: 54
- Why it is exploitable: `calculate()` passes its `expression` argument directly to Python `eval()`, and `ROUTES` exposes it unauthenticated via `POST /calc` with `req["expression"]`. `eval` is not an arithmetic parser: a caller can supply expressions such as calls to `__import__("os").system(...)` or `open(...)` to execute arbitrary Python/shell commands, read files and secrets, and fully compromise the host process.
- Fix: Remove `eval()` entirely. Parse arithmetic safely, e.g. with `ast.parse(expression, mode="eval")` and a strict allowlist walker that only permits numeric constants, `+ - * / // % **`, parentheses and unary plus/minus, rejecting all names, calls, attributes and subscripts; or use a dedicated safe expression-evaluation library.

### F3: Hardcoded live payment API secret in source code
- Severity: Critical
- Line: 11
- Why it is exploitable: A live Stripe secret key (`STRIPE_API_KEY`, `sk_live_...`) is embedded as a literal constant in the source file. Anyone who obtains the code — via repository access, a leak, a backup, or code disclosure — gains the full payment-provider credential and can make API calls as the account (charges, refunds, reading payment data) until it is revoked; removing it from a later commit does not remove it from git history.
- Fix: Revoke/rotate the exposed key immediately since it must be treated as compromised, remove the literal from the source and its history, and load the replacement at runtime from an environment variable or a secrets manager (e.g. `os.environ["STRIPE_API_KEY"]`), never committing real credentials.

### F4: Weak, unsalted MD5 password hashing
- Severity: High
- Line: 25
- Why it is exploitable: `hash_password()` stores passwords as a bare `hashlib.md5(password).hexdigest()` — MD5 is a fast general-purpose hash with no salt, no work factor and no memory hardness. If the `users` table leaks (for example via F1), hashes can be cracked at billions of guesses per second on commodity GPUs or matched instantly against precomputed/rainbow tables, and identical passwords are visibly identical across accounts, recovering users' plaintext passwords.
- Fix: Use a dedicated password KDF with a per-user random salt and a tuned cost, e.g. `hashlib.scrypt`, `hashlib.pbkdf2_hmac` with a high iteration count, or the `argon2`/`bcrypt` libraries, storing the algorithm, salt and parameters with the hash, and verify with a constant-time comparison such as `hmac.compare_digest`.

## Summary line
Findings: 4
