{ "feature_id": "F-006", "agent": "security", "verdict": "APPROVED", "threat_model": [ { "vector": "SQL injection", "result": "mitigated", "evidence": "Every query parameterized ($n). The only dynamic SQL (profile upsert, address update) builds SET clauses from a hardcoded column whitelist; user input only flows into parameter values." }, { "vector": "Privilege escalation (client sets own role)", "result": "mitigated", "evidence": "register/login schemas accept only email+password; no route writes identity_users.role. Role is returned from DB values, never echoed from input. DB CHECK constraint limits role to customer|admin." }, { "vector": "Broken access control / IDOR", "result": "mitigated", "evidence": "Each users route runs authenticate -> requireOwnerOrAdmin before data access; address repository scopes every query by user_id, so a valid foreign addressId is unreachable. Integration tests prove cross-user reads and mutations all return 403." }, { "vector": "Resource existence enumeration", "result": "mitigated", "evidence": "Authorization (403) is evaluated before existence checks (404) for non-owners, so a stranger cannot learn whether another user's profile exists." }, { "vector": "Stale authorization (demotion/promotion)", "result": "mitigated", "evidence": "Session authenticator resolves role from identity_users on every request; role changes apply immediately without re-login, and no role state is trusted from the client." }, { "vector": "Session security", "result": "unchanged from F-005", "evidence": "Opaque token in HttpOnly/SameSite=Lax cookie, SHA-256 hash stored server-side, expiry + revocation enforced in SQL. Cookie plugin moved to app root (cross-module), same policy." } ], "dependencies_added": [], "notes": "No new npm dependencies; spec/tech.md unchanged. The 403-vs-404 ordering is correct for anti-enumeration. GET /users admin list returning only profiled users is not a security issue (admin-only surface).", "evidence": "npm run test:integration: 22 passed including forged-cookie 401, cross-user 403 battery, and logout revocation regression" }