|
|
@@ -29,6 +29,11 @@
|
|
|
Argon2id slows it but does not prevent it. The UI is also reachable
|
|
|
on `:8080` directly per `docker-compose.yml`.
|
|
|
- **Severity: 3**
|
|
|
+- **Status:** Fixed in `466d686`. `extractSourceIp` no longer reads
|
|
|
+ `X-Forwarded-For`; Caddy's `trusted_proxies static private_ranges`
|
|
|
+ is the single trust boundary and PHP consumes only `REMOTE_ADDR`.
|
|
|
+ Regression test in `ui/tests/Integration/Auth/LocalLoginTest.php`
|
|
|
+ (`testRotatingXForwardedForDoesNotEvadePerIpLockout`).
|
|
|
|
|
|
### F2 — Throttle bucket includes IP → IP-rotation defeats the per-user lockout
|
|
|
- **File:** `ui/src/Auth/LoginThrottle.php:131-137`
|
|
|
@@ -38,6 +43,15 @@
|
|
|
pool) gets unlimited password attempts against the single
|
|
|
`LOCAL_ADMIN_USERNAME` account because each new IP is a fresh bucket.
|
|
|
- **Severity: 3**
|
|
|
+- **Status:** Fixed in `466d686`. `LoginThrottle` now evaluates a second
|
|
|
+ per-username bucket alongside the per-(user, ip) one with a
|
|
|
+ 25 / 50 / 100 → 60 s / 300 s / 1800 s ladder; `isLocked()` trips on
|
|
|
+ either bucket and `clear()` resets both on successful login.
|
|
|
+ Regression tests in `ui/tests/Unit/Auth/LoginThrottleTest.php`
|
|
|
+ (`testPerUsernameBucketLocksOutAcrossDistinctIps`,
|
|
|
+ `testPerUsernameLadderProgresses`,
|
|
|
+ `testLockoutSecondsRemainingReturnsLargerOfBuckets`) and
|
|
|
+ `LocalLoginTest::testRotatingRemoteAddrEventuallyHitsPerUsernameLockout`.
|
|
|
|
|
|
### F3 — Service token + `upsertLocal` mints arbitrary Admin users with no audit, RBAC, rate-limit, or impersonation
|
|
|
- **Files:** `api/src/App/AppFactory.php:156-169`,
|