| C1 |
Spoofing |
PARTIAL – the JWT signing secret is hard‑coded and printed, but user passwords are never stored in clear text. |
app/auth/auth.py – SECRET_KEY = os.getenv("OAUTH_SECRET_KEY","0f2883…") app/auth/auth.py – print(f"token: {token}") |
| C2 |
Spoofing |
VALID – JWTs contain a jti claim but no store‑or‑check mechanism; replay is possible. |
app/auth/auth.py – "jti": str(uuid.uuid4()), (no later lookup) |
| C3 |
Tampering |
PARTIAL – default MySQL credentials are hard‑coded; an attacker who obtains them could modify the DB. No raw SQL injection path. |
app/database/session.py – DATABASE_USER = os.getenv("MYSQL_DEV_USER","dev_project") DATABASE_PASSWORD = os.getenv("MYSQL_DEV_PASSWORD","SECURE_PASSWORD") |
| C4 |
Tampering |
VALID – static secret enables an attacker who reads the source to forge arbitrary JWTs. |
Same evidence as C1 (hard‑coded secret). |
| C5 |
Repudiation |
VALID – the application logs only table‑creation events; authentication, token issuance, and profile changes are never recorded. |
app/main.py – logger.info("Creating tables...") (no other security‑relevant logs). |
| C6 |
Information Disclosure |
VALID – CORS is configured with a wildcard origin while allowing credentials. |
app/main.py – origins = ["*"] and allow_credentials=True. |
| C7 |
Information Disclosure |
VALID – tokens (and usernames) are printed to stdout in the auth flow, potentially ending up in logs. |
app/auth/auth.py – print(f"token: {token}") and print(username). |
| C8 |
Denial‑of‑Service |
VALID – no rate‑limiting is applied to the /token endpoint, permitting credential‑stuffing or brute‑force attacks. |
app/routes/auth.py – /token route has no limiter middleware. |
| C9 |
Denial‑of‑Service |
VALID – profile CRUD endpoints accept unbounded JSON payloads; no size or request‑rate constraints are enforced. |
app/routes/profiles.py – create_profile, update_own_profile, delete_own_profile accept any request body without checks. |
| C10 |
Elevation of Privilege |
INVALID – every protected request re‑validates the user record and aborts if user.disabled is true, so a disabled account cannot use an existing token. |
app/auth/auth.py – if user.disabled: raise HTTPException(status_code=400, detail="User is deactivated"). |
| C11 |
Elevation of Privilege |
INVALID – the API currently has no privileged (admin) endpoints; lack of role claims does not constitute an actual risk today. |
No role‑related code in the repository. |
| T12 (new) |
Spoofing / Information Disclosure |
VALID – JWTs are stored in browser localStorage, which is readable by any script and thus vulnerable to XSS token theft. |
web/src/components/LoginForm.vue – localStorage.setItem('access_token', result.access_token); web/src/store/index.js – localStorage.setItem('access_token', result.access_token); (same logic in the legacy component). |
| T13 (new) |
Tampering |
VALID – default MySQL credentials (dev_project / SECURE_PASSWORD) are compiled into the code; if the environment does not override them, the database is exposed with a known password. |
Same evidence as C3 (session.py defaults). |
Final Validation - Threat Model
Executive Summary (validated)
The Genesis IAM service contains several concrete security weaknesses that are validated against the source code.
The most critical issues are:
print()statements that leak tokens (C1, C4, C7).dev_project/SECURE_PASSWORD), making the DB accessible if the environment is not overridden (C3‑new T13).localStoragecreates a clear XSS‑theft vector (new T12).All other catalogued items (C10, C11) were found not applicable to the current implementation.
Consolidated Threat List (validated)
app/auth/auth.py–SECRET_KEY = os.getenv("OAUTH_SECRET_KEY","0f2883…")app/auth/auth.py–print(f"token: {token}")jticlaim but no store‑or‑check mechanism; replay is possible.app/auth/auth.py–"jti": str(uuid.uuid4()),(no later lookup)app/database/session.py–DATABASE_USER = os.getenv("MYSQL_DEV_USER","dev_project")DATABASE_PASSWORD = os.getenv("MYSQL_DEV_PASSWORD","SECURE_PASSWORD")app/main.py–logger.info("Creating tables...")(no other security‑relevant logs).app/main.py–origins = ["*"]andallow_credentials=True.app/auth/auth.py–print(f"token: {token}")andprint(username)./tokenendpoint, permitting credential‑stuffing or brute‑force attacks.app/routes/auth.py–/tokenroute has no limiter middleware.app/routes/profiles.py–create_profile,update_own_profile,delete_own_profileaccept any request body without checks.user.disabledis true, so a disabled account cannot use an existing token.app/auth/auth.py–if user.disabled: raise HTTPException(status_code=400, detail="User is deactivated").localStorage, which is readable by any script and thus vulnerable to XSS token theft.web/src/components/LoginForm.vue–localStorage.setItem('access_token', result.access_token);web/src/store/index.js–localStorage.setItem('access_token', result.access_token);(same logic in the legacy component).dev_project/SECURE_PASSWORD) are compiled into the code; if the environment does not override them, the database is exposed with a known password.Recommendations (high‑level)
kidin the header.jtivalues (e.g., Redis) and reject replayed tokens; provide a revocation list for logout/revoke.slowapior custom middleware) on/token,/refresh_token, and profile routes; lock accounts after repeated failures.max_content_length=1 MiB) on profile endpoints.localStoragetoken storage withhttpOnly/Securecookies or a protected IndexedDB store; mitigate XSS exposure.Addressing the validated findings will substantially raise the security posture of the Genesis IAM service.
audit trail: adaptive-npr-tmp-4-threat-model
https://adaptive-npr-tmp-4-threat-model.s3.eu-west-1.amazonaws.com/genesis%2F2026-01-27_04-35-17-4ae971e77d6dff934cbd7b1734e8fd5ceca1ef43