|
|
@@ -11,7 +11,7 @@
|
|
|
>
|
|
|
> 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 (4 fixed, 38 open).
|
|
|
+> **Findings rolled up:** 5 sev-3 (5 fixed, 0 open), 27 sev-2 (27 fixed, 0 open), 42 sev-1 (5 fixed, 37 open).
|
|
|
|
|
|
---
|
|
|
|
|
|
@@ -1287,6 +1287,25 @@
|
|
|
`password_get_info()` and refuse to enable local-admin if the
|
|
|
algorithm is below threshold.
|
|
|
- **Severity: 1**
|
|
|
+- **Status:** Fixed. `App\App\Config::validateOrExit` (called from
|
|
|
+ `Bootstrap::run()` before `Container::build()`) now refuses to boot
|
|
|
+ when `LOCAL_ADMIN_ENABLED=true` and the configured
|
|
|
+ `LOCAL_ADMIN_PASSWORD_HASH` is anything other than Argon2id or
|
|
|
+ bcrypt with cost ≥ 12 (new `Config::BCRYPT_MIN_COST` constant). The
|
|
|
+ algorithm is read via `password_get_info()`, so unknown / legacy
|
|
|
+ formats (`$1$…` md5-crypt, `$5$…` sha256-crypt, plain text, base64
|
|
|
+ noise) all collapse into the rejection branch with the same
|
|
|
+ human-readable message pointing the operator at
|
|
|
+ `password_hash('…', PASSWORD_ARGON2ID)`. argon2i is also rejected
|
|
|
+ because it doesn't meet the SEC_REVIEW threshold even though
|
|
|
+ `password_hash` accepts it. Tests: bypass the validator (the
|
|
|
+ `Bootstrap::container()` test path is unchanged), so existing
|
|
|
+ fixtures continue to use Argon2id without ceremony. Regression
|
|
|
+ tests in `ui/tests/Unit/App/ConfigTest.php` cover Argon2id-accept,
|
|
|
+ bcrypt-cost-12-accept, bcrypt-cost-4-reject, argon2i-reject,
|
|
|
+ md5-crypt-reject, plain-string-reject, empty-string-reject, and
|
|
|
+ the "local admin disabled, hash not checked" branch (operators who
|
|
|
+ run OIDC-only don't have to invent a strong dummy hash).
|
|
|
|
|
|
### F38 — Disabled local-admin returns 404 *before* throttle, allowing unrestricted hammering
|
|
|
- **File:** `ui/src/Auth/LocalLoginController.php:60-62`
|