sec
This commit is contained in:
@@ -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.
|
||||
Reference in New Issue
Block a user