|
@@ -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 (40 fixed, 2 open).
|
|
|
|
|
|
|
+> **Findings rolled up:** 5 sev-3 (5 fixed, 0 open), 27 sev-2 (27 fixed, 0 open), 42 sev-1 (41 fixed, 1 open).
|
|
|
|
|
|
|
|
---
|
|
---
|
|
|
|
|
|
|
@@ -2332,6 +2332,20 @@
|
|
|
message-suppression. Cast to int explicitly and reject non-numeric
|
|
message-suppression. Cast to int explicitly and reject non-numeric
|
|
|
codes.
|
|
codes.
|
|
|
- **Severity: 1**
|
|
- **Severity: 1**
|
|
|
|
|
+- **Status:** Fixed. `JsonExceptionHandler::__invoke` now requires
|
|
|
|
|
+ `is_int($code)` before treating `getCode()` as an HTTP status.
|
|
|
|
|
+ PDO-derived exceptions return SQLSTATE *strings* — the previous
|
|
|
|
|
+ `>= 400 && < 600` loose-compare coerced them to int and could
|
|
|
|
|
+ land on a real status for the wrong reason (`'400'` → 400 with
|
|
|
|
|
+ the prod message-suppression skipped). The new gate falls back
|
|
|
|
|
+ to 500 for any non-int code, including the default 0 from
|
|
|
|
|
+ `new \Exception('msg')`. Regression tests in
|
|
|
|
|
+ `ui/tests/Unit/Http/JsonExceptionHandlerTest.php`:
|
|
|
|
|
+ `testStringCodeFallsBackTo500` (a `'400'` string code → 500),
|
|
|
|
|
+ `testIntCodeInRangeIsHonored` (a real `403` → 403),
|
|
|
|
|
+ `testIntCodeOutOfRangeFallsBackTo500` (200 not an error code →
|
|
|
|
|
+ 500), `testCodeOfZeroFallsBackTo500` (default 0 → 500),
|
|
|
|
|
+ `testSqlstateLikeStringIsNotCoercedIntoStatus` (`'42S02'` → 500).
|
|
|
|
|
|
|
|
### F74 — `LoginThrottle` writes `logger->warning` per failure with no log-rate cap
|
|
### F74 — `LoginThrottle` writes `logger->warning` per failure with no log-rate cap
|
|
|
- **File:** `ui/src/Auth/LoginThrottle.php:79-93`
|
|
- **File:** `ui/src/Auth/LoginThrottle.php:79-93`
|