|
@@ -1277,6 +1277,38 @@ is gone — see `src/Auth/BootstrapAdmin.php`.
|
|
|
`Request::ip()` / `isHttps()`. Tests: 202 / 533 (was 184 / 502).
|
|
`Request::ip()` / `isHttps()`. Tests: 202 / 533 (was 184 / 502).
|
|
|
Eighth fix from `doc/REVIEW_01.md`.
|
|
Eighth fix from `doc/REVIEW_01.md`.
|
|
|
|
|
|
|
|
|
|
+- [x] **R01-N08 — Idle session timeout + CSRF rotation on login**
|
|
|
|
|
+ (`bc745cd`). A signed-in session previously stayed valid until the
|
|
|
|
|
+ browser closed or the 8h `gc_maxlifetime` GC tick fired — a stolen
|
|
|
|
|
+ session cookie paired with the same-session CSRF token was good
|
|
|
|
|
+ for hours of attacker-driven mutations. `SessionGuard` now drives
|
|
|
|
|
+ a 30-minute idle window (`IDLE_TIMEOUT_SECONDS = 1800`) inside
|
|
|
|
|
+ `start()`: any request that lands more than 1800 s after the
|
|
|
|
|
+ previous one drops `user_id` / `login_at` / `last_active` /
|
|
|
|
|
+ `csrf_token` and `session_regenerate_id(true)` rotates the id —
|
|
|
|
|
+ the next gate sees an anonymous session and redirects to
|
|
|
|
|
+ `/auth/login`. Foreign session keys (the OIDC library's
|
|
|
|
|
+ state/nonce/PKCE) are preserved so an in-flight bounce to Entra
|
|
|
|
|
+ is not killed by a stale idle clock from a previous logged-in
|
|
|
|
|
+ session. `login()` now stamps `last_active = time()` and
|
|
|
|
|
+ `unset()`s `csrf_token`, so a token a pre-login attacker may
|
|
|
|
|
+ have captured from the public homepage form cannot be replayed
|
|
|
|
|
+ against the now-authenticated session (the next `csrfToken()`
|
|
|
|
|
+ call mints a fresh `bin2hex(random_bytes(32))`). The boundary
|
|
|
|
|
+ is `>=` so a session exactly 1800 s idle is expired. Two pure-
|
|
|
|
|
+ static helpers carry the policy so it is testable without
|
|
|
|
|
+ spinning up PHP's session machinery:
|
|
|
|
|
+ `isIdleExpired(int $lastActive, int $now): bool` and
|
|
|
|
|
+ `expireIdleSession(array &$session, int $now): bool`. New
|
|
|
|
|
+ `tests/Auth/SessionGuardTest.php` (9 cases) pins the constant,
|
|
|
|
|
+ the boundary semantics (0 / 1 s / 1799 s / 1800 s / 2 h), the
|
|
|
|
|
+ anonymous-session no-op, the `last_active`-missing seed-on-
|
|
|
|
|
+ first-hit branch, the auth-key drop on idle with foreign-key
|
|
|
|
|
+ survival, the exact-boundary expiry, the just-fresh case, and
|
|
|
|
|
+ the non-int `user_id` defence that mirrors `currentUserId()`'s
|
|
|
|
|
+ contract. Tests: 211 / 562 (was 202 / 533). Ninth fix from
|
|
|
|
|
+ `doc/REVIEW_01.md`.
|
|
|
|
|
+
|
|
|
- [x] **New sprint form: drop weeks input + task list row hover**
|
|
- [x] **New sprint form: drop weeks input + task list row hover**
|
|
|
(`3728106`). The `/sprints/new` form no longer collects an
|
|
(`3728106`). The `/sprints/new` form no longer collects an
|
|
|
`n_weeks` value — the week count is derived from `start_date` /
|
|
`n_weeks` value — the week count is derived from `start_date` /
|
|
@@ -1346,7 +1378,7 @@ for f in $(git ls-files '*.php'); do php -l "$f" | tail -1 | sed "s|^|$f: |"; do
|
|
|
Run the test suite:
|
|
Run the test suite:
|
|
|
```bash
|
|
```bash
|
|
|
vendor/bin/phpunit
|
|
vendor/bin/phpunit
|
|
|
-# → OK (202 tests, 533 assertions)
|
|
|
|
|
|
|
+# → OK (211 tests, 562 assertions)
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
The Phase 20 parser tests need `ext-dom`, `ext-zip`, `ext-xmlreader`,
|
|
The Phase 20 parser tests need `ext-dom`, `ext-zip`, `ext-xmlreader`,
|
|
@@ -1393,6 +1425,7 @@ before acting — nothing here is load-bearing once it grows stale.
|
|
|
## 13. Git history (as of this writing)
|
|
## 13. Git history (as of this writing)
|
|
|
|
|
|
|
|
```
|
|
```
|
|
|
|
|
+bc745cd Fix R01-N08: idle session timeout + CSRF rotation on login
|
|
|
a2e77ea Fix R01-N05 + R01-N07: trusted-proxy aware HTTPS + client IP
|
|
a2e77ea Fix R01-N05 + R01-N07: trusted-proxy aware HTTPS + client IP
|
|
|
f565c86 Fix R01-N03: explicit env-bootstrap for the first OIDC admin
|
|
f565c86 Fix R01-N03: explicit env-bootstrap for the first OIDC admin
|
|
|
2b8f167 Docs: mark R01-N06 fixed, refresh SPEC §3 / §4 / §7 / §9 / §11 / §13
|
|
2b8f167 Docs: mark R01-N06 fixed, refresh SPEC §3 / §4 / §7 / §9 / §11 / §13
|