This commit is contained in:
ddidderr committed 2026-09-12 21:47:40 +02:00
1 parent 9171560ad4
commit 6408ea6c12
2 files changed
-114

No files matched your search

@@ -0,0 +1,191 @@
# Retained security decisions and residual risks
Reassessed after the 2026-09-12 security pass.
This file records every finding from
`SECURITY_AUDIT_2026-08-28_GEMINI-3.7-HIGH_TEAMWORK.md` and
`security-report/report.md` that was deliberately **not** fixed, or only
partially fixed, and why. The judgement throughout is anchored in what lanspread
is: a desktop launcher for LAN parties where a room of people who know each
other share a fixed, operator-published catalog of games. Peers are
requester-anonymous by design, every byte a peer serves is verified against the
bundled catalog's BLAKE3 authority, and a hostile participant can be unplugged.
Findings that only make sense against an internet-facing, multi-tenant threat
model are noted as such.
The reassessment found that several availability findings had been dismissed too
aggressively. They are now fixed with finite aggregate, per-origin, retry, and
UI publication budgets. Entries retained below require trusted catalog input, an
explicit local operation, or a same-machine writer; they are not remote LAN-peer
integrity bypasses under this product model.
## Gemini audit
### NET-01 — Mutual TLS for inbound streams (partially fixed)
- Fixed: forged change hints. A hint is now honoured only when it arrives from
the IP address at which the claimed peer was authenticated, which removes the
reflected state-pull amplification the finding describes.
- Not fixed: requiring client certificates. Requester anonymity is intentional
(documented in the threat model: "responder identity, not membership, is
authenticated"). Anyone on the party LAN running the current build is supposed
to be able to browse and download. mTLS would add certificate handling on
every connection for no gain in that model, because a hostile participant can
still mint a valid self-signed identity.
- The "information harvesting" sub-point (display names, game lists, Call to
Play chat visible to any LAN client) is the product.
### NET-06 — Unauthenticated chunk and Stream Install egress (informational)
By design; see NET-01.
### EXP2-SEC-01 — Post-unpack manifest verification (partially fixed)
- Fixed: symlink/reparse-point audit before promotion, plus `unrar -ol-`.
- Retained: hashing every ordinary extracted file against the catalog manifest.
Ordinary Install intentionally consumes every current regular root `.eti`;
acceptance scenario S33 replaces a downloaded archive and requires those new
local bytes to be installed. Stream Install remains exact: it verifies every
path, kind, size, and BLAKE3 digest before promotion.
### EXP2-SEC-02 — RAR bomb / disk exhaustion
Ordinary archives may be locally replaced or added for S33, so a bomb need not
come from the catalog. The retained boundary is an explicitly selected local
root plus a user-started install. Games are large by nature and the ordinary
manifest does not define expected expanded bytes. Disk-full or extractor failure
rolls the transaction back, but decompressed bytes, CPU time, and free space are
not preflighted.
### EXP2-SEC-05 — Pre-verification chunk writes
Uncommitted downloads are never published: `version.ini` is written only after
every chunk verifies, and the ownership journal plus recovery path handle a
crash mid-download. Ordinary chunks can be 128 MiB; buffering complete chunks in
memory to avoid confined temporary writes would materially raise memory use
without changing publication integrity.
### EXP2-SEC-07 — Ambient filesystem calls in `stream_install.rs`
Remote paths, kinds, sizes, and digests are catalog-owned, and the completed
staging tree is audited before transactional promotion. The receiver still uses
ambient path calls inside its owned staging directory. Exploiting that gap
requires a concurrent same-machine writer able to mutate the selected root; it
is retained as defense-in-depth work rather than a remote peer bypass.
### SEC-IPC-01 — Elevated execution of game scripts (partially fixed)
- Fixed: launch-time trust binding. A fixed-role elevated worker reloads the
bundled authority, matches the embedded `ContentId`, verifies exact size and
BLAKE3 from a no-follow locked handle, resolves System32 `cmd.exe`, and keeps
path locks alive in the command process. Changed, unmanifested,
reparse-backed, markerless, and streamed-only scripts fail closed.
- Retained: all three scripts still run elevated. This is required for registry
entries, redistributables, and firewall rules. Native Windows lock-transfer
behavior is not proven in this Linux checkout, and a trusted script may still
invoke mutable secondary executables or configuration.
- The proposed `[A-Za-z0-9_-]` username allowlist was rejected because it would
mangle ordinary names (spaces, umlauts).
### SEC-IPC-04 — Sandboxing `unrar` (partially fixed)
- Fixed: `-ol-` and the post-extraction link audit.
- Not fixed: Landlock/AppContainer style OS sandboxing of the sidecar. Ordinary
S33 input is not necessarily catalog-identical, but it is selected local input
rather than peer-controlled bytes. `unrar` runs with `-ol-`, bounded output
capture and cancellation cleanup, a staging link audit, and bounded remote
Stream Install provider admission. Note that the `-sl-` flag the audit
recommends does not exist (`-sl<size>` is a size filter).
### SEC-IPC-05 — `allow-create-webview-window` capability
The log windows are opened from the frontend with the app's own URL, and the
companion-window ownership logic is unit-tested TypeScript. The audit found no
script-injection route, and the webview now has a CSP. Moving window creation
into a Rust command to drop one permission is churn for a hypothetical.
### SEC-FE-01 — ReDoS in the log-window regex filter
The regex is typed by the local user into their own log viewer. A user can
freeze their own UI thread with `(a+)+$`; nobody else can. Not a security issue
for this app.
## Codex scan (`security-report/report.md` numbering)
### [1] Anonymous LAN requesters can monopolize global pools (fixed 2026-09-12)
Requester anonymity remains, but resource anonymity does not. One observed IP
may start 8 handshakes per 3 seconds and retain at most 8 connections, 8 of 16
control tasks, 8 of 48 bulk transfers, 1 of 2 Stream Install providers, and 8
MiB of the shared 32 MiB response buffer. Raw and Stream Install sends have
absolute deadlines, so incremental progress cannot renew those slots
indefinitely.
### [3] Ordinary install extracts uncatalogued root archives (partially fixed)
- Fixed: link audit before promotion.
- Not fixed: restricting extraction to the catalog's archive set and verifying
extracted output. This is the behavior exercised by S33: the local user
replaces or adds `.eti` files and expects Ordinary Install to consume the
current set. See EXP2-SEC-01.
### [4] Sybil retry amplification (fixed)
One failed chunk may try at most eight distinct peer IDs at distinct endpoint
IPs. All retries share one nonrenewable 20-minute window created after the
initial failure; each in-flight attempt retains the earlier ten-minute limit.
Stream Install separately permits four endpoint IPs under one catalog-sized
total deadline.
### [5] Unbounded active-call rendering (fixed)
One remote author may retain at most 128 creator roots. Frontend projection is
limited to 128 nominations and the latest 256 messages, and messages are sorted
once rather than after every append.
### [8] Aggregate state and UI publication (fixed)
Committed state now has eight identities per endpoint IP, 16,384 aggregate
library rows, and 16,384 aggregate remote Call-to-Play events. Rejected
revisions retain watermarks instead of being pulled repeatedly. Each heavy UI
view keeps one queued snapshot and one replaceable pending snapshot while small
lifecycle events remain lossless and FIFO.
### [10] Elevated mutable-script trust binding (fixed)
See SEC-IPC-01. Required elevation remains; mutable-path trust binding does not.
### [11] Unbounded selected-root monitoring (fixed)
Index reads, games, archive fingerprints, recursive entries, depth, bytes,
elapsed scan time, `version.ini`, launch-settings traversal, and INI reads now
have explicit per-file and aggregate ceilings. Monitor failures retain the
previous complete snapshot and back off exponentially per selected root.
### [13] Catalog preflight can read outside the package root (fixed 2026-09-12)
Full-selection preflight now rejects a linked or non-directory package root
before the bounded, non-link `version.ini` read, and mismatch diagnostics omit
package-controlled observed contents.
## Summary
| Status | Findings |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Fixed | NET-02, NET-03, NET-04, NET-05, EXP2-SEC-03, EXP2-SEC-04, EXP2-SEC-06, SEC-IPC-02, SEC-IPC-03, SEC-DB-01, Codex [1], [2], [4], [5], [6], [7], [8], [9], [10], [11], [12], [13], [14], [15] |
| Partially fixed | NET-01, EXP2-SEC-01, SEC-IPC-01, SEC-IPC-04, Codex [3] |
| Accepted/retained | NET-06, EXP2-SEC-02, EXP2-SEC-05, EXP2-SEC-07, SEC-IPC-05, SEC-FE-01 |
The retained items have these practical boundaries:
| Retained risk | Boundary |
| ------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Anonymous membership and metadata/content access | Intended LAN behavior; responder pinning, exact catalog identity, and active per-origin quotas protect integrity and availability. |
| Ordinary archive and extracted-output authority | Explicit local install and S33 mutation; links are blocked, but the current archive set, expanded bytes, and decompression cost are local user authority. |
| Pre-verification temporary writes | Confined ownership journals and a final sentinel prevent publication; failed chunks can still consume bounded temporary disk space. |
| Ambient Stream Install staging calls | Remote paths, sizes, and digests are exact; a concurrent same-machine root mutation remains the escape precondition. |
| Elevated execution | Only exact catalog-authorized scripts launch, but those trusted scripts intentionally receive administrator authority and may invoke mutable dependencies. |
| External `unrar` | No OS sandbox; local mutated archives remain user-level extractor input. |
| Main-window companion creation | Used for fixed local log windows; CSP and no known renderer injection path limit reachability. |
| User regex CPU | The pattern is local input; remote-controlled log lines can only supply the haystack. |
| Distributed saturation | One IP cannot monopolize active pools; cooperating hosts on several IPs can still reach the finite global limits. |