| C1 |
Spoofing |
The JWT signing key (SECRET_KEY) is hard‑coded with a default value. An attacker who discovers the secret can forge arbitrary access / refresh tokens and impersonate any user (including admin). |
22 |
✅ VALID |
app/auth/auth.py – line: SECRET_KEY = os.getenv("OAUTH_SECRET_KEY","0f2883258b3c2cb9e21f1bdc827eafb9b7ad5509bf37103f82a1abab9109c65a") |
| C2 |
Spoofing |
JWT tokens are stored in the browser’s localStorage (both legacy and current Vue components). Any XSS can read the tokens and hijack the session. |
22 |
✅ VALID |
web/src/components/LoginForm.vue – localStorage.setItem('access_token', result.access_token);
web/src/components/LoginForm.vue – localStorage.setItem('refresh_token', result.refresh_token); |
| C3 |
Tampering |
CORS is configured with allow_origins=["*"] and allow_credentials=True, allowing any origin to make authenticated requests with a victim’s JWT (CSRF‑style attacks). |
21 |
✅ VALID |
app/main.py – origins = ["*"] and allow_credentials=True in CORSMiddleware setup |
| C6 |
Repudiation |
No immutable audit trail is kept for authentication events, token refreshes, profile changes, or user deletions, making it impossible to prove who performed which action. |
16 |
✅ VALID |
The code contains only print statements (e.g., in app/auth/auth.py) and no calls to a logging/audit facility. |
| C7 |
Information Disclosure |
Public endpoints (/users/{user_id} and /users/search) are accessible without authentication, allowing anyone to enumerate existing accounts. |
22 |
✅ VALID |
app/routes/users.py – @router.get("/{user_id}") and @router.post("/search") have no Depends(oauth_authenticate_current_user) |
| C8 |
Tampering |
The email column is unique only at the DB level; the API checks for duplicates but does not protect against race conditions, so concurrent requests can trigger an integrity error that is exposed to the client. |
14 |
✅ VALID |
app/services/user_service.py – validate_existing_user checks email/username before create_user_db; DB column email is marked unique=True in app/models/user_db_model.py. |
| C9 |
Information Disclosure |
Debug print statements log full JWT payloads, usernames and DB objects to stdout; logs may be readable by unauthorized parties, leaking sensitive data. |
17 |
✅ VALID |
app/auth/auth.py – print(f"token: {token}"), print(token), print(username), print(f"db user: {user}") |
| C10 |
Denial of Service |
No rate‑limiting or brute‑force protection is applied to the /token and /users endpoints, allowing attackers to flood these endpoints with unlimited requests (credential‑stuffing, DoS). |
22 |
✅ VALID |
No import of a rate‑limiting library (e.g., slowapi) and no middleware enforcing request caps. |
| C11 |
Elevation of Privilege |
Endpoints that retrieve user data (/users/{user_id} and /users/search) do not verify that the caller is authorised to view that specific user, allowing any authenticated (or unauthenticated) requester to read another user’s personal information. |
17 |
✅ VALID |
Same evidence as C7 – missing oauth_authenticate_current_user dependency on these routes. |
| C12 (new) |
Spoofing / Information Disclosure |
The MySQL connection uses a hard‑coded default password (SECURE_PASSWORD) when the environment variable is not set, exposing the DB to credential leakage and potential unauthorised access. |
23 (critical) |
✅ VALID |
app/database/session.py – DATABASE_PASSWORD = os.getenv("MYSQL_DEV_PASSWORD", "SECURE_PASSWORD") |
Final Validation - Threat Model
Validated Threat‑Model (only ✅ VALID /⚠️ PARTIAL entries)
Executive Summary (rewritten)
localStorage. Four are high‑priority – unrestricted CORS, missing audit logging, unauthenticated user‑enumeration, and lack of rate‑limiting. The remaining three are medium (debug prints leaking data, email‑uniqueness race, missing authorization on user‑lookup).localStorage– usehttpOnlySecure cookies or in‑memory storage. 4️⃣ Lock down CORS to the known front‑end origin(s) and disableallow_credentialsunless required. 5️⃣ Add audit logging & rate‑limiting, and enforce proper authorization on all user‑lookup endpoints.Consolidated Threat Mapping (validated)
SECRET_KEY) is hard‑coded with a default value. An attacker who discovers the secret can forge arbitrary access / refresh tokens and impersonate any user (including admin).app/auth/auth.py– line:SECRET_KEY = os.getenv("OAUTH_SECRET_KEY","0f2883258b3c2cb9e21f1bdc827eafb9b7ad5509bf37103f82a1abab9109c65a")localStorage(both legacy and current Vue components). Any XSS can read the tokens and hijack the session.web/src/components/LoginForm.vue–localStorage.setItem('access_token', result.access_token);web/src/components/LoginForm.vue–localStorage.setItem('refresh_token', result.refresh_token);allow_origins=["*"]andallow_credentials=True, allowing any origin to make authenticated requests with a victim’s JWT (CSRF‑style attacks).app/main.py–origins = ["*"]andallow_credentials=TrueinCORSMiddlewaresetupprintstatements (e.g., inapp/auth/auth.py) and no calls to a logging/audit facility./users/{user_id}and/users/search) are accessible without authentication, allowing anyone to enumerate existing accounts.app/routes/users.py–@router.get("/{user_id}")and@router.post("/search")have noDepends(oauth_authenticate_current_user)emailcolumn is unique only at the DB level; the API checks for duplicates but does not protect against race conditions, so concurrent requests can trigger an integrity error that is exposed to the client.app/services/user_service.py–validate_existing_userchecksemail/usernamebeforecreate_user_db; DB columnemailis markedunique=Trueinapp/models/user_db_model.py.printstatements log full JWT payloads, usernames and DB objects to stdout; logs may be readable by unauthorized parties, leaking sensitive data.app/auth/auth.py–print(f"token: {token}"),print(token),print(username),print(f"db user: {user}")/tokenand/usersendpoints, allowing attackers to flood these endpoints with unlimited requests (credential‑stuffing, DoS).slowapi) and no middleware enforcing request caps./users/{user_id}and/users/search) do not verify that the caller is authorised to view that specific user, allowing any authenticated (or unauthenticated) requester to read another user’s personal information.oauth_authenticate_current_userdependency on these routes.SECURE_PASSWORD) when the environment variable is not set, exposing the DB to credential leakage and potential unauthorised access.app/database/session.py–DATABASE_PASSWORD = os.getenv("MYSQL_DEV_PASSWORD", "SECURE_PASSWORD")Threats C4 (Stored XSS) and C5 (Verbose authentication errors) were invalid because the API returns generic JSON errors and never renders user‑supplied data as HTML. They have been omitted from the final list.
Additional Missing Threat (added)
SECURE_PASSWORD) inapp/database/session.pycan be used by an attacker who gains access to the source or deployment environment to connect to the database.app/database/session.py–DATABASE_PASSWORD = os.getenv("MYSQL_DEV_PASSWORD", "SECURE_PASSWORD")End of validated threat‑model.