feat: add bounded admin WebSocket backend (Phase 8D.5)
- Require current admin cookie sessions, Origin checks and single-use tickets - Reuse the shared console with session-aware authorization and slot allocation - Add HTTPD-owned I/O, bounded buffering and revocation cleanup - Prevent LRU eviction of serial clients and stale admin socket closure - Reject unsupported web-shell mutations before side effects - Add host regressions, a smoke client and resource accounting Validated by user sign-off after a 15-minute full-client soak at 230400 baud, with a few broker drops under heavy output. Browser UI remains for Phase 8D.6; numeric memory reserves remain open.
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
# Admin ticket store host checks
|
||||
|
||||
Run from the repository root:
|
||||
|
||||
```sh
|
||||
python3 tests/web_admin_tickets/run.py
|
||||
python3 tests/web_admin_tickets/run.py --sanitize
|
||||
```
|
||||
|
||||
Requires a C11 `cc`, Python 3, OpenSSL development headers/libcrypto, and (for
|
||||
`--sanitize`) ASan/UBSan runtimes. No firmware build, network, generated assets,
|
||||
or persistent build output. The runner reuses the session-store harness's tiny
|
||||
platform header fakes. `test.c` includes the **unmodified production C**, using
|
||||
real project principal/session declarations and OpenSSL SHA-256. Inclusion gives
|
||||
white-box access for wipe, saturation and exhaustion assertions without adding
|
||||
production test hooks. RNG, time and session validation are deterministic fakes;
|
||||
all external calls assert that the ticket critical section is not held.
|
||||
|
||||
## Exact test groups
|
||||
|
||||
1. Stopped/start/idempotent-start lifecycle; 64 hex output; SHA-256 digest-only
|
||||
storage; success, replay denial and full record wipe.
|
||||
2. Two-ticket capacity and no live eviction; exact counters; nested competing
|
||||
issuance takes the last slot and the losing output is wiped.
|
||||
3. Issue rejects user role, public-key method, mismatched generation, zero ID,
|
||||
NULL principal/output, stale sessions and session-check errors.
|
||||
4. Consume burns matches before denying wrong session, user role, public-key
|
||||
method, generation, stale/check-error, zero ID or NULL principal; also rejects
|
||||
a different session with the *same* account principal.
|
||||
5. Empty/NULL/short/long/nonhex input; uppercase hex consumes the same secret.
|
||||
6. Success one microsecond before expiry; rejection at expiry; stale reclaim on
|
||||
issue/snapshot; snapshot expiry cleanup; signed deadline overflow rejection.
|
||||
7. Revocation ID precedence, exact username length/name and global scope;
|
||||
revocation never invalidates the fake sessions.
|
||||
8. RNG/SHA failures, failed output wipe, consume SHA failure leaves the
|
||||
unidentifiable ticket intact, duplicate live RNG/digest rejection.
|
||||
9. Issuance RNG/SHA hooks exercise stop/restart, global and nonmatching revoke,
|
||||
and session invalidation; currentness hook exercises stop/restart.
|
||||
10. Consume SHA/postcheck hooks exercise stop/restart, global/nonmatching revoke,
|
||||
stale sessions and expiry; nested competing consumes admit exactly once.
|
||||
11. Prune check races replacement with the same ID, digest and deadline; the
|
||||
non-reused record generation protects the replacement from stale cleanup.
|
||||
12. Nonwrapping epoch and record generation exhaustion, permanent lifecycle
|
||||
failure at exhaustion, saturated counters, NULL/count-only snapshots and
|
||||
host structure sizes.
|
||||
|
||||
## Contract and limits
|
||||
|
||||
The public API is in `src/web_admin_tickets.h`. This module is inert until wired
|
||||
by a later integration increment. It adds no routes, session invalidation,
|
||||
transport, task, socket, queue, timer or heap allocation. Callers must authorize
|
||||
HTTP cookie/Origin/CSRF, invalidate the authoritative session store **before**
|
||||
calling revoke, wipe successful token outputs and recheck currentness at later
|
||||
sensitive boundaries. A successful consume is not an authorization lease.
|
||||
|
||||
Two tickets, 32 RNG bytes each, 64 hex characters plus NUL, absolute 30-second
|
||||
lifetime. Only SHA-256 of decoded secret bytes is retained with copied principal,
|
||||
session ID, deadline and unique generation. Both hex cases are accepted. Live
|
||||
digest collisions fail rather than creating ambiguous tickets. No retry loop
|
||||
or live eviction. Pruning checks at most two copied records per invocation.
|
||||
Every revoke advances the epoch even if no record matches, conservatively
|
||||
cancelling unrelated in-flight issue/consume work. Start is idempotent while
|
||||
ready. Stop/start never resets counters, epoch or record generation.
|
||||
|
||||
`issued` counts published tickets, `consumed` counts burned matches (including
|
||||
subsequently denied admissions), `rejected` counts failed issue/consume calls;
|
||||
`capacity_rejections` is a subset of rejected. All counters saturate at UINT32_MAX.
|
||||
Snapshot prunes expired/stale records and exports counts, readiness and storage
|
||||
size only. A capacity failure is ESP_ERR_NO_MEM; malformed input INVALID_ARG;
|
||||
unauthorized/stale/lifecycle-raced work INVALID_STATE; no live consume match
|
||||
NOT_FOUND; SHA failure ESP_FAIL; RNG errors propagate. Failed issue wipes all 65
|
||||
output bytes when output is non-NULL. Output must not alias inputs.
|
||||
|
||||
Host measured sizes: ticket 104 B, two-ticket state 248 B, fake lock 4 B, snapshot
|
||||
40 B; snapshot `storage_bytes` = 252 B. Estimated 32-bit target sizes: ticket
|
||||
96 B, state 232 B, plus the target portMUX (typically 8 B), roughly **240 B static
|
||||
RAM**. These are estimates, not target linker measurements. Issue plus nested
|
||||
prune has 240 B of explicit ticket/random local payload on this host (about
|
||||
224 B on a 32-bit target), excluding scalar/compiler frames and session/RNG/SHA
|
||||
call stacks; caller also owns a 65 B token. No measured target stack/flash delta.
|
||||
|
||||
Hooks test deterministic interleavings, not true multicore scheduling or IDF
|
||||
portMUX semantics. They do not validate the real DRBG, mbedTLS, session database,
|
||||
HTTP admission, hardware, or full Phase 8D.5 integration. Post-check account
|
||||
changes without notification are subject to the same no-lease boundary as the
|
||||
session API. Combined hardware validation remains pending; no firmware build
|
||||
or device operation is part of this increment.
|
||||
Reference in New Issue
Block a user