diff --git a/design/security-review-2026-06-28.md b/design/security-review-2026-06-28.md index 3a38ea7..03fe550 100644 --- a/design/security-review-2026-06-28.md +++ b/design/security-review-2026-06-28.md @@ -30,21 +30,23 @@ remedy, are deferred/accepted with a reason. | #6 | Med | **FIXED** (`3532e35`) — EIS relay moved to `$XDG_RUNTIME_DIR` (0700) + symlink reject | | #7 | Med→Low | **FIXED** (`3532e35`) — `vdisplay::ENV_LOCK` serializes setup-path env mutation (data-race UB closed); full per-session `SessionContext` threading for value-confusion is a follow-up | | #8 | Low | **FIXED** (`6f903f7`, *Win CI/box pending*) — web-password file created empty → locked → written | -| #9 | Low | **ACCEPTED** — disarm-on-any-attempt IS the documented single-online-guess (prior-fix #2); the delegated-approval flow is structurally immune. Steer hostile LANs to it | +| #9 | Low | **FIXED** (`f0574a5`) — a red-team showed the original "delegated approval is the immune fallback" premise was circular with #13. Added a **fingerprint-bound PIN window** (`arm_for`/`pin_for_attempt`): an attempt from any other fingerprint is rejected WITHOUT consuming the window, so an unpaired peer can't pair *or* burn a window armed for a specific device. (Disarm-on-attempt still gives the single-online-guess for the unbound flow; with #13 now flood-resistant, the knock fallback genuinely holds. Web "pair-this-pending-device-with-a-PIN" UX is a follow-up.) | | #10 | Low | **FIXED** (`3532e35`) — ENet decrypt-failed warn throttled (exponential) | | #11 | Low | **FIXED** (`6f903f7`, *Win CI/box pending*) — logs dir DACL-locked (subsumed by #3) | | #12 | Low/Info | **FIXED** (`3532e35`) — parked pairing-waiter cap (+regression test) | -| #13 | Info | **ACCEPTED** — `PENDING_CAP` + LRU + `requested_at` refresh make an actively-retrying device non-evictable | +| #13 | Info→Low | **FIXED** (`f0574a5`) — the "actively-retrying device is non-evictable" claim was really a timing race (and the designed flow parks, doesn't re-knock). Added a **per-source-IP cap** (`MAX_PENDING_PER_IP`, QUIC-validated source) so one host can't fill/evict the queue, and eviction now **never drops a live parked knock** — making the delegated-approval path genuinely flood-resistant | | S2 | Low–Med | **FIXED** (`3532e35`) — a malformed Opus frame drops the frame, keeps the shared mic open | | S3 | Low | **FIXED** (`3532e35`) — held buttons/keys are capped `HashSet`s | | S4 | Low | **FIXED** (`3532e35`) — Epic launcher-cache reads size-capped | | S5 | Low→Info | **FIXED** (`3532e35`) — `fps==0`/absurd rejected at the `open_video` chokepoint | | S6 | Low→Info | **FIXED** (`3532e35`) — shared mic mpsc bounded (drop-newest) | -| S7 | Low→Info | **ACKNOWLEDGED** — `rsa 0.9` Marvin has no fixed upstream release; GameStream is off by default and this is a signing (not decryption-oracle) path. Migrate the GameStream identity to Ed25519/ECDSA when feasible | +| S7 | Low→Info | **ACCEPTED, rationale corrected + hardened** (`f6c9576`) — the prior "signing, not decryption, so the path isn't exercised" reason was *wrong* (signing runs the same secret-exponent modexp Marvin is about). Accept stands for the right reasons: no decryption/padding oracle; the signed `serversecret` is host-random (not attacker-chosen); signing is operator-PIN-gated; GameStream is off by default and the native plane uses rustls not `rsa`; Moonlight mandates RSA-2048 (no Ed25519 migration possible). Also closed the timing-sample amplifier: sign once per ceremony. No upstream `rsa` fix exists | -**Net:** 15 of 18 fixed — 5 Linux-verified clusters, 4 Windows DACL paths (#2/#3/#8/#11, awaiting CI), -and #5 (now on-box validated on the RTX box, 2026-06-29); #9/#13 accepted-with-rationale; S7 -acknowledged (no upstream fix). No finding remains open and actionable. +**Net:** 17 of 18 fixed — 5 Linux-verified clusters, 4 Windows DACL paths (#2/#3/#8/#11, awaiting CI +compile-confirm), #5 (on-box validated on the RTX box, 2026-06-29), and #9/#13 (closed after a +red-team showed their accepts were circular). S7's accept stands but its rationale was corrected and +the timing amplifier hardened; only the transitive `rsa 0.9` advisory itself is un-fixable (no +upstream release). No finding remains open and actionable. ## Consolidated overview & top priorities