This commit is contained in:
2026-09-12 21:47:40 +02:00
parent 9171560ad4
commit 6408ea6c12
2 changed files with 0 additions and 114 deletions
-114
View File
@@ -1,114 +0,0 @@
# Security review — 2026-09-12
## Scope and threat boundary
This pass reviewed the full Lanspread workspace after the Fable 5.1 remediation.
The product is a desktop launcher for LAN parties, not an Internet-facing
multi-tenant service. Remote LAN peers and their network traffic are untrusted.
The bundled catalog is trusted authority. A user-selected local game root and
explicit local install actions remain user authority. The three Windows launch
scripts intentionally run elevated.
## Reassessment of previously ignored findings
The earlier file dismissed several finite-resource findings too broadly. This
pass fixed anonymous pool monopolization, retry multiplication, oversized remote
state, heavy UI publication, selected-root ingestion, elevated-script trust
binding, and catalog-preflight traversal. The remaining accepted decisions and
their exact limits are recorded in `SECURITY_FIXES_FABLE5.1_IGNORED.md`.
The main retained decisions are:
- anonymous LAN membership, metadata browsing, and catalog-content downloads;
- Ordinary Install consuming the current root `.eti` set, including the S33
local-mutation workflow;
- transactional pre-verification disk writes;
- no OS sandbox around `unrar`;
- local-user regex CPU cost and the fixed companion-window capability; and
- required elevation for catalog-authorized setup, game, and server scripts.
These are proportionate for the application only because remote paths and
content remain catalog-bound, local mutations require an explicit local action,
and remote work now has finite global and per-origin budgets.
## Findings fixed in this pass
| Finding | Final boundary |
| -------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| mDNS advertised-address quota bypass | Candidate quotas use the observed response IP. One source gets 8 active-or-cooling candidates. |
| Unbounded mDNS dependency state | Cache records are capped at 1,024 globally and 128 per source; timers are deduplicated and capped at 4,096. |
| Anonymous request pool monopolization | One IP gets 8 handshake attempts per 3 seconds, 8/64 connections, 8/16 control tasks, 8/48 bulk tasks, and 1/2 Stream Install providers. |
| Renewable bulk-send deadlines | Raw chunks use one absolute ten-minute window; Stream Install uses a catalog-sized absolute window plus ten-minute inactivity through FIN. |
| Ordinary Sybil retry multiplication | At most 8 distinct identities at distinct IPs share one 20-minute retry window after the initial failure. |
| Stream Install drip and retry multiplication | At most 4 endpoint IPs share `10 min + 2 x catalog bytes / 1 MiB/s`; only written nonempty file bytes refresh inactivity. |
| Eager control-response allocation | An 8 MiB endpoint-IP budget and shared 32 MiB connector budget are acquired after manual four-byte length validation and held through decode. |
| Aggregate remote state | At most 16,384 remote library rows and 16,384 remote Call-to-Play events; rejected revisions retain watermarks. |
| Peer identity amplification | At most 8 authenticated identities may reside at one endpoint IP, within the global 64-peer limit. |
| Unbounded full-view publication | Remote-library and Call-to-Play views keep one queued and one replaceable pending snapshot; lifecycle events remain FIFO. |
| Call-to-Play projection cost | One author retains 128 creator roots; the UI projects 128 nominations and 256 messages and sorts messages once. |
| Selected-root ingestion | Index, traversal, depth, byte, elapsed-time, bounded `version.ini`, and 64 MiB aggregate settings-read limits fail without publishing partial state. |
| Mutable elevated Windows scripts | A fixed-role elevated worker reloads embedded catalog authority, verifies the locked script, uses System32 `cmd.exe`, and transfers locks into the child. |
| Catalog preflight root traversal | Package roots must be regular non-link directories before a bounded leaf read; diagnostics omit observed package contents. |
| Developer recipe shell injection | Just positional arguments and exported values are passed as quoted shell data. |
| Unused frontend shell authority | The main-window `shell:allow-open` capability was removed. |
## Independent follow-up review
A fresh source-to-sink review found and closed six additional gaps: response
semaphore head-of-line blocking, half-open handshake fairness, an unbounded
public readiness-sentinel read, aggregate settings-file reads, repeated monitor
backoff reset after a root switch, and an over-strict ten-minute provider egress
deadline. The corrected provider deadline is ten minutes plus one catalog copy
at 1 MiB/s. The handshake limiter drops excess Initials because issuing a Retry
token would bypass s2n's next connection-attempt callback.
The reviewer read back the follow-up range and found no remaining concrete
source-level bypass or regression.
## Dependency scan
`just security-audit` refreshed RustSec and scanned 578 locked Rust packages,
then audited the Deno lockfile. It found no known vulnerabilities in either
graph. RustSec reported seven informational warnings:
- six unmaintained build/transitive crates (`proc-macro-error` and five `unic-*`
crates); and
- `glib 0.18.5` unsoundness limited to `VariantStrIter` methods.
The affected `glib` API has no call site in Lanspread or its locked dependants,
and the GTK 0.18 branch is constrained by the current Tauri Linux backend. The
warnings are dependency maintenance work rather than reachable findings.
## Residual limits
- Native Windows execution was unavailable. Linux-visible worker authority,
parsing, digest, role, and handoff tests pass, but the Windows lock-transfer
test has not run.
- The absent production scripts may invoke mutable secondary executables or
configuration as administrator. The production payload must be audited before
claiming transitive administrator-code provenance.
- Ordinary Install deliberately accepts locally changed or additional `.eti`
archives. Their expanded output and decompression cost are not catalog
authority; links are skipped and audited before promotion.
- Stream Install staging still uses ambient path calls. Exploitation requires a
concurrent same-machine writer able to mutate the selected root.
- Per-origin fairness cannot stop cooperating hosts or one host using several
usable IP addresses; finite global limits remain authoritative.
## Validation
- `just fmt`: passed.
- `just clippy`: passed with warnings denied.
- `just test`: passed, including 541 peer tests and all workspace targets.
- `just frontend-test`: 94 tests passed.
- `just build-fixture`: passed.
- `just peer-cli-build`: passed.
- `just peer-cli-tests`: all four catalog profiles passed; the container
scenarios could not start because this host denies Podman's `newuidmap`
capability, including outside the sandbox.
- `just mdns-vendor-test`: 106 tests passed with socket access.
- `just security-audit`: no known Rust or Deno vulnerabilities; seven accepted
RustSec informational warnings.
Physical-LAN multicast behavior and native Windows execution remain runtime
validation boundaries.