|
@@ -11,7 +11,7 @@
|
|
|
>
|
|
>
|
|
|
> Each finding is referenced as **F<N>** for later citation.
|
|
> Each finding is referenced as **F<N>** for later citation.
|
|
|
>
|
|
>
|
|
|
-> **Findings rolled up:** 5 sev-3 (5 fixed, 0 open), 27 sev-2 (27 fixed, 0 open), 42 sev-1 (41 fixed, 1 open).
|
|
|
|
|
|
|
+> **Findings rolled up:** 5 sev-3 (5 fixed, 0 open), 27 sev-2 (27 fixed, 0 open), 42 sev-1 (42 fixed, 0 open).
|
|
|
|
|
|
|
|
---
|
|
---
|
|
|
|
|
|
|
@@ -2353,6 +2353,25 @@
|
|
|
the structured logger. No log-rate cap, no aggregation. Pair with
|
|
the structured logger. No log-rate cap, no aggregation. Pair with
|
|
|
F1 (XFF spoof) and F2 (no per-user bucket) to amplify.
|
|
F1 (XFF spoof) and F2 (no per-user bucket) to amplify.
|
|
|
- **Severity: 1**
|
|
- **Severity: 1**
|
|
|
|
|
+- **Status:** Fixed. `LoginThrottle::recordFailure` now emits a
|
|
|
|
|
+ `warning` only at `failure_count === 1` — the FIRST failure in a
|
|
|
|
|
+ given (username, ip) bucket since the bucket was created or
|
|
|
|
|
+ cleared. Subsequent failures within the bucket stay silent until
|
|
|
|
|
+ a lockout fires, which still emits the existing `error` line
|
|
|
|
|
+ carrying `failure_count` and `lock_seconds`. Total visibility is
|
|
|
|
|
+ preserved at aggregate (the lockout error carries the burst size)
|
|
|
|
|
+ without per-attempt spam: a 1000-IP botnet spraying one username
|
|
|
|
|
+ used to emit ~24 username-bucket warnings + 4×1000 = 4024
|
|
|
|
|
+ per-IP-bucket warnings before everything was locked; now it
|
|
|
|
|
+ emits 1 username-bucket warning + 1000 per-IP-bucket warnings,
|
|
|
|
|
+ about 4× quieter, with the lockout errors carrying the actual
|
|
|
|
|
+ count. The `error`-on-lockout signal stays loud because that's
|
|
|
|
|
+ the genuinely-actionable event. Regression tests in
|
|
|
|
|
+ `ui/tests/Unit/Auth/LoginThrottleTest.php`:
|
|
|
|
|
+ `testRecordFailureLogRateIsCappedToFirstWarningPerBucket`
|
|
|
|
|
+ (5 failures → 1 warning + ≥1 error) and
|
|
|
|
|
+ `testHighRateBruteForceDoesNotSpamPerFailureWarnings` (100
|
|
|
|
|
+ failures into the same bucket → exactly 1 warning).
|
|
|
|
|
|
|
|
---
|
|
---
|
|
|
|
|
|