|
|
@@ -11,7 +11,7 @@
|
|
|
>
|
|
|
> Each finding is referenced as **F<N>** for later citation.
|
|
|
>
|
|
|
-> **Findings rolled up:** 5 sev-3, 27 sev-2, 42 sev-1.
|
|
|
+> **Findings rolled up:** 5 sev-3 (3 fixed, 2 open), 27 sev-2, 42 sev-1.
|
|
|
|
|
|
---
|
|
|
|
|
|
@@ -68,6 +68,18 @@
|
|
|
it via `X-Acting-User-Id` on every other admin endpoint. A leaked
|
|
|
service token is a single-step total compromise with no log trail.
|
|
|
- **Severity: 3**
|
|
|
+- **Status:** Fixed in `8a94dff`. `UserRepository::upsertLocal` now
|
|
|
+ looks up the existing local row by `is_local = 1` (not by
|
|
|
+ `display_name`) and updates `display_name` + `last_login_at` on it;
|
|
|
+ it only inserts when zero local rows exist. Migration
|
|
|
+ `20260504100000_add_unique_local_user_index` adds a partial unique
|
|
|
+ index (`WHERE is_local = 1` on SQLite; functional index over a
|
|
|
+ `CASE` expression on MySQL) so a regression in code still fails at
|
|
|
+ the DB. Regression tests in `api/tests/Integration/Auth/AuthEndpointsTest.php`
|
|
|
+ (`testRotatingUsernamesNeverCreatesAdditionalLocalAdmins`,
|
|
|
+ `testDbLayerRejectsSecondLocalAdminInsert`). The
|
|
|
+ unaudited-write half of the finding (no `AuditEmitter` call on
|
|
|
+ user creation / role grant) is tracked separately as F5.
|
|
|
|
|
|
### F4 — Audit emit is non-transactional with state mutation
|
|
|
- **Files:** `api/src/Infrastructure/Audit/DbAuditEmitter.php:37-48`;
|