192 lines
12 KiB
Markdown
192 lines
12 KiB
Markdown
# 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. |
|