## NOT APPROVED ### Important findings 1. **CSRF tokens have no age/expiry validation** - **File:** `scripts/health/health_dashboard_server.py` - **Lines:** 46, 120–126, 228–232 - Tokens are stored as an unbounded-age set entry and remain valid until used or arbitrarily evicted after 256 entries. One-time replay protection works, but the requested maximum-age validation is absent. - **Fix:** Store `token -> issued_monotonic_time`, reject and remove tokens older than a short configured TTL (for example 10–15 minutes), and periodically purge expired entries. Add deterministic expired/fresh-token tests. 2. **Allowed-host enforcement does not cover every HTTP method** - **File:** `scripts/health/health_dashboard_server.py` - **Lines:** 177–245 - Host validation exists for `GET`, `HEAD`, and `POST`. Other methods such as `OPTIONS`, `PUT`, `DELETE`, `PATCH`, and `TRACE` fall through to `BaseHTTPRequestHandler` before the configured host gate. They currently receive an unsupported-method response, so no write route was found, but this fails the explicit “gate on every method” contract. - **Fix:** Put host validation in a common request-dispatch boundary, or implement all relevant `do_*` handlers through one host-gated rejection method. Add synthetic tests showing disallowed hosts receive `421` for every supported and unsupported method. 3. **Worker JSON parser does not reject duplicate object keys** - **File:** `scripts/health/health_dashboard_action_worker.py` - **Lines:** 63–76 - The post-parse schema is strict, but Python’s `json.loads()` silently accepts duplicate keys using the last value. Therefore the worker does not validate the *exact* serialized payload. - **Fix:** Decode with an `object_pairs_hook` that rejects duplicate keys at every object level, then apply the existing exact-key/type validation. Add duplicate top-level and duplicate score-key rejection tests. ### Verified controls - Default bind is `127.0.0.1`; allowed hosts are explicitly configurable. - Allowlisted `Host` parsing resists ordinary DNS-rebinding attempts and rejects malformed hosts. - Document/report paths use canonical containment checks; prefix siblings and symlink escapes are rejected. - Nonce CSP contains no `unsafe-inline`; generated HTML uses listeners rather than inline event handlers. - Same-origin check, double-submit token comparison, one-time token consumption, body-size limits, complete score validation, and future-date rejection are present. - Network service writes only atomically renamed `0600` queue files; systemd makes the health tree read-only to it. - Separate worker has `PrivateNetwork=true`, exact post-parse field/type/range validation, and sequential DB-then-report subprocess execution. - Report output uses temporary-file plus `os.replace`; systemd activation serializes the single worker service. - Privacy Mode uses ephemeral DOM classes; no `localStorage`, `sessionStorage`, IndexedDB, or cookie persistence was found. - No production DB or health assets were opened. ### Execution evidence - `python3 -m pytest -q tests/test_health_dashboard_v4.py` → **11 passed** - `systemd-analyze verify` on all three units → **passed, no diagnostics** - Offline systemd security analysis completed; worker network isolation was recognized. The units remain relatively lightly sandboxed overall (`8.8 EXPOSED` server, `7.6 EXPOSED` worker), but no additional release-blocking sandbox escape was established from the reviewed routes. ### Repository impact - **Files created or modified:** none. - Review was read-only and used synthetic data only.